pidgin/pidgin

Remove the old Bonjour protocol plugin

2 months ago, Gary Kramlich
a3b862b8dcde
Parents 6d41b29637ef
Children 68cc8544b438
Remove the old Bonjour protocol plugin

We have a started a new version and with migrations that are currently in place
this didn't really work anymore anyways. So rather than letting it sit around
and make things harder to migrate we're removing it.

Testing Done:
Hung out with the turtles.

Reviewed at https://reviews.imfreedom.org/r/3072/
  • +0 -796
    libpurple/protocols/bonjour/bonjour.c
  • +0 -68
    libpurple/protocols/bonjour/bonjour.h
  • +0 -1228
    libpurple/protocols/bonjour/bonjour_ft.c
  • +0 -62
    libpurple/protocols/bonjour/bonjour_ft.h
  • +0 -307
    libpurple/protocols/bonjour/buddy.c
  • +0 -114
    libpurple/protocols/bonjour/buddy.h
  • +0 -193
    libpurple/protocols/bonjour/dns_sd_proxy.c
  • +0 -194
    libpurple/protocols/bonjour/dns_sd_proxy.h
  • +0 -697
    libpurple/protocols/bonjour/mdns_avahi.c
  • +0 -282
    libpurple/protocols/bonjour/mdns_common.c
  • +0 -92
    libpurple/protocols/bonjour/mdns_common.h
  • +0 -756
    libpurple/protocols/bonjour/mdns_dns_sd.c
  • +0 -25
    libpurple/protocols/bonjour/mdns_dns_sd.h
  • +0 -46
    libpurple/protocols/bonjour/mdns_types.h
  • +0 -40
    libpurple/protocols/bonjour/mdns_win32.c
  • +0 -46
    libpurple/protocols/bonjour/meson.build
  • +0 -237
    libpurple/protocols/bonjour/parser.c
  • +0 -34
    libpurple/protocols/bonjour/parser.h
  • +0 -11
    libpurple/protocols/bonjour/resources/bonjour.gresource.xml
  • +0 -0
    libpurple/protocols/bonjour/resources/icons/16x16/apps/im-bonjour.png
  • +0 -189
    libpurple/protocols/bonjour/resources/icons/16x16/apps/scalable/im-bonjour.svg
  • +0 -0
    libpurple/protocols/bonjour/resources/icons/22x22/apps/im-bonjour.png
  • +0 -198
    libpurple/protocols/bonjour/resources/icons/22x22/apps/scalable/im-bonjour.svg
  • +0 -0
    libpurple/protocols/bonjour/resources/icons/48x48/apps/im-bonjour.png
  • +0 -207
    libpurple/protocols/bonjour/resources/icons/scalable/apps/im-bonjour.svg
  • +0 -1423
    libpurple/protocols/bonjour/xmpp.c
  • +0 -111
    libpurple/protocols/bonjour/xmpp.h
  • +0 -1
    libpurple/protocols/meson.build
  • +1 -20
    meson.build
  • +0 -10
    po/POTFILES.in
  • +0 -4
    protocols/bonjour/meson.build
  • --- a/libpurple/protocols/bonjour/bonjour.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,796 +0,0 @@
    -/*
    - * purple - Bonjour Protocol Plugin
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <glib.h>
    -#ifndef _WIN32
    -#include <pwd.h>
    -#else
    -#define UNICODE
    -#include <winsock2.h>
    -#include <windows.h>
    -#include <lm.h>
    -#endif
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include <purple.h>
    -
    -#include "bonjour.h"
    -#include "mdns_common.h"
    -#include "buddy.h"
    -#include "bonjour_ft.h"
    -#include "xmpp.h"
    -
    -struct _BonjourProtocol {
    - PurpleProtocol parent;
    -};
    -
    -static PurpleProtocol *my_protocol = NULL;
    -
    -static char *default_firstname;
    -static char *default_lastname;
    -
    -const char *
    -bonjour_get_jid(PurpleAccount *account)
    -{
    - PurpleConnection *conn = purple_account_get_connection(account);
    - BonjourData *bd = purple_connection_get_protocol_data(conn);
    - return bd->jid;
    -}
    -
    -const gchar *
    -bonjour_get_group_name(void) {
    - return _("Bonjour");
    -}
    -
    -static void
    -bonjour_removeallfromlocal(PurpleConnection *conn, PurpleGroup *bonjour_group)
    -{
    - PurpleAccount *account = purple_connection_get_account(conn);
    - PurpleBlistNode *cnode, *cnodenext, *bnode, *bnodenext;
    - PurpleBuddy *buddy;
    -
    - if (bonjour_group == NULL)
    - return;
    -
    - /* Go through and remove all buddies that belong to this account */
    - for (cnode = purple_blist_node_get_first_child((PurpleBlistNode *) bonjour_group); cnode; cnode = cnodenext) {
    - cnodenext = purple_blist_node_get_sibling_next(cnode);
    - if (!PURPLE_IS_META_CONTACT(cnode))
    - continue;
    - for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = bnodenext) {
    - bnodenext = purple_blist_node_get_sibling_next(bnode);
    - if (!PURPLE_IS_BUDDY(bnode))
    - continue;
    - buddy = (PurpleBuddy *) bnode;
    - if (purple_buddy_get_account(buddy) != account)
    - continue;
    - purple_account_remove_buddy(account, buddy, NULL);
    - purple_blist_remove_buddy(buddy);
    - }
    - }
    -
    -}
    -
    -static void
    -bonjour_login(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleAccount *account) {
    - PurpleConnection *gc = purple_account_get_connection(account);
    - BonjourData *bd;
    - PurpleStatus *status;
    - PurplePresence *presence;
    -
    - if (!mdns_available()) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    - _("Unable to find Apple's \"Bonjour for Windows\" toolkit, see "
    - "https://developer.pidgin.im/BonjourWindows for more information."));
    - return;
    - }
    -
    - purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
    - PURPLE_CONNECTION_FLAG_NO_IMAGES);
    - bd = g_new0(BonjourData, 1);
    - purple_connection_set_protocol_data(gc, bd);
    -
    - /* Start waiting for xmpp connections (iChat style) */
    - bd->xmpp_data = g_new0(BonjourXMPP, 1);
    - bd->xmpp_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
    - bd->xmpp_data->account = account;
    -
    - if (bonjour_xmpp_start(bd->xmpp_data) == -1) {
    - /* Send a message about the connection error */
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to listen for incoming IM connections"));
    - return;
    - }
    -
    - /* Connect to the mDNS daemon looking for buddies in the LAN */
    - bd->dns_sd_data = bonjour_dns_sd_new();
    - bd->dns_sd_data->first = g_strdup(purple_account_get_string(account, "first", default_firstname));
    - bd->dns_sd_data->last = g_strdup(purple_account_get_string(account, "last", default_lastname));
    - bd->dns_sd_data->port_p2pj = bd->xmpp_data->port;
    - /* Not engaged in AV conference */
    - bd->dns_sd_data->vc = g_strdup("!");
    -
    - status = purple_account_get_active_status(account);
    - presence = purple_account_get_presence(account);
    - if (purple_presence_is_available(presence))
    - bd->dns_sd_data->status = g_strdup("avail");
    - else if (purple_presence_is_idle(presence))
    - bd->dns_sd_data->status = g_strdup("away");
    - else
    - bd->dns_sd_data->status = g_strdup("dnd");
    - bd->dns_sd_data->msg = g_strdup(purple_status_get_attr_string(status, "message"));
    -
    - bd->dns_sd_data->account = account;
    - if (!bonjour_dns_sd_start(bd->dns_sd_data))
    - {
    - purple_connection_error (gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to establish connection with the local mDNS server. Is it running?"));
    - return;
    - }
    -
    - bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
    -
    - /* Show the buddy list by telling Purple we have already connected */
    - purple_connection_set_state(gc, PURPLE_CONNECTION_STATE_CONNECTED);
    -}
    -
    -static void
    -bonjour_close(G_GNUC_UNUSED PurpleProtocol *protocol,
    - PurpleConnection *connection)
    -{
    - PurpleGroup *bonjour_group;
    - BonjourData *bd = purple_connection_get_protocol_data(connection);
    -
    - bonjour_group = purple_blist_find_group(BONJOUR_GROUP_NAME);
    -
    - /* Remove all the bonjour buddies */
    - bonjour_removeallfromlocal(connection, bonjour_group);
    -
    - /* Stop looking for buddies in the LAN */
    - if (bd != NULL && bd->dns_sd_data != NULL)
    - {
    - bonjour_dns_sd_stop(bd->dns_sd_data);
    - bonjour_dns_sd_free(bd->dns_sd_data);
    - }
    -
    - if (bd != NULL && bd->xmpp_data != NULL)
    - {
    - /* Stop waiting for conversations */
    - bonjour_xmpp_stop(bd->xmpp_data);
    - g_free(bd->xmpp_data);
    - }
    -
    - /* Delete the bonjour group
    - * (purple_blist_remove_group will bail out if the group isn't empty)
    - */
    - if (bonjour_group != NULL)
    - purple_blist_remove_group(bonjour_group);
    -
    - /* Cancel any file transfers */
    - while (bd != NULL && bd->xfer_lists) {
    - purple_xfer_cancel_local(bd->xfer_lists->data);
    - }
    -
    - if (bd != NULL)
    - g_free(bd->jid);
    - g_free(bd);
    - purple_connection_set_protocol_data(connection, NULL);
    -}
    -
    -static GList *
    -bonjour_protocol_get_account_options(G_GNUC_UNUSED PurpleProtocol *protocol) {
    - PurpleAccountOption *option;
    - GList *opts = NULL;
    -
    - /* Creating the options for the protocol */
    - option = purple_account_option_int_new(_("Local Port"), "port",
    - BONJOUR_DEFAULT_PORT);
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_string_new(_("First name"), "first",
    - default_firstname);
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_string_new(_("Last name"), "last",
    - default_lastname);
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_string_new(_("Email"), "email", "");
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_string_new(_("AIM Account"), "AIM", "");
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_string_new(_("XMPP Account"), "jid", "");
    - opts = g_list_append(opts, option);
    -
    - return opts;
    -}
    -
    -static PurpleBuddyIconSpec *
    -bonjour_protocol_get_buddy_icon_spec(G_GNUC_UNUSED PurpleProtocol *protocol) {
    - return purple_buddy_icon_spec_new("png,gif,jpeg",
    - 0, 0, 96, 96, 65535,
    - PURPLE_ICON_SCALE_DISPLAY);
    -}
    -
    -static void
    -bonjour_conversation_send_message_async(PurpleProtocolConversation *protocol,
    - PurpleConversation *conversation,
    - PurpleMessage *message,
    - GCancellable *cancellable,
    - GAsyncReadyCallback callback,
    - gpointer data)
    -{
    - GTask *task = NULL;
    - const char *target = NULL;
    -
    - task = g_task_new(protocol, cancellable, callback, data);
    -
    - target = purple_conversation_get_name(conversation);
    - if(purple_strempty(target)) {
    - target = purple_message_get_recipient(message);
    - }
    -
    - if(purple_message_is_empty(message) || purple_strempty(target)) {
    - g_task_return_boolean(task, TRUE);
    - } else {
    - BonjourData *bd = NULL;
    - PurpleAccount *account = NULL;
    - PurpleConnection *connection = NULL;
    - int result = 0;
    -
    - account = purple_conversation_get_account(conversation);
    - connection = purple_account_get_connection(account);
    - bd = purple_connection_get_protocol_data(connection);
    -
    - result = bonjour_xmpp_send_message(bd->xmpp_data, target,
    - purple_message_get_contents(message));
    -
    - purple_conversation_write_message(conversation, message);
    -
    - if(result >= 0) {
    - g_task_return_boolean(task, TRUE);
    - } else {
    - g_task_return_new_error(task, BONJOUR_DOMAIN, 0, "unknown error");
    - }
    - }
    -
    - g_clear_object(&task);
    -}
    -
    -static gboolean
    -bonjour_conversation_send_message_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
    - GAsyncResult *result,
    - GError **error)
    -{
    - return g_task_propagate_boolean(G_TASK(result), error);
    -}
    -
    -static void
    -bonjour_set_status(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleAccount *account, PurpleStatus *status)
    -{
    - PurpleConnection *gc;
    - BonjourData *bd;
    - PurplePresence *presence;
    - const char *message, *bonjour_status;
    - gchar *stripped;
    -
    - gc = purple_account_get_connection(account);
    - bd = purple_connection_get_protocol_data(gc);
    - presence = purple_account_get_presence(account);
    -
    - message = purple_status_get_attr_string(status, "message");
    - if (message == NULL)
    - message = "";
    - stripped = purple_markup_strip_html(message);
    -
    - /*
    - * The three possible status for Bonjour are
    - * -available ("avail")
    - * -idle ("away")
    - * -away ("dnd")
    - * Each of them can have an optional message.
    - */
    - if (purple_presence_is_available(presence))
    - bonjour_status = "avail";
    - else if (purple_presence_is_idle(presence))
    - bonjour_status = "away";
    - else
    - bonjour_status = "dnd";
    -
    - bonjour_dns_sd_send_status(bd->dns_sd_data, bonjour_status, stripped);
    - g_free(stripped);
    -}
    -
    -/*
    - * The add_buddy callback removes the buddy from the local list.
    - * Bonjour manages buddies for you, and adding someone locally by
    - * hand is stupid. Perhaps we should change libpurple not to allow adding
    - * if there is no add_buddy callback.
    - */
    -static void
    -bonjour_fake_add_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - G_GNUC_UNUSED PurpleConnection *connection,
    - PurpleBuddy *buddy,
    - G_GNUC_UNUSED PurpleGroup *group,
    - G_GNUC_UNUSED const gchar *message)
    -{
    - purple_debug_error("bonjour", "Buddy '%s' manually added; removing. "
    - "Bonjour buddies must be discovered and not manually added.\n",
    - purple_buddy_get_name(buddy));
    -
    - /* I suppose we could alert the user here, but it seems unnecessary. */
    -
    - /* If this causes problems, it can be moved to an idle callback */
    - purple_blist_remove_buddy(buddy);
    -}
    -
    -
    -static void
    -bonjour_remove_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - G_GNUC_UNUSED PurpleConnection *connection,
    - PurpleBuddy *buddy,
    - G_GNUC_UNUSED PurpleGroup *group)
    -{
    - BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
    - if (bb) {
    - bonjour_buddy_delete(bb);
    - purple_buddy_set_protocol_data(buddy, NULL);
    - }
    -}
    -
    -static GList *
    -bonjour_status_types(G_GNUC_UNUSED PurpleProtocol *protocol,
    - PurpleAccount *account)
    -{
    - GList *status_types = NULL;
    - PurpleStatusType *type;
    -
    - g_return_val_if_fail(account != NULL, NULL);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - BONJOUR_STATUS_ID_AVAILABLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_append(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
    - BONJOUR_STATUS_ID_AWAY,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_append(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
    - BONJOUR_STATUS_ID_OFFLINE,
    - NULL, TRUE, TRUE, FALSE);
    - status_types = g_list_append(status_types, type);
    -
    - return status_types;
    -}
    -
    -static void
    -bonjour_convo_closed(G_GNUC_UNUSED PurpleProtocolClient *client,
    - PurpleConnection *connection, const char *who)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    - BonjourBuddy *bb;
    -
    - account = purple_connection_get_account(connection);
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager, account, who);
    -
    - if(!PURPLE_IS_CONTACT(contact) ||
    - (bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy")) == NULL)
    - {
    - /*
    - * This buddy is not in our buddy list, and therefore does not really
    - * exist, so we won't have any data about them.
    - */
    - g_clear_object(&contact);
    -
    - return;
    - }
    -
    - bonjour_xmpp_close_conversation(bb->conversation);
    - bb->conversation = NULL;
    - g_clear_object(&contact);
    -}
    -
    -static void
    -bonjour_set_buddy_icon(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *conn,
    - G_GNUC_UNUSED PurpleImage *img)
    -{
    - BonjourData *bd = purple_connection_get_protocol_data(conn);
    - bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
    -}
    -
    -static void
    -bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group)
    -{
    - if (buddy == NULL)
    - return;
    -
    - /* If we're moving them out of the bonjour group, make them persistent */
    - if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
    - purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy), TRUE);
    - else
    - purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy),
    - !purple_blist_node_is_transient(PURPLE_BLIST_NODE(buddy)));
    -
    -}
    -
    -static void
    -bonjour_group_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - PurpleConnection *connection, const char *who,
    - G_GNUC_UNUSED const char *old_group, const char *new_group)
    -{
    - PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
    -
    - bonjour_do_group_change(buddy, new_group);
    -}
    -
    -static void
    -bonjour_rename_group(G_GNUC_UNUSED PurpleProtocolServer *protocol_server,
    - G_GNUC_UNUSED PurpleConnection *connection,
    - G_GNUC_UNUSED const char *old_name, PurpleGroup *group,
    - GList *moved_buddies)
    -{
    - const gchar *new_group;
    -
    - new_group = purple_group_get_name(group);
    -
    - g_list_foreach(moved_buddies, (GFunc)bonjour_do_group_change, (gpointer)new_group);
    -}
    -
    -static gboolean
    -bonjour_can_receive_file(G_GNUC_UNUSED PurpleProtocolXfer *prplxfer,
    - PurpleConnection *connection, const char *who)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    - gboolean ret = FALSE;
    -
    - account = purple_connection_get_account(connection);
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager, account, who);
    -
    - if(PURPLE_IS_CONTACT(contact)) {
    - BonjourBuddy *bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    -
    - ret = (bb != NULL);
    - }
    -
    - g_clear_object(&contact);
    -
    - return ret;
    -}
    -
    -static gssize
    -bonjour_get_max_message_size(G_GNUC_UNUSED PurpleProtocolClient *client,
    - G_GNUC_UNUSED PurpleConversation *conv)
    -{
    - return -1; /* 5MB successfully tested. */
    -}
    -
    -#ifdef WIN32
    -static void
    -_set_default_name_cb(G_GNUC_UNUSED GObject *source, GAsyncResult *result,
    - G_GNUC_UNUSED gpointer data)
    -{
    - gchar *fullname = NULL;
    - const char *splitpoint;
    - GError *error = NULL;
    -
    - fullname = g_task_propagate_pointer(G_TASK(result), &error);
    - if (fullname == NULL) {
    - purple_debug_info("bonjour",
    - "Unable to look up First and Last name or Username "
    - "from system (%s); using defaults.",
    - error ? error->message : "unknown reason");
    - g_clear_error(&error);
    - return;
    - }
    -
    - g_free(default_firstname);
    - g_free(default_lastname);
    -
    - /* Split the real name into a first and last name */
    - splitpoint = strchr(fullname, ' ');
    - if (splitpoint != NULL) {
    - default_firstname = g_strndup(fullname, splitpoint - fullname);
    - default_lastname = g_strdup(&splitpoint[1]);
    - } else {
    - default_firstname = g_strdup(fullname);
    - default_lastname = g_strdup("");
    - }
    - g_free(fullname);
    -}
    -
    -static void
    -_win32_name_lookup_thread(GTask *task, G_GNUC_UNUSED gpointer source,
    - G_GNUC_UNUSED gpointer data,
    - G_GNUC_UNUSED GCancellable *cancellable)
    -{
    - gchar *fullname = NULL;
    - wchar_t username[UNLEN + 1];
    - DWORD dwLenUsername = UNLEN + 1;
    -
    - GetUserNameW((LPWSTR) &username, &dwLenUsername);
    -
    - if (*username != '\0') {
    - LPBYTE servername = NULL;
    - LPBYTE info = NULL;
    -
    - NetGetDCName(NULL, NULL, &servername);
    -
    - /* purple_debug_info("bonjour", "Looking up the full name from the %s.\n", (servername ? "domain controller" : "local machine")); */
    -
    - if (NetUserGetInfo((LPCWSTR) servername, username, 10, &info) == NERR_Success
    - && info != NULL && ((LPUSER_INFO_10) info)->usri10_full_name != NULL
    - && *(((LPUSER_INFO_10) info)->usri10_full_name) != '\0') {
    - fullname = g_utf16_to_utf8(
    - ((LPUSER_INFO_10) info)->usri10_full_name,
    - -1, NULL, NULL, NULL);
    - }
    - /* Fall back to the local machine if we didn't get the full name from the domain controller */
    - else if (servername != NULL) {
    - /* purple_debug_info("bonjour", "Looking up the full name from the local machine"); */
    -
    - if (info != NULL) NetApiBufferFree(info);
    - info = NULL;
    -
    - if (NetUserGetInfo(NULL, username, 10, &info) == NERR_Success
    - && info != NULL && ((LPUSER_INFO_10) info)->usri10_full_name != NULL
    - && *(((LPUSER_INFO_10) info)->usri10_full_name) != '\0') {
    - fullname = g_utf16_to_utf8(
    - ((LPUSER_INFO_10) info)->usri10_full_name,
    - -1, NULL, NULL, NULL);
    - }
    - }
    -
    - if (info != NULL) NetApiBufferFree(info);
    - if (servername != NULL) NetApiBufferFree(servername);
    -
    - if (!fullname)
    - fullname = g_utf16_to_utf8(username, -1, NULL, NULL, NULL);
    - }
    -
    - g_task_return_pointer(task, fullname, g_free);
    -}
    -#endif
    -
    -static void
    -initialize_default_account_values(void)
    -{
    -#ifndef _WIN32
    - struct passwd *info;
    -#else
    - GTask *lookup;
    -#endif
    - const char *fullname = NULL, *splitpoint, *tmp;
    - gchar *conv = NULL;
    -
    -#ifndef _WIN32
    - /* Try to figure out the user's real name */
    - info = getpwuid(getuid());
    - if ((info != NULL) && (info->pw_gecos != NULL) && (info->pw_gecos[0] != '\0'))
    - fullname = info->pw_gecos;
    - else if ((info != NULL) && (info->pw_name != NULL) && (info->pw_name[0] != '\0'))
    - fullname = info->pw_name;
    - else if (((fullname = getlogin()) != NULL) && (fullname[0] == '\0'))
    - fullname = NULL;
    -#else
    - /* The Win32 username lookup functions are synchronous so we do it in a thread */
    - lookup = g_task_new(my_protocol, NULL, _set_default_name_cb, NULL);
    - g_task_run_in_thread(lookup, _win32_name_lookup_thread);
    - g_object_unref(lookup);
    -#endif
    -
    - /* Make sure fullname is valid UTF-8. If not, try to convert it. */
    - if (fullname != NULL && !g_utf8_validate(fullname, -1, NULL)) {
    - fullname = conv = g_locale_to_utf8(fullname, -1, NULL, NULL, NULL);
    - if (conv == NULL || *conv == '\0')
    - fullname = NULL;
    - }
    -
    - if (fullname == NULL)
    - fullname = _("Purple Person");
    -
    - /* Split the real name into a first and last name */
    - splitpoint = strchr(fullname, ' ');
    - if (splitpoint != NULL) {
    - default_firstname = g_strndup(fullname, splitpoint - fullname);
    - tmp = &splitpoint[1];
    -
    - /* The last name may be followed by a comma and additional data.
    - * Only use the last name itself.
    - */
    - splitpoint = strchr(tmp, ',');
    - if (splitpoint != NULL)
    - default_lastname = g_strndup(tmp, splitpoint - tmp);
    - else
    - default_lastname = g_strdup(tmp);
    - } else {
    - default_firstname = g_strdup(fullname);
    - default_lastname = g_strdup("");
    - }
    -
    - g_free(conv);
    -}
    -
    -static void
    -bonjour_protocol_init(G_GNUC_UNUSED BonjourProtocol *self)
    -{
    -}
    -
    -static void
    -bonjour_protocol_class_init(BonjourProtocolClass *klass)
    -{
    - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    -
    - protocol_class->login = bonjour_login;
    - protocol_class->close = bonjour_close;
    - protocol_class->status_types = bonjour_status_types;
    -
    - protocol_class->get_account_options = bonjour_protocol_get_account_options;
    - protocol_class->get_buddy_icon_spec = bonjour_protocol_get_buddy_icon_spec;
    -}
    -
    -static void
    -bonjour_protocol_class_finalize(G_GNUC_UNUSED BonjourProtocolClass *klass)
    -{
    -}
    -
    -static void
    -bonjour_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    -{
    - client_iface->convo_closed = bonjour_convo_closed;
    - client_iface->get_max_message_size = bonjour_get_max_message_size;
    -}
    -
    -static void
    -bonjour_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    -{
    - server_iface->add_buddy = bonjour_fake_add_buddy;
    - server_iface->remove_buddy = bonjour_remove_buddy;
    - server_iface->group_buddy = bonjour_group_buddy;
    - server_iface->rename_group = bonjour_rename_group;
    - server_iface->set_buddy_icon = bonjour_set_buddy_icon;
    - server_iface->set_status = bonjour_set_status;
    -}
    -
    -static void
    -bonjour_protocol_conversation_iface_init(PurpleProtocolConversationInterface *iface)
    -{
    - iface->send_message_async = bonjour_conversation_send_message_async;
    - iface->send_message_finish = bonjour_conversation_send_message_finish;
    -}
    -
    -static void
    -bonjour_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
    -{
    - xfer_iface->can_receive = bonjour_can_receive_file;
    - xfer_iface->send_file = bonjour_send_file;
    - xfer_iface->new_xfer = bonjour_new_xfer;
    -}
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - BonjourProtocol,
    - bonjour_protocol,
    - PURPLE_TYPE_PROTOCOL,
    - G_TYPE_FLAG_FINAL,
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    - bonjour_protocol_client_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    - bonjour_protocol_server_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CONVERSATION,
    - bonjour_protocol_conversation_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    - bonjour_protocol_xfer_iface_init))
    -
    -static PurpleProtocol *
    -bonjour_protocol_new(void) {
    - return g_object_new(
    - BONJOUR_TYPE_PROTOCOL,
    - "id", "prpl-bonjour",
    - "name", "Bonjour (Deprecated)",
    - "description", _("Bonjour is a serverless protocol for local "
    - "networks."),
    - "icon-name", "im-bonjour",
    - "icon-resource-path", "/im/pidgin/libpurple/bonjour/icons",
    - "options", OPT_PROTO_NO_PASSWORD,
    - NULL);
    -}
    -
    -static GPluginPluginInfo *
    -bonjour_query(G_GNUC_UNUSED GError **error)
    -{
    - return purple_plugin_info_new(
    - "id", "prpl-bonjour",
    - "name", "Bonjour Protocol",
    - "version", DISPLAY_VERSION,
    - "category", N_("Protocol"),
    - "summary", N_("Bonjour Protocol Plugin"),
    - "description", N_("Bonjour Protocol Plugin"),
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
    - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
    - NULL
    - );
    -}
    -
    -static gboolean
    -bonjour_load(GPluginPlugin *plugin, GError **error)
    -{
    - PurpleProtocolManager *manager = purple_protocol_manager_get_default();
    -
    - bonjour_protocol_register_type(G_TYPE_MODULE(plugin));
    -
    - xep_xfer_register(G_TYPE_MODULE(plugin));
    -
    - my_protocol = bonjour_protocol_new();
    - if(!purple_protocol_manager_register(manager, my_protocol, error)) {
    - g_clear_object(&my_protocol);
    -
    - return FALSE;
    - }
    -
    - initialize_default_account_values();
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -bonjour_unload(G_GNUC_UNUSED 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;
    - }
    -
    - g_clear_object(&my_protocol);
    -
    - g_free(default_firstname);
    - g_free(default_lastname);
    -
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(bonjour)
    --- a/libpurple/protocols/bonjour/bonjour.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,68 +0,0 @@
    -/**
    - * @file bonjour.h The Purple interface to mDNS and peer to peer XMPP.
    - *
    - * purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_BONJOUR_BONJOUR_H
    -#define PURPLE_BONJOUR_BONJOUR_H
    -
    -#include <gmodule.h>
    -
    -#include <purple.h>
    -
    -#include "mdns_common.h"
    -#include "xmpp.h"
    -
    -#define BONJOUR_GROUP_NAME (bonjour_get_group_name())
    -#define BONJOUR_PROTOCOL_NAME "bonjour"
    -
    -#define BONJOUR_STATUS_ID_OFFLINE "offline"
    -#define BONJOUR_STATUS_ID_AVAILABLE "available"
    -#define BONJOUR_STATUS_ID_AWAY "away"
    -
    -#define BONJOUR_DEFAULT_PORT 5298
    -
    -#define BONJOUR_DOMAIN (g_quark_from_static_string("bonjour"))
    -
    -#define BONJOUR_TYPE_PROTOCOL (bonjour_protocol_get_type())
    -
    -G_MODULE_EXPORT
    -G_DECLARE_FINAL_TYPE(BonjourProtocol, bonjour_protocol, BONJOUR, PROTOCOL,
    - PurpleProtocol)
    -
    -typedef struct
    -{
    - BonjourDnsSd *dns_sd_data;
    - BonjourXMPP *xmpp_data;
    - GSList *xfer_lists;
    - gchar *jid;
    -} BonjourData;
    -
    -/**
    - * This will always be username@machinename
    - */
    -const char *bonjour_get_jid(PurpleAccount *account);
    -
    -const gchar *bonjour_get_group_name(void);
    -
    -#endif /* PURPLE_BONJOUR_BONJOUR_H */
    --- a/libpurple/protocols/bonjour/bonjour_ft.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1228 +0,0 @@
    -/*
    - * purple - Bonjour Protocol Plugin
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
    - */
    -
    -#include <purpleconfig.h>
    -
    -#include <sys/types.h>
    -
    -#include <purple.h>
    -
    -#include "bonjour.h"
    -#include "bonjour_ft.h"
    -
    -static void
    -bonjour_bytestreams_init(PurpleXfer *xfer);
    -static void
    -bonjour_bytestreams_connect(PurpleXfer *xfer);
    -static void
    -bonjour_xfer_init(PurpleXfer *xfer);
    -static void
    -bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
    - goffset filesize, const char *filename, int option);
    -
    -/* Look for specific xfer handle */
    -static unsigned int next_id = 0;
    -
    -struct _XepXfer
    -{
    - PurpleXfer parent;
    -
    - GCancellable *cancellable;
    - void *data;
    - char *filename;
    - int filesize;
    - char *iq_id;
    - char *sid;
    - char *recv_id;
    - char *buddy_ip;
    - int mode;
    - GSocketClient *client;
    - GSocketService *service;
    - GSocketConnection *conn;
    - char rx_buf[0x500];
    - char tx_buf[0x500];
    - char *jid;
    - char *proxy_host;
    - int proxy_port;
    - PurpleXmlNode *streamhost;
    - PurpleContact *contact;
    -};
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(XepXfer, xep_xfer, PURPLE_TYPE_XFER,
    - G_TYPE_FLAG_FINAL, {})
    -
    -static void
    -xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type)
    -{
    - PurpleXmlNode *error_node;
    - XepIq *iq;
    -
    - g_return_if_fail(error_code != NULL);
    - g_return_if_fail(error_type != NULL);
    -
    - if(!to || !id) {
    - purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
    - return;
    - }
    -
    - iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->xmpp_data->account), id);
    - if(iq == NULL)
    - return;
    -
    - error_node = purple_xmlnode_new_child(iq->node, "error");
    - purple_xmlnode_set_attrib(error_node, "code", error_code);
    - purple_xmlnode_set_attrib(error_node, "type", error_type);
    -
    - /* TODO: Make this better */
    - if (purple_strequal(error_code, "403")) {
    - PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "forbidden");
    - purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
    -
    - tmp_node = purple_xmlnode_new_child(error_node, "text");
    - purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
    - purple_xmlnode_insert_data(tmp_node, "Offer Declined", -1);
    - } else if (purple_strequal(error_code, "404")) {
    - PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "item-not-found");
    - purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
    - }
    -
    - xep_iq_send_and_free(iq);
    -}
    -
    -static void bonjour_xfer_cancel_send(PurpleXfer *xfer)
    -{
    - XepXfer *xf = XEP_XFER(xfer);
    -
    - purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
    - g_cancellable_cancel(xf->cancellable);
    -}
    -
    -static void bonjour_xfer_request_denied(PurpleXfer *xfer)
    -{
    - XepXfer *xf = XEP_XFER(xfer);
    -
    - purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
    -
    - if(xf) {
    - xep_ft_si_reject(xf->data, xf->sid, purple_xfer_get_remote_user(xfer), "403", "cancel");
    - }
    -}
    -
    -static void bonjour_xfer_cancel_recv(PurpleXfer *xfer)
    -{
    - XepXfer *xf = XEP_XFER(xfer);
    -
    - purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
    - g_cancellable_cancel(xf->cancellable);
    -}
    -
    -static void bonjour_xfer_end(PurpleXfer *xfer)
    -{
    - purple_debug_info("bonjour", "Bonjour-xfer-end for xfer %p", xfer);
    -
    - /* We can't allow the server side to close the connection until the client is complete,
    - * otherwise there is a RST resulting in an error on the client side */
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND && purple_xfer_is_completed(xfer)) {
    - XepXfer *xf = XEP_XFER(xfer);
    - g_io_stream_close_async(G_IO_STREAM(xf->conn), G_PRIORITY_DEFAULT, xf->cancellable, NULL, NULL);
    - purple_xfer_set_fd(xfer, -1);
    - }
    -}
    -
    -static PurpleXfer*
    -bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
    -{
    - GSList *xfers;
    - PurpleXfer *xfer;
    - XepXfer *xf;
    -
    - if(!sid || !from || !bd)
    - return NULL;
    -
    - purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
    - sid, from);
    -
    - for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) {
    - xfer = xfers->data;
    - if(xfer == NULL)
    - break;
    - xf = XEP_XFER(xfer);
    - if(xf == NULL)
    - break;
    - if(xf->sid && purple_xfer_get_remote_user(xfer) && purple_strequal(xf->sid, sid) &&
    - purple_strequal(purple_xfer_get_remote_user(xfer), from))
    - return xfer;
    - }
    -
    - purple_debug_info("bonjour", "Look for xfer list fail\n");
    -
    - return NULL;
    -}
    -
    -static void
    -xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
    -{
    - PurpleXmlNode *si_node, *feature, *field, *file, *x;
    - XepIq *iq;
    - XepXfer *xf = XEP_XFER(xfer);
    - BonjourData *bd = NULL;
    - char buf[32];
    -
    - if(!xf)
    - return;
    -
    - bd = xf->data;
    - if(!bd)
    - return;
    -
    - purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id);
    -
    - /* Assign stream id. */
    - g_free(xf->iq_id);
    - xf->iq_id = g_strdup_printf("%u", next_id++);
    - iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->xmpp_data->account), xf->iq_id);
    - if(iq == NULL)
    - return;
    -
    - /*Construct Stream initialization offer message.*/
    - si_node = purple_xmlnode_new_child(iq->node, "si");
    - purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
    - purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
    - g_free(xf->sid);
    - xf->sid = g_strdup(xf->iq_id);
    - purple_xmlnode_set_attrib(si_node, "id", xf->sid);
    -
    - file = purple_xmlnode_new_child(si_node, "file");
    - purple_xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
    - purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer));
    - g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer));
    - purple_xmlnode_set_attrib(file, "size", buf);
    -
    - feature = purple_xmlnode_new_child(si_node, "feature");
    - purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
    -
    - x = purple_xmlnode_new_child(feature, "x");
    - purple_xmlnode_set_namespace(x, "jabber:x:data");
    - purple_xmlnode_set_attrib(x, "type", "form");
    -
    - field = purple_xmlnode_new_child(x, "field");
    - purple_xmlnode_set_attrib(field, "var", "stream-method");
    - purple_xmlnode_set_attrib(field, "type", "list-single");
    -
    - if (xf->mode & XEP_BYTESTREAMS) {
    - PurpleXmlNode *option = purple_xmlnode_new_child(field, "option");
    - PurpleXmlNode *value = purple_xmlnode_new_child(option, "value");
    - purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
    - }
    - if (xf->mode & XEP_IBB) {
    - PurpleXmlNode *option = purple_xmlnode_new_child(field, "option");
    - PurpleXmlNode *value = purple_xmlnode_new_child(option, "value");
    - purple_xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
    - }
    -
    - xep_iq_send_and_free(iq);
    -}
    -
    -static void
    -xep_ft_si_result(PurpleXfer *xfer, const char *to)
    -{
    - PurpleXmlNode *si_node, *feature, *field, *value, *x;
    - XepIq *iq;
    - XepXfer *xf;
    - BonjourData *bd;
    -
    - if(!to || !xfer)
    - return;
    - xf = XEP_XFER(xfer);
    -
    - bd = xf->data;
    -
    - purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
    - iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->xmpp_data->account), xf->iq_id);
    - if(iq == NULL)
    - return;
    -
    - si_node = purple_xmlnode_new_child(iq->node, "si");
    - purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
    - /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
    -
    - feature = purple_xmlnode_new_child(si_node, "feature");
    - purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
    -
    - x = purple_xmlnode_new_child(feature, "x");
    - purple_xmlnode_set_namespace(x, "jabber:x:data");
    - purple_xmlnode_set_attrib(x, "type", "submit");
    -
    - field = purple_xmlnode_new_child(x, "field");
    - purple_xmlnode_set_attrib(field, "var", "stream-method");
    -
    - value = purple_xmlnode_new_child(field, "value");
    - purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
    -
    - xep_iq_send_and_free(iq);
    -}
    -
    -/**
    - * Frees the whole tree of an xml node
    - *
    - * First determines the root of the xml tree and then frees the whole tree
    - * from there.
    - *
    - * @param node The node to free the tree from
    - */
    -static void
    -purple_xmlnode_free_tree(PurpleXmlNode *node)
    -{
    - g_return_if_fail(node != NULL);
    -
    - while(purple_xmlnode_get_parent(node))
    - node = purple_xmlnode_get_parent(node);
    -
    - purple_xmlnode_free(node);
    -}
    -
    -PurpleXfer *
    -bonjour_new_xfer(G_GNUC_UNUSED PurpleProtocolXfer *prplxfer,
    - PurpleConnection *gc, const char *who)
    -{
    - PurpleXfer *xfer;
    - XepXfer *xep_xfer;
    - BonjourData *bd;
    -
    - if(who == NULL || gc == NULL)
    - return NULL;
    -
    - purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
    - bd = purple_connection_get_protocol_data(gc);
    - if(bd == NULL)
    - return NULL;
    -
    - /* Build the file transfer handle */
    - xep_xfer = g_object_new(
    - XEP_TYPE_XFER,
    - "account", purple_connection_get_account(gc),
    - "type", PURPLE_XFER_TYPE_SEND,
    - "remote-user", who,
    - NULL
    - );
    - xfer = PURPLE_XFER(xep_xfer);
    - xep_xfer->data = bd;
    -
    - purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
    -
    - /* We don't support IBB yet */
    - /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
    - xep_xfer->mode = XEP_BYTESTREAMS;
    - xep_xfer->sid = NULL;
    -
    - bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
    -
    - return xfer;
    -}
    -
    -void
    -bonjour_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file)
    -{
    - PurpleXfer *xfer;
    -
    - g_return_if_fail(gc != NULL);
    - g_return_if_fail(who != NULL);
    -
    - purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who);
    -
    - xfer = bonjour_new_xfer(prplxfer, gc, who);
    -
    - if (file)
    - purple_xfer_request_accepted(xfer, file);
    - else
    - purple_xfer_request(xfer);
    -
    -}
    -
    -static void
    -bonjour_xfer_init(PurpleXfer *xfer)
    -{
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    - BonjourBuddy *bb;
    - XepXfer *xf;
    -
    - xf = XEP_XFER(xfer);
    -
    - purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager,
    - purple_xfer_get_account(xfer),
    - purple_xfer_get_remote_user(xfer));
    -
    - /* this buddy is offline. */
    - if(!PURPLE_IS_CONTACT(contact) ||
    - (bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy")) == NULL)
    - {
    - g_clear_object(&contact);
    -
    - return;
    - }
    -
    - /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
    - if (bb->ips)
    - xf->buddy_ip = g_strdup(bb->ips->data);
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
    - /* initiate file transfer, send SI offer. */
    - purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_SEND.\n");
    - xep_ft_si_offer(xfer, purple_xfer_get_remote_user(xfer));
    - } else {
    - /* accept file transfer request, send SI result. */
    - xep_ft_si_result(xfer, purple_xfer_get_remote_user(xfer));
    - purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_RECEIVE.\n");
    - }
    -
    - g_clear_object(&contact);
    -}
    -
    -void
    -xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet,
    - PurpleContact *contact)
    -{
    - const char *type, *id;
    - BonjourData *bd;
    - PurpleXfer *xfer;
    - const gchar *name = NULL;
    -
    - g_return_if_fail(pc != NULL);
    - g_return_if_fail(packet != NULL);
    - g_return_if_fail(PURPLE_IS_CONTACT(contact));
    -
    - bd = purple_connection_get_protocol_data(pc);
    - if(bd == NULL) {
    - return;
    - }
    -
    - purple_debug_info("bonjour", "xep-si-parse.\n");
    -
    - name = purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact));
    -
    - type = purple_xmlnode_get_attrib(packet, "type");
    - id = purple_xmlnode_get_attrib(packet, "id");
    - if(!type)
    - return;
    -
    - if(purple_strequal(type, "set")) {
    - PurpleXmlNode *si;
    - gboolean parsed_receive = FALSE;
    -
    - si = purple_xmlnode_get_child(packet, "si");
    -
    - purple_debug_info("bonjour", "si offer Message type - SET.\n");
    - if (si) {
    - const char *profile;
    -
    - profile = purple_xmlnode_get_attrib(si, "profile");
    -
    - if (purple_strequal(profile, "http://jabber.org/protocol/si/profile/file-transfer")) {
    - const char *filename = NULL, *filesize_str = NULL;
    - goffset filesize = 0;
    - PurpleXmlNode *file;
    -
    - const char *sid = purple_xmlnode_get_attrib(si, "id");
    -
    - if ((file = purple_xmlnode_get_child(si, "file"))) {
    - filename = purple_xmlnode_get_attrib(file, "name");
    - if((filesize_str = purple_xmlnode_get_attrib(file, "size")))
    - filesize = g_ascii_strtoll(filesize_str, NULL, 10);
    - }
    -
    - /* TODO: Make sure that it is advertising a bytestreams transfer */
    -
    - if (filename) {
    - bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS);
    -
    - parsed_receive = TRUE;
    - }
    - }
    - }
    -
    - if (!parsed_receive) {
    - BonjourData *bd = purple_connection_get_protocol_data(pc);
    -
    - purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
    - xep_ft_si_reject(bd, id, name, "403", "cancel");
    - /*TODO: Send Cancel (501) */
    - }
    - } else if(purple_strequal(type, "result")) {
    - purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
    -
    - xfer = bonjour_si_xfer_find(bd, id, name);
    -
    - if(xfer == NULL) {
    - BonjourData *bd = purple_connection_get_protocol_data(pc);
    - purple_debug_info("bonjour", "xfer find fail.\n");
    - xep_ft_si_reject(bd, id, name, "403", "cancel");
    - } else
    - bonjour_bytestreams_init(xfer);
    -
    - } else if(purple_strequal(type, "error")) {
    - purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
    -
    - xfer = bonjour_si_xfer_find(bd, id, name);
    -
    - if(xfer == NULL)
    - purple_debug_info("bonjour", "xfer find fail.\n");
    - else
    - purple_xfer_cancel_remote(xfer);
    - } else
    - purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type);
    -}
    -
    -/**
    - * Will compare a host with a buddy_ip.
    - *
    - * Additionally to a common 'purple_strequal(host, buddy_ip)', it will also return TRUE
    - * if 'host' is a link local IPv6 address without an appended interface
    - * identifier and 'buddy_ip' string is "host" + "%iface".
    - *
    - * Note: This may theoretically result in the attempt to connect to the wrong
    - * host, because we do not know for sure which interface the according link
    - * local IPv6 address might relate to and RFC4862 for instance only ensures the
    - * uniqueness of this address on a given link. So we could possibly have two
    - * distinct buddies with the same ipv6 link local address on two distinct
    - * interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with
    - * link local ip addresses properly...
    - * However, in practice the possibility for such a conflict is relatively low
    - * (2011 - might be different in the future though?).
    - *
    - * @param host ipv4 or ipv6 address string
    - * @param buddy_ip ipv4 or ipv6 address string
    - * @return TRUE if they match, FALSE otherwise
    - */
    -static gboolean
    -xep_cmp_addr(const char *host, const char *buddy_ip)
    -{
    - GInetAddress *addr = NULL;
    -
    - addr = g_inet_address_new_from_string(host);
    - if (addr != NULL &&
    - g_inet_address_get_family(addr) == G_SOCKET_FAMILY_IPV6 &&
    - g_inet_address_get_is_link_local(addr)) {
    - g_clear_object(&addr);
    -
    - if (strlen(buddy_ip) <= strlen(host) || buddy_ip[strlen(host)] != '%') {
    - return FALSE;
    - }
    -
    - return !strncmp(host, buddy_ip, strlen(host));
    - } else {
    - g_clear_object(&addr);
    - return purple_strequal(host, buddy_ip);
    - }
    -}
    -
    -static inline gint
    -xep_addr_differ(const char *buddy_ip, const char *host)
    -{
    - return !xep_cmp_addr(host, buddy_ip);
    -}
    -
    -/**
    - * Create and insert an identical twin
    - *
    - * Creates a copy of the specified node and inserts it right after
    - * this original node.
    - *
    - * @param node The node to clone
    - * @return A pointer to the new, cloned twin if successful
    - * or NULL otherwise.
    - */
    -static PurpleXmlNode *
    -purple_xmlnode_insert_twin_copy(PurpleXmlNode *node) {
    - PurpleXmlNode *copy;
    -
    - g_return_val_if_fail(node != NULL, NULL);
    -
    - copy = purple_xmlnode_copy(node);
    - g_return_val_if_fail(copy != NULL, NULL);
    -
    - copy->next = node->next;
    - node->next = copy;
    -
    - return copy;
    -}
    -
    -/**
    - * Tries to append an interface scope to an IPv6 link local address.
    - *
    - * If the given address is a link local IPv6 address (with no
    - * interface scope) then we try to determine all fitting interfaces
    - * from our Bonjour IP address list.
    - *
    - * For any such found matches we insert a copy of our current xml
    - * streamhost entry right after this streamhost entry and append
    - * the determined interface to the host address of this copy.
    - *
    - * @param cur_streamhost The XML streamhost node we examine
    - * @param host The host address to examine in text form
    - * @param pb Buddy to get the list of link local IPv6 addresses
    - * and their interface from
    - * @return Returns TRUE if the specified 'host' address is a
    - * link local IPv6 address with no interface scope.
    - * Otherwise returns FALSE.
    - */
    -static gboolean
    -add_ipv6_link_local_ifaces(PurpleXmlNode *cur_streamhost, const char *host,
    - PurpleContact *contact)
    -{
    - PurpleXmlNode *new_streamhost = NULL;
    - GInetAddress *addr;
    - BonjourBuddy *bb;
    - GSList *ip_elem;
    -
    - addr = g_inet_address_new_from_string(host);
    - if (addr == NULL ||
    - g_inet_address_get_family(addr) != G_SOCKET_FAMILY_IPV6 ||
    - !g_inet_address_get_is_link_local(addr) ||
    - strchr(host, '%'))
    - {
    - g_clear_object(&addr);
    - return FALSE;
    - }
    - g_clear_object(&addr);
    -
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    -
    - for (ip_elem = bb->ips;
    - (ip_elem = g_slist_find_custom(ip_elem, host, (GCompareFunc)&xep_addr_differ));
    - ip_elem = ip_elem->next) {
    - purple_debug_info("bonjour", "Inserting an PurpleXmlNode twin copy for %s with new host address %s\n",
    - host, (char*)ip_elem->data);
    - new_streamhost = purple_xmlnode_insert_twin_copy(cur_streamhost);
    - purple_xmlnode_set_attrib(new_streamhost, "host", ip_elem->data);
    - }
    -
    - if (!new_streamhost)
    - purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n",
    - host);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -__xep_bytestreams_parse(PurpleContact *contact, PurpleXfer *xfer,
    - PurpleXmlNode *streamhost, const char *iq_id)
    -{
    - char *tmp_iq_id;
    - const char *jid, *host, *port;
    - int portnum;
    - XepXfer *xf = XEP_XFER(xfer);
    -
    - for(; streamhost; streamhost = purple_xmlnode_get_next_twin(streamhost)) {
    - if(!(jid = purple_xmlnode_get_attrib(streamhost, "jid")) ||
    - !(host = purple_xmlnode_get_attrib(streamhost, "host")) ||
    - !(port = purple_xmlnode_get_attrib(streamhost, "port")) ||
    - !(portnum = atoi(port))) {
    - purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
    - continue;
    - }
    -
    - /* skip IPv6 link local addresses with no interface scope
    - * (but try to add a new one with an interface scope then) */
    - if(add_ipv6_link_local_ifaces(streamhost, host, contact))
    - continue;
    -
    - tmp_iq_id = g_strdup(iq_id);
    - g_free(xf->iq_id);
    - g_free(xf->jid);
    - g_free(xf->proxy_host);
    -
    - xf->iq_id = tmp_iq_id;
    - xf->jid = g_strdup(jid);
    - xf->proxy_host = g_strdup(host);
    - xf->proxy_port = portnum;
    - xf->streamhost = streamhost;
    - xf->contact = contact;
    - purple_debug_info("bonjour", "bytestream offer parse"
    - "jid=%s host=%s port=%d.\n", jid, host, portnum);
    - bonjour_bytestreams_connect(xfer);
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -void
    -xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet,
    - PurpleContact *contact)
    -{
    - const char *type, *from, *iq_id, *sid;
    - PurpleXmlNode *query, *streamhost;
    - BonjourData *bd;
    - PurpleXfer *xfer;
    -
    - g_return_if_fail(pc != NULL);
    - g_return_if_fail(packet != NULL);
    - g_return_if_fail(PURPLE_IS_CONTACT(contact));
    -
    - bd = purple_connection_get_protocol_data(pc);
    - if(bd == NULL) {
    - return;
    - }
    -
    - purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
    -
    - type = purple_xmlnode_get_attrib(packet, "type");
    - from = purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact));
    - query = purple_xmlnode_get_child(packet,"query");
    - if(!type)
    - return;
    -
    - query = purple_xmlnode_copy(query);
    - if (!query)
    - return;
    -
    - if(!purple_strequal(type, "set")) {
    - purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
    - return;
    - }
    -
    - purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
    -
    - iq_id = purple_xmlnode_get_attrib(packet, "id");
    -
    - sid = purple_xmlnode_get_attrib(query, "sid");
    - xfer = bonjour_si_xfer_find(bd, sid, from);
    - streamhost = purple_xmlnode_get_child(query, "streamhost");
    -
    - if(xfer && streamhost && __xep_bytestreams_parse(contact, xfer, streamhost, iq_id))
    - return; /* success */
    -
    - purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
    -
    - if (iq_id && xfer != NULL)
    - xep_ft_si_reject(bd, iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
    -}
    -
    -static void
    -bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid,
    - const char *from, goffset filesize, const char *filename,
    - G_GNUC_UNUSED int option)
    -{
    - PurpleXfer *xfer;
    - XepXfer *xf;
    - BonjourData *bd;
    -
    - if(pc == NULL || id == NULL || from == NULL)
    - return;
    -
    - bd = purple_connection_get_protocol_data(pc);
    - if(bd == NULL)
    - return;
    -
    - purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
    -
    - /* Build the file transfer handle */
    - xf = g_object_new(
    - XEP_TYPE_XFER,
    - "account", purple_connection_get_account(pc),
    - "type", PURPLE_XFER_TYPE_RECEIVE,
    - "remote-user", from,
    - NULL
    - );
    -
    - xfer = PURPLE_XFER(xf);
    -
    - xf->data = bd;
    - purple_xfer_set_filename(xfer, filename);
    - xf->iq_id = g_strdup(id);
    - xf->sid = g_strdup(sid);
    -
    - if(filesize > 0)
    - purple_xfer_set_size(xfer, filesize);
    -
    - bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
    -
    - purple_xfer_request(xfer);
    -}
    -
    -static void
    -bonjour_sock5_request_cb(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(user_data);
    - XepXfer *xf = XEP_XFER(xfer);
    - gsize bytes_written = 0;
    - GError *error = NULL;
    - GSocket *sock = NULL;
    - gint fd = -1;
    -
    - purple_debug_info("bonjour", "bonjour_sock5_request_cb");
    -
    - if (!g_output_stream_write_all_finish(G_OUTPUT_STREAM(source), result,
    - &bytes_written, &error)) {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - purple_xfer_cancel_remote(xfer);
    - }
    - g_clear_error(&error);
    - return;
    - }
    -
    - sock = g_socket_connection_get_socket(xf->conn);
    - fd = g_socket_get_fd(sock);
    - purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d", fd);
    -
    - _purple_network_set_common_socket_flags(fd);
    - purple_xfer_start(xfer, fd, NULL, -1);
    -}
    -
    -static void
    -bonjour_sock5_read_connect_cb(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(user_data);
    - XepXfer *xf = XEP_XFER(xfer);
    - GOutputStream *output = NULL;
    - gsize bytes_read = 0;
    - GError *error = NULL;
    -
    - purple_debug_info("bonjour", "bonjour_sock5_request_state4_cb");
    -
    - if (!g_input_stream_read_all_finish(G_INPUT_STREAM(source), result,
    - &bytes_read, &error)) {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - purple_xfer_cancel_remote(xfer);
    - }
    - g_clear_error(&error);
    - return;
    - }
    -
    - xf->tx_buf[0] = 0x05;
    - xf->tx_buf[1] = 0x00;
    - xf->tx_buf[2] = 0x00;
    - xf->tx_buf[3] = 0x03;
    - xf->tx_buf[4] = strlen(xf->buddy_ip);
    - memcpy(xf->tx_buf + 5, xf->buddy_ip, strlen(xf->buddy_ip));
    - xf->tx_buf[5 + strlen(xf->buddy_ip)] = 0x00;
    - xf->tx_buf[6 + strlen(xf->buddy_ip)] = 0x00;
    - output = g_io_stream_get_output_stream(G_IO_STREAM(xf->conn));
    - g_output_stream_write_all_async(
    - output, xf->tx_buf, 7 + strlen(xf->buddy_ip), G_PRIORITY_DEFAULT,
    - xf->cancellable, bonjour_sock5_request_cb, xfer);
    -}
    -
    -static void
    -bonjour_sock5_write_server_method_cb(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(user_data);
    - XepXfer *xf = XEP_XFER(xfer);
    - GInputStream *input = NULL;
    - gsize bytes_written = 0;
    - GError *error = NULL;
    -
    - purple_debug_info("bonjour", "bonjour_sock5_request_state3_cb");
    -
    - if (!g_output_stream_write_all_finish(G_OUTPUT_STREAM(source), result,
    - &bytes_written, &error)) {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - purple_xfer_cancel_remote(xfer);
    - }
    - g_clear_error(&error);
    - return;
    - }
    -
    - input = g_io_stream_get_input_stream(G_IO_STREAM(xf->conn));
    - g_input_stream_read_all_async(input, xf->rx_buf, 20, G_PRIORITY_DEFAULT,
    - xf->cancellable,
    - bonjour_sock5_read_connect_cb, xfer);
    -}
    -
    -static void
    -bonjour_sock5_read_client_version_cb(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(user_data);
    - XepXfer *xf = XEP_XFER(xfer);
    - GOutputStream *output = NULL;
    - gsize bytes_read = 0;
    - GError *error = NULL;
    -
    - purple_debug_info("bonjour", "bonjour_sock5_read_client_version_cb");
    -
    - if (!g_input_stream_read_all_finish(G_INPUT_STREAM(source), result,
    - &bytes_read, &error)) {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - purple_xfer_cancel_remote(xfer);
    - }
    - g_clear_error(&error);
    - return;
    - }
    -
    - xf->tx_buf[0] = 0x05;
    - xf->tx_buf[1] = 0x00;
    - output = g_io_stream_get_output_stream(G_IO_STREAM(xf->conn));
    - g_output_stream_write_all_async(output, xf->tx_buf, 2, G_PRIORITY_DEFAULT,
    - xf->cancellable,
    - bonjour_sock5_write_server_method_cb, xfer);
    -}
    -
    -static void
    -bonjour_sock5_incoming_cb(G_GNUC_UNUSED GSocketService *service,
    - GSocketConnection *connection, GObject *source_object,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(source_object);
    - XepXfer *xf = XEP_XFER(xfer);
    - GInputStream *input = NULL;
    -
    - if (xf == NULL) {
    - return;
    - }
    -
    - purple_debug_info("bonjour", "bonjour_sock5_incoming_cb");
    -
    - xf->conn = g_object_ref(connection);
    - g_socket_service_stop(xf->service);
    - g_clear_object(&xf->service);
    -
    - purple_debug_info("bonjour", "Accepted SOCKS5 ft connection");
    - input = g_io_stream_get_input_stream(G_IO_STREAM(xf->conn));
    -
    - g_input_stream_read_all_async(input, xf->rx_buf, 3, G_PRIORITY_DEFAULT,
    - xf->cancellable,
    - bonjour_sock5_read_client_version_cb, xfer);
    -}
    -
    -static void
    -bonjour_bytestreams_init(PurpleXfer *xfer)
    -{
    - XepXfer *xf;
    - XepIq *iq;
    - PurpleXmlNode *query, *streamhost;
    - guint16 port;
    - gchar *port_str;
    - GList *local_ips;
    - BonjourData *bd;
    - GError *error = NULL;
    -
    - if (xfer == NULL) {
    - return;
    - }
    -
    - purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
    - xf = XEP_XFER(xfer);
    -
    - xf->service = g_socket_service_new();
    - port = purple_socket_listener_add_any_inet_port(
    - G_SOCKET_LISTENER(xf->service), G_OBJECT(xfer), &error);
    - if (port == 0) {
    - purple_debug_error("bonjour",
    - "Unable to open port for file transfer: %s",
    - error->message);
    - purple_xfer_cancel_local(xfer);
    - g_error_free(error);
    - return;
    - }
    -
    - g_signal_connect(xf->service, "incoming",
    - G_CALLBACK(bonjour_sock5_incoming_cb), NULL);
    -
    - bd = xf->data;
    -
    - iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->xmpp_data->account), xf->sid);
    -
    - query = purple_xmlnode_new_child(iq->node, "query");
    - purple_xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
    - purple_xmlnode_set_attrib(query, "sid", xf->sid);
    - purple_xmlnode_set_attrib(query, "mode", "tcp");
    -
    - purple_xfer_set_local_port(xfer, port);
    -
    - #pragma message("Need to figure out how to get the local ip addresses")
    - local_ips = NULL;
    -
    - port_str = g_strdup_printf("%hu", port);
    - while(local_ips) {
    - streamhost = purple_xmlnode_new_child(query, "streamhost");
    - purple_xmlnode_set_attrib(streamhost, "jid", xf->sid);
    - purple_xmlnode_set_attrib(streamhost, "host", local_ips->data);
    - purple_xmlnode_set_attrib(streamhost, "port", port_str);
    - g_free(local_ips->data);
    - local_ips = g_list_delete_link(local_ips, local_ips);
    - }
    - g_free(port_str);
    -
    - xep_iq_send_and_free(iq);
    -}
    -
    -static void
    -bonjour_bytestreams_handle_failure(PurpleXfer *xfer, const gchar *error_message)
    -{
    - XepXfer *xf = XEP_XFER(xfer);
    - PurpleXmlNode *tmp_node;
    - gboolean ret;
    -
    - purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s",
    - xf->proxy_host, error_message);
    -
    - tmp_node = purple_xmlnode_get_next_twin(xf->streamhost);
    - ret = __xep_bytestreams_parse(xf->contact, xfer, tmp_node, xf->iq_id);
    -
    - if (!ret) {
    - xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer),
    - "404", "cancel");
    - /* Cancel the connection */
    - purple_xfer_cancel_local(xfer);
    - }
    -}
    -
    -static void
    -bonjour_bytestreams_connect_cb(GObject *source, GAsyncResult *result,
    - gpointer user_data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(user_data);
    - XepXfer *xf = XEP_XFER(xfer);
    - GIOStream *stream;
    - GError *error = NULL;
    - GSocket *socket;
    - XepIq *iq;
    - PurpleXmlNode *q_node, *tmp_node;
    - BonjourData *bd;
    -
    - stream = g_proxy_connect_finish(G_PROXY(source), result, &error);
    - if (stream == NULL) {
    - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    - purple_debug_error("bonjour",
    - "Unable to connect to destination host: %s",
    - error->message);
    - bonjour_bytestreams_handle_failure(
    - xfer, "Unable to connect to destination host.");
    - }
    -
    - g_clear_error(&error);
    - return;
    - }
    -
    - if (!G_IS_SOCKET_CONNECTION(stream)) {
    - purple_debug_error("bonjour",
    - "GProxy didn't return a GSocketConnection.");
    - bonjour_bytestreams_handle_failure(
    - xfer, "GProxy didn't return a GSocketConnection.");
    - g_object_unref(stream);
    - return;
    - }
    -
    - purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
    -
    - bd = xf->data;
    -
    - /* Here, start the file transfer.*/
    -
    - /* Notify Initiator of Connection */
    - iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->xmpp_data->account), xf->iq_id);
    - q_node = purple_xmlnode_new_child(iq->node, "query");
    - purple_xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
    - tmp_node = purple_xmlnode_new_child(q_node, "streamhost-used");
    - purple_xmlnode_set_attrib(tmp_node, "jid", xf->jid);
    - xep_iq_send_and_free(iq);
    -
    - xf->conn = G_SOCKET_CONNECTION(stream);
    - socket = g_socket_connection_get_socket(xf->conn);
    - purple_xfer_start(xfer, g_socket_get_fd(socket), NULL, -1);
    -}
    -
    -/* This is called when we connect to the SOCKS5 proxy server (through any
    - * relevant account proxy)
    - */
    -static void
    -bonjour_bytestreams_socks5_connect_to_host_cb(GObject *source,
    - GAsyncResult *result,
    - gpointer user_data)
    -{
    - PurpleXfer *xfer = PURPLE_XFER(user_data);
    - XepXfer *xf = XEP_XFER(xfer);
    - GSocketConnection *conn;
    - GProxy *proxy;
    - GSocketAddress *addr;
    - GInetSocketAddress *inet_addr;
    - GSocketAddress *proxy_addr;
    - PurpleContact *contact = NULL;
    - gchar *hash_input;
    - gchar *dstaddr;
    - GError *error = NULL;
    -
    - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    - result, &error);
    - if (conn == NULL) {
    - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    - purple_debug_error("bonjour",
    - "Unable to connect to SOCKS5 host: %s",
    - error->message);
    - bonjour_bytestreams_handle_failure(
    - xfer, "Unable to connect to SOCKS5 host.");
    - }
    -
    - g_clear_error(&error);
    - return;
    - }
    -
    - proxy = g_proxy_get_default_for_protocol("socks5");
    - if (proxy == NULL) {
    - purple_debug_error("bonjour", "SOCKS5 proxy backend missing.");
    - bonjour_bytestreams_handle_failure(xfer,
    - "SOCKS5 proxy backend missing.");
    - g_object_unref(conn);
    - return;
    - }
    -
    - addr = g_socket_connection_get_remote_address(conn, &error);
    - if (addr == NULL) {
    - purple_debug_error(
    - "bonjour",
    - "Unable to retrieve SOCKS5 host address from connection: %s",
    - error->message);
    - bonjour_bytestreams_handle_failure(
    - xfer, "Unable to retrieve SOCKS5 host address from connection");
    - g_object_unref(conn);
    - g_object_unref(proxy);
    - g_clear_error(&error);
    - return;
    - }
    -
    - contact = xf->contact;
    - hash_input = g_strdup_printf("%s%s%s", xf->sid,
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact)),
    - bonjour_get_jid(purple_contact_get_account(contact)));
    - dstaddr = g_compute_checksum_for_string(G_CHECKSUM_SHA1, hash_input, -1);
    - g_free(hash_input);
    -
    - purple_debug_info("bonjour", "Connecting to %s using SOCKS5 proxy %s:%d",
    - dstaddr, xf->proxy_host, xf->proxy_port);
    -
    - inet_addr = G_INET_SOCKET_ADDRESS(addr);
    - proxy_addr =
    - g_proxy_address_new(g_inet_socket_address_get_address(inet_addr),
    - g_inet_socket_address_get_port(inet_addr),
    - "socks5", dstaddr, 0, NULL, NULL);
    - g_object_unref(inet_addr);
    -
    - g_proxy_connect_async(proxy, G_IO_STREAM(conn), G_PROXY_ADDRESS(proxy_addr),
    - xf->cancellable, bonjour_bytestreams_connect_cb,
    - xfer);
    - g_object_unref(proxy_addr);
    - g_object_unref(conn);
    - g_object_unref(proxy);
    - g_free(dstaddr);
    -}
    -
    -static void
    -bonjour_bytestreams_connect(PurpleXfer *xfer)
    -{
    - PurpleAccount *account = NULL;
    - XepXfer *xf;
    - GError *error = NULL;
    -
    - if (xfer == NULL) {
    - return;
    - }
    -
    - purple_debug_info("bonjour", "bonjour-bytestreams-connect.");
    -
    - xf = XEP_XFER(xfer);
    - account = purple_contact_get_account(xf->contact);
    -
    - xf->client = purple_gio_socket_client_new(account, &error);
    - if (xf->client == NULL) {
    - /* Cancel the connection */
    - purple_debug_error("bonjour",
    - "Failed to connect to SOCKS5 streamhost proxy: %s",
    - error->message);
    - g_clear_error(&error);
    - xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer),
    - "404", "cancel");
    - purple_xfer_cancel_local(xfer);
    - return;
    - }
    -
    - purple_debug_info("bonjour", "Connecting to SOCKS5 proxy %s:%d",
    - xf->proxy_host, xf->proxy_port);
    -
    - g_socket_client_connect_to_host_async(
    - xf->client, xf->proxy_host, xf->proxy_port, xf->cancellable,
    - bonjour_bytestreams_socks5_connect_to_host_cb, xfer);
    -}
    -
    -static void
    -xep_xfer_init(XepXfer *xf)
    -{
    - xf->cancellable = g_cancellable_new();
    -}
    -
    -static void
    -xep_xfer_finalize(GObject *obj) {
    - XepXfer *xf = XEP_XFER(obj);
    - BonjourData *bd = (BonjourData*)xf->data;
    -
    - if(bd != NULL) {
    - bd->xfer_lists = g_slist_remove(bd->xfer_lists, PURPLE_XFER(xf));
    - purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
    - }
    - g_cancellable_cancel(xf->cancellable);
    - g_clear_object(&xf->cancellable);
    - g_clear_object(&xf->client);
    - if (xf->service) {
    - g_socket_service_stop(xf->service);
    - }
    - g_clear_object(&xf->service);
    - g_clear_object(&xf->conn);
    -
    - g_free(xf->iq_id);
    - g_free(xf->jid);
    - g_free(xf->proxy_host);
    - g_free(xf->buddy_ip);
    - g_free(xf->sid);
    -
    - g_clear_pointer(&xf->streamhost, purple_xmlnode_free_tree);
    -
    - G_OBJECT_CLASS(xep_xfer_parent_class)->finalize(obj);
    -}
    -
    -static void
    -xep_xfer_class_finalize(G_GNUC_UNUSED XepXferClass *klass) {
    -}
    -
    -static void
    -xep_xfer_class_init(XepXferClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    -
    - obj_class->finalize = xep_xfer_finalize;
    -
    - xfer_class->init = bonjour_xfer_init;
    - xfer_class->request_denied = bonjour_xfer_request_denied;
    - xfer_class->cancel_recv = bonjour_xfer_cancel_recv;
    - xfer_class->cancel_send = bonjour_xfer_cancel_send;
    - xfer_class->end = bonjour_xfer_end;
    -}
    -
    -void
    -xep_xfer_register(GTypeModule *module) {
    - xep_xfer_register_type(module);
    -}
    --- a/libpurple/protocols/bonjour/bonjour_ft.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,62 +0,0 @@
    -/*
    - * purple - Bonjour Protocol Plugin
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
    - */
    -#ifndef PURPLE_BONJOUR_BONJOUR_FT_H
    -#define PURPLE_BONJOUR_BONJOUR_FT_H
    -
    -#include <purple.h>
    -
    -G_BEGIN_DECLS
    -
    -#define XEP_TYPE_XFER (xep_xfer_get_type())
    -G_DECLARE_FINAL_TYPE(XepXfer, xep_xfer, XEP, XFER, PurpleXfer);
    -
    -typedef enum {
    - XEP_BYTESTREAMS = 1,
    - XEP_IBB = 2,
    - XEP_UNKNOWN = 4
    -} XepSiMode;
    -
    -/**
    - * Create a new PurpleXfer
    - *
    - * @param gc The PurpleConnection handle.
    - * @param who Who will we be sending it to?
    - */
    -PurpleXfer *bonjour_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    -
    -/**
    - * Send a file.
    - *
    - * @param gc The PurpleConnection handle.
    - * @param who Who are we sending it to?
    - * @param file What file? If NULL, user will choose after this call.
    - */
    -void bonjour_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file);
    -
    -void xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleContact *contact);
    -void xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleContact *contact);
    -
    -void xep_xfer_register(GTypeModule *module);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_BONJOUR_BONJOUR_FT_H */
    --- a/libpurple/protocols/bonjour/buddy.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,307 +0,0 @@
    -/*
    - * 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 Library 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 <stdlib.h>
    -
    -#include <purple.h>
    -
    -#include "buddy.h"
    -#include "bonjour.h"
    -#include "glibcompat.h"
    -
    -/**
    - * Creates a new buddy.
    - */
    -BonjourBuddy *
    -bonjour_buddy_new(const gchar *name, PurpleAccount* account)
    -{
    - BonjourBuddy *buddy = g_new0(BonjourBuddy, 1);
    -
    - buddy->account = account;
    - buddy->name = g_strdup(name);
    -
    - _mdns_init_buddy(buddy);
    -
    - return buddy;
    -}
    -
    -void
    -clear_bonjour_buddy_values(BonjourBuddy *buddy)
    -{
    - g_clear_pointer(&buddy->first, g_free);
    - g_clear_pointer(&buddy->email, g_free);
    - g_clear_pointer(&buddy->ext, g_free);
    - g_clear_pointer(&buddy->jid, g_free);
    - g_clear_pointer(&buddy->last, g_free);
    - g_clear_pointer(&buddy->msg, g_free);
    - g_clear_pointer(&buddy->nick, g_free);
    - g_clear_pointer(&buddy->node, g_free);
    - g_clear_pointer(&buddy->phsh, g_free);
    - g_clear_pointer(&buddy->status, g_free);
    - g_clear_pointer(&buddy->vc, g_free);
    - g_clear_pointer(&buddy->ver, g_free);
    - g_clear_pointer(&buddy->AIM, g_free);
    -}
    -
    -void
    -set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, guint32 len){
    - gchar **fld = NULL;
    -
    - g_return_if_fail(record_key != NULL);
    -
    - if (purple_strequal(record_key, "1st"))
    - fld = &buddy->first;
    - else if(purple_strequal(record_key, "email"))
    - fld = &buddy->email;
    - else if(purple_strequal(record_key, "ext"))
    - fld = &buddy->ext;
    - else if(purple_strequal(record_key, "jid"))
    - fld = &buddy->jid;
    - else if(purple_strequal(record_key, "last"))
    - fld = &buddy->last;
    - else if(purple_strequal(record_key, "msg"))
    - fld = &buddy->msg;
    - else if(purple_strequal(record_key, "nick"))
    - fld = &buddy->nick;
    - else if(purple_strequal(record_key, "node"))
    - fld = &buddy->node;
    - else if(purple_strequal(record_key, "phsh"))
    - fld = &buddy->phsh;
    - else if(purple_strequal(record_key, "status"))
    - fld = &buddy->status;
    - else if(purple_strequal(record_key, "vc"))
    - fld = &buddy->vc;
    - else if(purple_strequal(record_key, "ver"))
    - fld = &buddy->ver;
    - else if(purple_strequal(record_key, "AIM"))
    - fld = &buddy->AIM;
    -
    - if(fld == NULL)
    - return;
    -
    - g_free(*fld);
    - *fld = NULL;
    - *fld = g_strndup(value, len);
    -}
    -
    -/**
    - * Check if all the compulsory buddy data is present.
    - */
    -gboolean
    -bonjour_buddy_check(BonjourBuddy *buddy)
    -{
    - if (buddy->account == NULL)
    - return FALSE;
    -
    - if (buddy->name == NULL)
    - return FALSE;
    -
    - return TRUE;
    -}
    -
    -/**
    - * If the buddy does not yet exist, then create it and add it to
    - * our buddy list. In either case we set the correct status for
    - * the buddy.
    - */
    -void
    -bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy)
    -{
    - PurpleAccount *account = bonjour_buddy->account;
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    - PurplePerson *person = NULL;
    - PurplePresence *presence = NULL;
    - PurplePresencePrimitive primitive = PURPLE_PRESENCE_PRIMITIVE_AVAILABLE;
    -
    - /* Translate between the Bonjour status and the Purple status */
    - if(bonjour_buddy->status != NULL &&
    - g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0)
    - {
    - primitive = PURPLE_PRESENCE_PRIMITIVE_AWAY;
    - }
    -
    - /*
    - * TODO: Figure out the idle time by getting the "away"
    - * field from the DNS SD.
    - */
    -
    - /* Determine if we already know about this contact. */
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager, account,
    - bonjour_buddy->name);
    -
    - /* If not, create the contact and add them to the manager. */
    - if(!PURPLE_IS_CONTACT(contact)) {
    - PurpleTags *tags = NULL;
    -
    - contact = purple_contact_new(account, NULL);
    - purple_contact_manager_add(manager, contact);
    -
    - purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact),
    - bonjour_buddy->name);
    -
    - tags = purple_contact_info_get_tags(PURPLE_CONTACT_INFO(contact));
    - purple_tags_add_with_value(tags, "group", BONJOUR_GROUP_NAME);
    - }
    -
    - /* Make sure we have a person for this contact as we want them to appear in
    - * the contact list.
    - */
    - person = purple_contact_info_get_person(PURPLE_CONTACT_INFO(contact));
    - if(!PURPLE_IS_PERSON(person)) {
    - person = g_object_new(PURPLE_TYPE_PERSON, "id", bonjour_buddy->name,
    - NULL);
    - purple_contact_info_set_person(PURPLE_CONTACT_INFO(contact), person);
    - purple_person_add_contact_info(person, PURPLE_CONTACT_INFO(contact));
    -
    - /* We remove our reference as the pointer is valid and we're treating
    - * it just like if we had called _get_person above but with the new
    - * instance.
    - */
    - g_object_unref(person);
    - }
    -
    - /* Set the alias. */
    - if(!purple_strempty(bonjour_buddy->nick)) {
    - purple_contact_info_set_alias(PURPLE_CONTACT_INFO(contact),
    - bonjour_buddy->nick);
    - } else {
    - GStrvBuilder *builder = NULL;
    - GStrv parts = NULL;
    - gchar *alias = NULL;
    -
    - builder = g_strv_builder_new();
    -
    - if(!purple_strempty(bonjour_buddy->first)) {
    - g_strv_builder_add(builder, bonjour_buddy->first);
    - }
    -
    - if(!purple_strempty(bonjour_buddy->last)) {
    - g_strv_builder_add(builder, bonjour_buddy->last);
    - }
    -
    - parts = g_strv_builder_end(builder);
    -
    - alias = g_strjoinv(" ", parts);
    - g_strfreev(parts);
    -
    - if(!purple_strempty(alias)) {
    - purple_contact_info_set_alias(PURPLE_CONTACT_INFO(contact), alias);
    - } else {
    - purple_contact_info_set_alias(PURPLE_CONTACT_INFO(contact), NULL);
    - }
    - g_free(alias);
    - }
    -
    - g_object_set_data(G_OBJECT(contact), "bonjour-buddy", bonjour_buddy);
    -
    - /* Set the user's status */
    - presence = purple_contact_info_get_presence(PURPLE_CONTACT_INFO(contact));
    - purple_presence_set_primitive(presence, primitive);
    - purple_presence_set_message(presence, bonjour_buddy->msg);
    - purple_presence_set_idle(presence, FALSE, NULL);
    -
    - /* TODO: Because we don't save Bonjour buddies in blist.xml,
    - * we will always have to look up the buddy icon at login time.
    - * I think we should figure out a way to do something about this. */
    -
    -#if 0
    - /* Deal with the buddy icon */
    - old_hash = purple_buddy_icons_get_checksum_for_user(buddy);
    - new_hash = (bonjour_buddy->phsh && *(bonjour_buddy->phsh)) ? bonjour_buddy->phsh : NULL;
    - if (new_hash && !purple_strequal(old_hash, new_hash)) {
    - /* Look up the new icon data */
    - /* TODO: Make sure the hash assigned to the retrieved buddy icon is the same
    - * as what we looked up. */
    - bonjour_dns_sd_retrieve_buddy_icon(bonjour_buddy);
    - } else if (!new_hash)
    - purple_buddy_icons_set_for_user(account, name, NULL, 0, NULL);
    -#endif
    -
    - g_clear_object(&contact);
    -}
    -
    -/**
    - * The buddy has signed off Bonjour.
    - * If the buddy is being saved, mark as offline, otherwise delete
    - */
    -void bonjour_buddy_signed_off(PurpleContact *contact) {
    - BonjourBuddy *bb = NULL;
    - PurplePresence *presence = NULL;
    -
    - presence = purple_contact_info_get_presence(PURPLE_CONTACT_INFO(contact));
    - purple_presence_set_primitive(presence, PURPLE_PRESENCE_PRIMITIVE_OFFLINE);
    -
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - if(bb != NULL) {
    - bonjour_buddy_delete(bb);
    - }
    - g_object_set_data(G_OBJECT(contact), "bonjour-buddy", NULL);
    -}
    -
    -/**
    - * We got the buddy icon data; deal with it
    - */
    -void bonjour_buddy_got_buddy_icon(BonjourBuddy *buddy, gconstpointer data, gsize len) {
    - /* Recalculate the hash instead of using the current phsh to make sure it is accurate for the icon. */
    - gchar *hash;
    -
    - if (data == NULL || len == 0)
    - return;
    -
    - hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, len);
    -
    - purple_debug_info("bonjour", "Got buddy icon for %s icon hash='%s' phsh='%s'.\n", buddy->name,
    - hash, buddy->phsh ? buddy->phsh : "(null)");
    -
    - purple_buddy_icons_set_for_user(buddy->account, buddy->name,
    - g_memdup2(data, len), len, hash);
    -
    - g_free(hash);
    -}
    -
    -/**
    - * Deletes a buddy from memory.
    - */
    -void
    -bonjour_buddy_delete(BonjourBuddy *buddy)
    -{
    - g_free(buddy->name);
    - g_slist_free_full(buddy->ips, g_free);
    - g_free(buddy->first);
    - g_free(buddy->phsh);
    - g_free(buddy->status);
    - g_free(buddy->email);
    - g_free(buddy->last);
    - g_free(buddy->jid);
    - g_free(buddy->AIM);
    - g_free(buddy->vc);
    - g_free(buddy->msg);
    - g_free(buddy->ext);
    - g_free(buddy->nick);
    - g_free(buddy->node);
    - g_free(buddy->ver);
    -
    - bonjour_xmpp_close_conversation(buddy->conversation);
    - buddy->conversation = NULL;
    -
    - /* Clean up any mdns implementation data */
    - _mdns_delete_buddy(buddy);
    -
    - g_free(buddy);
    -}
    --- a/libpurple/protocols/bonjour/buddy.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,114 +0,0 @@
    -/*
    - * 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 Library 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_BONJOUR_BUDDY_H
    -#define PURPLE_BONJOUR_BUDDY_H
    -
    -#include <glib.h>
    -
    -#include <purple.h>
    -
    -#include "xmpp.h"
    -
    -typedef struct
    -{
    - PurpleAccount *account;
    -
    - gchar *name;
    - GSList *ips;
    - gint port_p2pj;
    -
    - gchar *first;
    - gchar *phsh;
    - gchar *status;
    - gchar *email;
    - gchar *last;
    - gchar *jid;
    - gchar *AIM;
    - gchar *vc;
    - gchar *msg;
    - gchar *ext;
    - gchar *nick;
    - gchar *node;
    - gchar *ver;
    -
    - BonjourXMPPConversation *conversation;
    -
    - gpointer mdns_impl_data;
    -} BonjourBuddy;
    -
    -static const char *const buddy_TXT_records[] = {
    - "1st",
    - "email",
    - "ext",
    - "jid",
    - "last",
    - "msg",
    - "nick",
    - "node",
    - "phsh",
    -/* "port.p2pj", Deprecated - MUST ignore */
    - "status",
    -/* "txtvers", Deprecated - hardcoded to 1 */
    - "vc",
    - "ver",
    - "AIM", /* non standard */
    - NULL
    -};
    -
    -/**
    - * Creates a new buddy.
    - */
    -BonjourBuddy *bonjour_buddy_new(const gchar *name, PurpleAccount *account);
    -
    -/**
    - * Clear any existing values from the buddy.
    - * This is called before updating so that we can notice removals
    - */
    -void clear_bonjour_buddy_values(BonjourBuddy *buddy);
    -
    -/**
    - * Sets a value in the BonjourBuddy struct, destroying the old value
    - */
    -void set_bonjour_buddy_value(BonjourBuddy *buddy, const char *record_key, const char *value, guint32 len);
    -
    -/**
    - * Check if all the compulsory buddy data is present.
    - */
    -gboolean bonjour_buddy_check(BonjourBuddy *buddy);
    -
    -/**
    - * If the buddy doesn't previously exists, it is created.
    - */
    -void bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy);
    -
    -/**
    - * The buddy has signed off Bonjour.
    - * If the buddy is being saved, mark as offline, otherwise delete
    - */
    -void bonjour_buddy_signed_off(PurpleContact *contact);
    -
    -/**
    - * We got the buddy icon data; deal with it
    - */
    -void bonjour_buddy_got_buddy_icon(BonjourBuddy *buddy, gconstpointer data, gsize len);
    -
    -/**
    - * Deletes a buddy from memory.
    - */
    -void bonjour_buddy_delete(BonjourBuddy *buddy);
    -
    -#endif /* PURPLE_BONJOUR_BUDDY_H */
    --- a/libpurple/protocols/bonjour/dns_sd_proxy.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,193 +0,0 @@
    -/*
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU Library 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 <gmodule.h>
    -
    -#include <purple.h>
    -
    -#include "dns_sd_proxy.h"
    -
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceAddRecord)(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
    - uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceBrowse)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context);
    -static int (DNSSD_API* _DNSServiceConstructFullName)(char *fullName, const char *service, const char *regtype, const char *domain);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceGetAddrInfo)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - DNSServiceProtocol protocol, const char *hostname, DNSServiceGetAddrInfoReply callBack, void *context);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceProcessResult)(DNSServiceRef sdRef);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceQueryRecord)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context);
    -static void (DNSSD_API* _DNSServiceRefDeallocate)(DNSServiceRef sdRef);
    -static int (DNSSD_API* _DNSServiceRefSockFD)(DNSServiceRef sdRef);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceRegister)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
    - const void *txtRecord, DNSServiceRegisterReply callBack, void *context);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceResolve)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
    - const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceRemoveRecord)(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags);
    -static DNSServiceErrorType (DNSSD_API* _DNSServiceUpdateRecord)(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
    - uint16_t rdlen, const void *rdata, uint32_t ttl);
    -static void (DNSSD_API* _TXTRecordCreate)(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer);
    -static void (DNSSD_API* _TXTRecordDeallocate)(TXTRecordRef *txtRecord);
    -static const void * (DNSSD_API* _TXTRecordGetBytesPtr)(const TXTRecordRef *txtRecord);
    -static int16_t (DNSSD_API* _TXTRecordGetLength)(const TXTRecordRef *txtRecord);
    -static const void * (DNSSD_API* _TXTRecordGetValuePtr)(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen);
    -static DNSServiceErrorType (DNSSD_API* _TXTRecordSetValue)(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value);
    -
    -
    -gboolean dns_sd_available(void) {
    - static gboolean initialized = FALSE;
    - static gboolean loaded = FALSE;
    -
    - if (!initialized) {
    - GModule *dnssd = NULL;
    - initialized = TRUE;
    - if((dnssd = g_module_open("dnssd.dll", 0)) != NULL &&
    - g_module_symbol(dnssd, "DNSServiceAddRecord", (gpointer *)&_DNSServiceAddRecord) &&
    - g_module_symbol(dnssd, "DNSServiceBrowse", (gpointer *)&_DNSServiceBrowse) &&
    - g_module_symbol(dnssd, "DNSServiceConstructFullName", (gpointer *)&_DNSServiceConstructFullName) &&
    - g_module_symbol(dnssd, "DNSServiceGetAddrInfo", (gpointer *)&_DNSServiceGetAddrInfo) &&
    - g_module_symbol(dnssd, "DNSServiceProcessResult", (gpointer *)&_DNSServiceProcessResult) &&
    - g_module_symbol(dnssd, "DNSServiceQueryRecord", (gpointer *)&_DNSServiceQueryRecord) &&
    - g_module_symbol(dnssd, "DNSServiceRefDeallocate", (gpointer *)&_DNSServiceRefDeallocate) &&
    - g_module_symbol(dnssd, "DNSServiceRefSockFD", (gpointer *)&_DNSServiceRefSockFD) &&
    - g_module_symbol(dnssd, "DNSServiceRegister", (gpointer *)&_DNSServiceRegister) &&
    - g_module_symbol(dnssd, "DNSServiceResolve", (gpointer *)&_DNSServiceResolve) &&
    - g_module_symbol(dnssd, "DNSServiceRemoveRecord", (gpointer *)&_DNSServiceRemoveRecord) &&
    - g_module_symbol(dnssd, "DNSServiceUpdateRecord", (gpointer *)&_DNSServiceUpdateRecord) &&
    - g_module_symbol(dnssd, "TXTRecordCreate", (gpointer *)&_TXTRecordCreate) &&
    - g_module_symbol(dnssd, "TXTRecordDeallocate", (gpointer *)&_TXTRecordDeallocate) &&
    - g_module_symbol(dnssd, "TXTRecordGetBytesPtr", (gpointer *)&_TXTRecordGetBytesPtr) &&
    - g_module_symbol(dnssd, "TXTRecordGetLength", (gpointer *)&_TXTRecordGetLength) &&
    - g_module_symbol(dnssd, "TXTRecordGetValuePtr", (gpointer *)&_TXTRecordGetValuePtr) &&
    - g_module_symbol(dnssd, "TXTRecordSetValue", (gpointer *)&_TXTRecordSetValue))
    - {
    - /* TODO: The dnssd module leaks, but there's not a good
    - * place to put the cleanup yet, as there's quite a bit
    - * of abstraction between mDNS API here to the actual
    - * plugin unload. */
    - loaded = TRUE;
    - } else {
    - g_clear_pointer(&dnssd, g_module_close);
    - }
    - }
    - return loaded;
    -}
    -
    -
    -DNSServiceErrorType _wpurple_DNSServiceAddRecord(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
    - uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl) {
    - g_return_val_if_fail(_DNSServiceAddRecord != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceAddRecord)(sdRef, RecordRef, flags, rrtype, rdlen, rdata, ttl);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceBrowse(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context) {
    - g_return_val_if_fail(_DNSServiceBrowse != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceBrowse)(sdRef, flags, interfaceIndex, regtype, domain, callBack, context);
    -}
    -
    -int _wpurple_DNSServiceConstructFullName(char *fullName, const char *service, const char *regtype, const char *domain) {
    - g_return_val_if_fail(_DNSServiceConstructFullName != NULL, 0);
    - return (_DNSServiceConstructFullName)(fullName, service, regtype, domain);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceGetAddrInfo(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - DNSServiceProtocol protocol, const char *hostname, DNSServiceGetAddrInfoReply callBack, void *context) {
    - g_return_val_if_fail(_DNSServiceGetAddrInfo != NULL, 0);
    - return (_DNSServiceGetAddrInfo)(sdRef, flags, interfaceIndex, protocol, hostname, callBack, context);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceProcessResult(DNSServiceRef sdRef) {
    - g_return_val_if_fail(_DNSServiceProcessResult != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceProcessResult)(sdRef);
    -}
    -
    -
    -DNSServiceErrorType _wpurple_DNSServiceQueryRecord(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context) {
    - g_return_val_if_fail(_DNSServiceQueryRecord != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceQueryRecord)(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context);
    -}
    -
    -void _wpurple_DNSServiceRefDeallocate(DNSServiceRef sdRef) {
    - g_return_if_fail(_DNSServiceRefDeallocate != NULL);
    - (_DNSServiceRefDeallocate)(sdRef);
    -}
    -
    -int _wpurple_DNSServiceRefSockFD(DNSServiceRef sdRef) {
    - g_return_val_if_fail(_DNSServiceRefSockFD != NULL, -1);
    - return (_DNSServiceRefSockFD)(sdRef);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceRegister(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
    - const void *txtRecord, DNSServiceRegisterReply callBack, void *context) {
    - g_return_val_if_fail(_DNSServiceRegister != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceRegister)(sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceResolve(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
    - const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context) {
    - g_return_val_if_fail(_DNSServiceResolve != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceResolve)(sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceRemoveRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags) {
    - g_return_val_if_fail(_DNSServiceRemoveRecord != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceRemoveRecord)(sdRef, RecordRef, flags);
    -}
    -
    -DNSServiceErrorType _wpurple_DNSServiceUpdateRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
    - uint16_t rdlen, const void *rdata, uint32_t ttl) {
    - g_return_val_if_fail(_DNSServiceUpdateRecord != NULL, kDNSServiceErr_Unknown);
    - return (_DNSServiceUpdateRecord)(sdRef, RecordRef, flags, rdlen, rdata, ttl);
    -}
    -
    -void _wpurple_TXTRecordCreate(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer) {
    - g_return_if_fail(_TXTRecordCreate != NULL);
    - (_TXTRecordCreate)(txtRecord, bufferLen, buffer);
    -}
    -
    -void _wpurple_TXTRecordDeallocate(TXTRecordRef *txtRecord) {
    - g_return_if_fail(_TXTRecordDeallocate != NULL);
    - (_TXTRecordDeallocate)(txtRecord);
    -}
    -
    -const void * _wpurple_TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) {
    - g_return_val_if_fail(_TXTRecordGetBytesPtr != NULL, NULL);
    - return (_TXTRecordGetBytesPtr)(txtRecord);
    -}
    -
    -uint16_t _wpurple_TXTRecordGetLength(const TXTRecordRef *txtRecord) {
    - g_return_val_if_fail(_TXTRecordGetLength != NULL, 0);
    - return (_TXTRecordGetLength)(txtRecord);
    -}
    -
    -const void * _wpurple_TXTRecordGetValuePtr(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen) {
    - g_return_val_if_fail(_TXTRecordGetValuePtr != NULL, NULL);
    - return (_TXTRecordGetValuePtr)(txtLen, txtRecord, key, valueLen);
    -}
    -
    -DNSServiceErrorType _wpurple_TXTRecordSetValue(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value) {
    - g_return_val_if_fail(_TXTRecordSetValue != NULL, kDNSServiceErr_Unknown);
    - return (_TXTRecordSetValue)(txtRecord, key, valueSize, value);
    -}
    -
    --- a/libpurple/protocols/bonjour/dns_sd_proxy.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,194 +0,0 @@
    -/*
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU Library 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_BONJOUR_DNS_SD_PROXY_H
    -#define PURPLE_BONJOUR_DNS_SD_PROXY_H
    -
    -#include <stdint.h>
    -
    -/* The following is a subset of Apple's dns_sd.h file
    - * http://www.opensource.apple.com/source/mDNSResponder/mDNSResponder-333.10/mDNSShared/dns_sd.h
    - *
    - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
    - *
    - * Redistribution and use in source and binary forms, with or without
    - * modification, are permitted provided that the following conditions are met:
    - *
    - * 1. Redistributions of source code must retain the above copyright notice,
    - * this list of conditions and the following disclaimer.
    - * 2. Redistributions in binary form must reproduce the above copyright notice,
    - * this list of conditions and the following disclaimer in the documentation
    - * and/or other materials provided with the distribution.
    - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
    - * contributors may be used to endorse or promote products derived from this
    - * software without specific prior written permission.
    - *
    - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
    - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
    - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    - */
    -
    -# if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
    -# define DNSSD_API __stdcall
    -# else
    -# define DNSSD_API
    -# endif
    -
    -# define kDNSServiceInterfaceIndexAny 0
    -# define kDNSServiceMaxDomainName 1009
    -
    -typedef gint32 DNSServiceErrorType;
    -typedef guint32 DNSServiceFlags;
    -typedef struct _DNSServiceRef_t *DNSServiceRef;
    -typedef struct _DNSRecordRef_t *DNSRecordRef;
    -typedef guint32 DNSServiceProtocol;
    -
    -typedef union _TXTRecordRef_t {
    - gchar PrivateData[16];
    - gchar *ForceNaturalAlignment;
    -} TXTRecordRef;
    -
    -typedef void (DNSSD_API *DNSServiceBrowseReply)();
    -typedef void (DNSSD_API *DNSServiceGetAddrInfoReply)();
    -typedef void (DNSSD_API *DNSServiceQueryRecordReply)(
    - DNSServiceRef sdRef, DNSServiceFlags flags, guint32 interfaceIndex,
    - DNSServiceErrorType errorCode, const gchar *fullname,
    - guint16 rrtype, guint16 rrclass, guint16 rdlen, const void *rdata,
    - guint32 ttl, void *context);
    -typedef void (DNSSD_API *DNSServiceRegisterReply)();
    -typedef void (DNSSD_API *DNSServiceResolveReply)(
    - DNSServiceRef sdRef, DNSServiceFlags flags, guint32 interfaceIndex,
    - DNSServiceErrorType errorCode, const gchar *fullname,
    - const gchar *hosttarget, guint16 port, guint16 txtLen,
    - const guchar *txtRecord, void *context);
    -
    -enum {
    - kDNSServiceErr_NoError = 0,
    - kDNSServiceErr_Unknown = -65537,
    -};
    -
    -enum {
    - kDNSServiceFlagsAdd = 0x2,
    - kDNSServiceFlagsLongLivedQuery = 0x100,
    -};
    -
    -enum {
    - kDNSServiceType_NULL = 10,
    - kDNSServiceType_TXT = 16,
    -};
    -
    -enum {
    - kDNSServiceClass_IN = 1,
    -};
    -
    -enum {
    - kDNSServiceProtocol_IPv4 = 0x01,
    -};
    -
    -
    -gboolean dns_sd_available(void);
    -
    -DNSServiceErrorType _wpurple_DNSServiceAddRecord(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
    - uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl);
    -#define DNSServiceAddRecord(sdRef, RecordRef, flags, rrtype, rdlen, rdata, ttl) \
    - _wpurple_DNSServiceAddRecord(sdRef, RecordRef, flags, rrtype, rdlen, rdata, ttl)
    -
    -DNSServiceErrorType _wpurple_DNSServiceBrowse(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context);
    -#define DNSServiceBrowse(sdRef, flags, interfaceIndex, regtype, domain, callBack, context) \
    - _wpurple_DNSServiceBrowse(sdRef, flags, interfaceIndex, regtype, domain, callBack, context)
    -
    -int _wpurple_DNSServiceConstructFullName(char *fullName, const char *service, const char *regtype, const char *domain);
    -#define DNSServiceConstructFullName(fullName, service, regtype, domain) \
    - _wpurple_DNSServiceConstructFullName(fullName, service, regtype, domain)
    -
    -DNSServiceErrorType _wpurple_DNSServiceGetAddrInfo(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - DNSServiceProtocol protocol, const char *hostname, DNSServiceGetAddrInfoReply callBack, void *context);
    -#define DNSServiceGetAddrInfo(sdRef, flags, interfaceIndex, protocol, hostname, callBack, context) \
    - _wpurple_DNSServiceGetAddrInfo(sdRef, flags, interfaceIndex, protocol, hostname, callBack, context)
    -
    -DNSServiceErrorType _wpurple_DNSServiceProcessResult(DNSServiceRef sdRef);
    -#define DNSServiceProcessResult(sdRef) \
    - _wpurple_DNSServiceProcessResult(sdRef);
    -
    -DNSServiceErrorType _wpurple_DNSServiceQueryRecord(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context);
    -#define DNSServiceQueryRecord(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context) \
    - _wpurple_DNSServiceQueryRecord(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context)
    -
    -void _wpurple_DNSServiceRefDeallocate(DNSServiceRef sdRef);
    -#define DNSServiceRefDeallocate(sdRef) \
    - _wpurple_DNSServiceRefDeallocate(sdRef)
    -
    -int _wpurple_DNSServiceRefSockFD(DNSServiceRef sdRef);
    -#define DNSServiceRefSockFD(sdRef) \
    - _wpurple_DNSServiceRefSockFD(sdRef)
    -
    -DNSServiceErrorType _wpurple_DNSServiceRegister(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    - const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
    - const void *txtRecord, DNSServiceRegisterReply callBack, void *context);
    -#define DNSServiceRegister(sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context) \
    - _wpurple_DNSServiceRegister(sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context)
    -
    -DNSServiceErrorType _wpurple_DNSServiceResolve(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
    - const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context);
    -#define DNSServiceResolve(sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context) \
    - _wpurple_DNSServiceResolve(sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context)
    -
    -DNSServiceErrorType _wpurple_DNSServiceRemoveRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags);
    -#define DNSServiceRemoveRecord(sdRef, RecordRef, flags) \
    - _wpurple_DNSServiceRemoveRecord(sdRef, RecordRef, flags)
    -
    -DNSServiceErrorType _wpurple_DNSServiceUpdateRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
    - uint16_t rdlen, const void *rdata, uint32_t ttl);
    -#define DNSServiceUpdateRecord(sdRef, RecordRef, flags, rdlen, rdata, ttl) \
    - _wpurple_DNSServiceUpdateRecord(sdRef, RecordRef, flags, rdlen, rdata, ttl)
    -
    -void _wpurple_TXTRecordCreate(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer);
    -#define TXTRecordCreate(txtRecord, bufferLen, buffer) \
    - _wpurple_TXTRecordCreate(txtRecord, bufferLen, buffer)
    -
    -void _wpurple_TXTRecordDeallocate(TXTRecordRef *txtRecord);
    -#define TXTRecordDeallocate(txtRecord) \
    - _wpurple_TXTRecordDeallocate(txtRecord)
    -
    -const void * _wpurple_TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord);
    -#define TXTRecordGetBytesPtr(txtRecord) \
    - _wpurple_TXTRecordGetBytesPtr(txtRecord)
    -
    -uint16_t _wpurple_TXTRecordGetLength(const TXTRecordRef *txtRecord);
    -#define TXTRecordGetLength(txtRecord) \
    - _wpurple_TXTRecordGetLength(txtRecord)
    -
    -const void * _wpurple_TXTRecordGetValuePtr(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen);
    -#define TXTRecordGetValuePtr(txtLen, txtRecord, key, valueLen) \
    - _wpurple_TXTRecordGetValuePtr(txtLen, txtRecord, key, valueLen)
    -
    -DNSServiceErrorType _wpurple_TXTRecordSetValue(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value);
    -#define TXTRecordSetValue(txtRecord, key, valueSize, value) \
    - _wpurple_TXTRecordSetValue(txtRecord, key, valueSize, value)
    -
    -#endif /* PURPLE_BONJOUR_DNS_SD_PROXY_H */
    --- a/libpurple/protocols/bonjour/mdns_avahi.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,697 +0,0 @@
    -/*
    - * 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 Library General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    - */
    -
    -#include <purple.h>
    -
    -#include "buddy.h"
    -#include "bonjour.h"
    -
    -#include <avahi-client/client.h>
    -#include <avahi-client/lookup.h>
    -#include <avahi-client/publish.h>
    -
    -#include <avahi-common/address.h>
    -#include <avahi-common/malloc.h>
    -#include <avahi-common/error.h>
    -#include <avahi-common/strlst.h>
    -
    -#include <avahi-glib/glib-malloc.h>
    -#include <avahi-glib/glib-watch.h>
    -
    -/* Avahi only defines the types that it actually uses (which at this time doesn't include NULL) */
    -#ifndef AVAHI_DNS_TYPE_NULL
    -#define AVAHI_DNS_TYPE_NULL 0x0A
    -#endif
    -
    -/* data used by avahi bonjour implementation */
    -typedef struct {
    - AvahiClient *client;
    - AvahiGLibPoll *glib_poll;
    - AvahiServiceBrowser *sb;
    - AvahiEntryGroup *group;
    - AvahiEntryGroup *buddy_icon_group;
    -} AvahiSessionImplData;
    -
    -typedef struct {
    - AvahiServiceResolver *resolver;
    - AvahiIfIndex interface;
    - AvahiProtocol protocol;
    - gchar *name;
    - gchar *type;
    - gchar *domain;
    - /* This is a reference to the entry in BonjourBuddy->ips */
    - const char *ip;
    -} AvahiSvcResolverData;
    -
    -typedef struct {
    - GSList *resolvers;
    - AvahiRecordBrowser *buddy_icon_rec_browser;
    -} AvahiBuddyImplData;
    -
    -static gint
    -_find_resolver_data(gconstpointer a, gconstpointer b) {
    - const AvahiSvcResolverData *rd_a = a;
    - const AvahiSvcResolverData *rd_b = b;
    - gint ret = 1;
    -
    - if(rd_a->interface == rd_b->interface
    - && rd_a->protocol == rd_b->protocol
    - && purple_strequal(rd_a->name, rd_b->name)
    - && purple_strequal(rd_a->type, rd_b->type)
    - && purple_strequal(rd_a->domain, rd_b->domain)) {
    - ret = 0;
    - }
    -
    - return ret;
    -}
    -
    -static gint
    -_find_resolver_data_by_resolver(gconstpointer a, gconstpointer b) {
    - const AvahiSvcResolverData *rd_a = a;
    - const AvahiServiceResolver *resolver = b;
    - gint ret = 1;
    -
    - if(rd_a->resolver == resolver)
    - ret = 0;
    -
    - return ret;
    -}
    -
    -static void
    -_cleanup_resolver_data(AvahiSvcResolverData *rd) {
    - if (rd->resolver)
    - avahi_service_resolver_free(rd->resolver);
    - g_free(rd->name);
    - g_free(rd->type);
    - g_free(rd->domain);
    - g_free(rd);
    -}
    -
    -
    -static void
    -_resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
    - AvahiProtocol protocol, AvahiResolverEvent event,
    - const char *name, const char *type, const char *domain,
    - G_GNUC_UNUSED const char *host_name, const AvahiAddress *a,
    - uint16_t port, AvahiStringList *txt,
    - G_GNUC_UNUSED AvahiLookupResultFlags flags, void *userdata)
    -{
    - BonjourBuddy *bb = NULL;
    - PurpleAccount *account = userdata;
    - PurpleContactManager *manager = NULL;
    - PurpleContact *contact = NULL;
    - AvahiStringList *l;
    - size_t size;
    - char *key, *value;
    - char ip[AVAHI_ADDRESS_STR_MAX];
    - AvahiBuddyImplData *b_impl;
    - AvahiSvcResolverData *rd;
    - GSList *res;
    -
    - g_return_if_fail(r != NULL);
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager, account,
    - name);
    - if(PURPLE_IS_CONTACT(contact)) {
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - }
    -
    - switch (event) {
    - case AVAHI_RESOLVER_FAILURE:
    - purple_debug_error("bonjour", "_resolve_callback - Failure: %s\n",
    - avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
    -
    - avahi_service_resolver_free(r);
    - if (bb != NULL) {
    - b_impl = bb->mdns_impl_data;
    - res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
    - if (res != NULL) {
    - rd = res->data;
    - b_impl->resolvers = g_slist_delete_link(b_impl->resolvers, res);
    -
    - /* We've already freed the resolver */
    - rd->resolver = NULL;
    - _cleanup_resolver_data(rd);
    -
    - /* If this was the last resolver, remove the buddy */
    - if (b_impl->resolvers == NULL) {
    - bonjour_buddy_signed_off(contact);
    - }
    - }
    - }
    - break;
    - case AVAHI_RESOLVER_FOUND:
    -
    - purple_debug_info("bonjour", "_resolve_callback - name:%s account:%p bb:%p\n",
    - name, account, bb);
    -
    - /* create a buddy record */
    - if (bb == NULL)
    - bb = bonjour_buddy_new(name, account);
    - b_impl = bb->mdns_impl_data;
    -
    - /* If we're reusing an existing buddy, it may be a new resolver or an existing one. */
    - res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
    - if (res != NULL)
    - rd = res->data;
    - else {
    - rd = g_new0(AvahiSvcResolverData, 1);
    - rd->resolver = r;
    - rd->interface = interface;
    - rd->protocol = protocol;
    - rd->name = g_strdup(name);
    - rd->type = g_strdup(type);
    - rd->domain = g_strdup(domain);
    -
    - b_impl->resolvers = g_slist_prepend(b_impl->resolvers, rd);
    - }
    -
    -
    - /* Get the ip as a string */
    - ip[0] = '\0';
    - avahi_address_snprint(ip, AVAHI_ADDRESS_STR_MAX, a);
    -
    - if (protocol == AVAHI_PROTO_INET6)
    - append_iface_if_linklocal(ip, interface);
    -
    - purple_debug_info("bonjour", "_resolve_callback - name:%s ip:%s prev_ip:%s\n",
    - name, ip, rd->ip);
    -
    - if (rd->ip == NULL || !purple_strequal(rd->ip, ip)) {
    - /* We store duplicates in bb->ips, so we always remove the one */
    - if (rd->ip != NULL) {
    - bb->ips = g_slist_remove(bb->ips, rd->ip);
    - g_free((gchar *) rd->ip);
    - }
    - rd->ip = g_strdup(ip);
    - /* IPv6 goes at the front of the list and IPv4 at the end so that we "prefer" IPv6, if present */
    - if (protocol == AVAHI_PROTO_INET6) {
    - bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip);
    - } else {
    - bb->ips = g_slist_append(bb->ips, (gchar *) rd->ip);
    - }
    - }
    -
    - bb->port_p2pj = port;
    -
    - /* Obtain the parameters from the text_record */
    - clear_bonjour_buddy_values(bb);
    - for(l = txt; l != NULL; l = l->next) {
    - if (avahi_string_list_get_pair(l, &key, &value, &size) < 0)
    - continue;
    - set_bonjour_buddy_value(bb, key, value, size);
    - /* TODO: Since we're using the glib allocator, I think we
    - * can use the values instead of re-copying them */
    - avahi_free(key);
    - avahi_free(value);
    - }
    -
    - if (!bonjour_buddy_check(bb)) {
    - b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
    - _cleanup_resolver_data(rd);
    - /* If this was the last resolver, remove the buddy */
    - if (b_impl->resolvers == NULL) {
    - if (PURPLE_IS_CONTACT(contact)) {
    - bonjour_buddy_signed_off(contact);
    - } else {
    - bonjour_buddy_delete(bb);
    - }
    - }
    - } else {
    - /* Add or update the buddy in our buddy list */
    - bonjour_buddy_add_to_purple(bb);
    - }
    -
    - break;
    - default:
    - purple_debug_info("bonjour", "Unrecognized Service Resolver event: %d.\n", event);
    - }
    -
    - g_clear_object(&contact);
    -}
    -
    -static void
    -_browser_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
    - AvahiProtocol protocol, AvahiBrowserEvent event,
    - const char *name, const char *type, const char *domain,
    - G_GNUC_UNUSED AvahiLookupResultFlags flags, void *userdata)
    -{
    - PurpleAccount *account = userdata;
    -
    - switch (event) {
    - case AVAHI_BROWSER_FAILURE:
    - purple_debug_error("bonjour", "_browser_callback - Failure: %s\n",
    - avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
    - /* TODO: This is an error that should be handled. */
    - break;
    - case AVAHI_BROWSER_NEW:
    - /* A new peer has joined the network and uses iChat bonjour */
    - purple_debug_info("bonjour", "_browser_callback - new service\n");
    - /* Make sure it isn't us */
    - if (purple_utf8_strcasecmp(name, bonjour_get_jid(account)) != 0) {
    - if (!avahi_service_resolver_new(avahi_service_browser_get_client(b),
    - interface, protocol, name, type, domain, protocol,
    - 0, _resolver_callback, account)) {
    - purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n",
    - avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
    - }
    - }
    - break;
    - case AVAHI_BROWSER_REMOVE: {
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    -
    - purple_debug_info("bonjour", "_browser_callback - Remove service\n");
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager,
    - account, name);
    - if (PURPLE_IS_CONTACT(contact)) {
    - BonjourBuddy *bb = NULL;
    - AvahiBuddyImplData *b_impl;
    - GSList *l;
    - AvahiSvcResolverData *rd_search;
    -
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - g_return_if_fail(bb != NULL);
    -
    - b_impl = bb->mdns_impl_data;
    -
    - /* There may be multiple presences, we should only get rid of this one */
    -
    - rd_search = g_new0(AvahiSvcResolverData, 1);
    - rd_search->interface = interface;
    - rd_search->protocol = protocol;
    - rd_search->name = (gchar *) name;
    - rd_search->type = (gchar *) type;
    - rd_search->domain = (gchar *) domain;
    -
    - l = g_slist_find_custom(b_impl->resolvers, rd_search, _find_resolver_data);
    -
    - g_free(rd_search);
    -
    - if (l != NULL) {
    - AvahiSvcResolverData *rd = l->data;
    - b_impl->resolvers =
    - g_slist_delete_link(b_impl->resolvers, l);
    - /* This IP is no longer available */
    - if (rd->ip != NULL) {
    - bb->ips = g_slist_remove(bb->ips, rd->ip);
    - g_free((gchar *) rd->ip);
    - }
    - _cleanup_resolver_data(rd);
    -
    - /* If this was the last resolver, remove the buddy */
    - if (b_impl->resolvers == NULL) {
    - bonjour_buddy_signed_off(contact);
    - }
    - }
    - }
    -
    - g_clear_object(&contact);
    -
    - break;
    - }
    - case AVAHI_BROWSER_ALL_FOR_NOW:
    - case AVAHI_BROWSER_CACHE_EXHAUSTED:
    - break;
    - default:
    - purple_debug_info("bonjour", "Unrecognized Service browser event: %d.\n", event);
    - }
    -}
    -
    -static void
    -_buddy_icon_group_cb(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
    - BonjourDnsSd *data = userdata;
    - AvahiSessionImplData *idata = data->mdns_impl_data;
    -
    - g_return_if_fail(g == idata->buddy_icon_group || idata->buddy_icon_group == NULL);
    -
    - switch(state) {
    - case AVAHI_ENTRY_GROUP_ESTABLISHED:
    - purple_debug_info("bonjour", "Successfully registered buddy icon data.\n");
    - break;
    - case AVAHI_ENTRY_GROUP_COLLISION:
    - purple_debug_error("bonjour", "Collision registering buddy icon data.\n");
    - break;
    - case AVAHI_ENTRY_GROUP_FAILURE:
    - purple_debug_error("bonjour", "Error registering buddy icon data: %s.\n",
    - avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
    - break;
    - case AVAHI_ENTRY_GROUP_UNCOMMITED:
    - case AVAHI_ENTRY_GROUP_REGISTERING:
    - break;
    - }
    -
    -}
    -
    -static void
    -_entry_group_cb(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
    - AvahiSessionImplData *idata = userdata;
    -
    - g_return_if_fail(g == idata->group || idata->group == NULL);
    -
    - switch(state) {
    - case AVAHI_ENTRY_GROUP_ESTABLISHED:
    - purple_debug_info("bonjour", "Successfully registered service.\n");
    - break;
    - case AVAHI_ENTRY_GROUP_COLLISION:
    - purple_debug_error("bonjour", "Collision registering entry group.\n");
    - /* TODO: Handle error - this should log out the account. (Possibly with "wants to die")*/
    - break;
    - case AVAHI_ENTRY_GROUP_FAILURE:
    - purple_debug_error("bonjour", "Error registering entry group: %s\n.",
    - avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
    - /* TODO: Handle error - this should log out the account.*/
    - break;
    - case AVAHI_ENTRY_GROUP_UNCOMMITED:
    - case AVAHI_ENTRY_GROUP_REGISTERING:
    - break;
    - }
    -
    -}
    -
    -static void
    -_buddy_icon_record_cb(AvahiRecordBrowser *b,
    - G_GNUC_UNUSED AvahiIfIndex interface,
    - G_GNUC_UNUSED AvahiProtocol protocol,
    - AvahiBrowserEvent event, G_GNUC_UNUSED const char *name,
    - G_GNUC_UNUSED uint16_t clazz,
    - G_GNUC_UNUSED uint16_t type,
    - const void *rdata, size_t size,
    - G_GNUC_UNUSED AvahiLookupResultFlags flags,
    - void *userdata)
    -{
    - BonjourBuddy *buddy = userdata;
    - AvahiBuddyImplData *idata = buddy->mdns_impl_data;
    -
    - switch (event) {
    - case AVAHI_BROWSER_CACHE_EXHAUSTED:
    - case AVAHI_BROWSER_ALL_FOR_NOW:
    - /* Ignore these "meta" informational events */
    - return;
    - case AVAHI_BROWSER_NEW:
    - bonjour_buddy_got_buddy_icon(buddy, rdata, size);
    - break;
    - case AVAHI_BROWSER_REMOVE:
    - case AVAHI_BROWSER_FAILURE:
    - purple_debug_error("bonjour", "Error retrieving buddy icon record: %s\n",
    - avahi_strerror(avahi_client_errno(avahi_record_browser_get_client(b))));
    - break;
    - }
    -
    - /* Stop listening */
    - avahi_record_browser_free(b);
    - if (idata->buddy_icon_rec_browser == b) {
    - idata->buddy_icon_rec_browser = NULL;
    - }
    -}
    -
    -/****************************
    - * mdns_interface functions *
    - ****************************/
    -
    -static gboolean
    -avahi_mdns_init_session(BonjourDnsSd *data)
    -{
    - AvahiSessionImplData *idata = g_new0(AvahiSessionImplData, 1);
    - const AvahiPoll *poll_api;
    - int error;
    -
    - /* Tell avahi to use g_malloc and g_free */
    - avahi_set_allocator (avahi_glib_allocator ());
    -
    - /* This currently depends on the glib mainloop,
    - * we should make it use the libpurple abstraction */
    -
    - idata->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
    -
    - poll_api = avahi_glib_poll_get(idata->glib_poll);
    -
    - idata->client = avahi_client_new(poll_api, 0, NULL, data, &error);
    -
    - if (idata->client == NULL) {
    - purple_debug_error("bonjour", "Error initializing Avahi: %s\n", avahi_strerror(error));
    - avahi_glib_poll_free(idata->glib_poll);
    - g_free(idata);
    - return FALSE;
    - }
    -
    - data->mdns_impl_data = idata;
    -
    - bonjour_dns_sd_set_jid(data->account, avahi_client_get_host_name(idata->client));
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -avahi_mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records)
    -{
    - int publish_result = 0;
    - AvahiSessionImplData *idata = data->mdns_impl_data;
    - AvahiStringList *lst = NULL;
    -
    - g_return_val_if_fail(idata != NULL, FALSE);
    -
    - if (!idata->group) {
    - idata->group = avahi_entry_group_new(idata->client,
    - _entry_group_cb, idata);
    - if (!idata->group) {
    - purple_debug_error("bonjour",
    - "Unable to initialize the data for the mDNS (%s).\n",
    - avahi_strerror(avahi_client_errno(idata->client)));
    - return FALSE;
    - }
    - }
    -
    - while (records) {
    - PurpleKeyValuePair *kvp = records->data;
    - lst = avahi_string_list_add_pair(lst, kvp->key, kvp->value);
    - records = records->next;
    - }
    -
    - /* Publish the service */
    - switch (type) {
    - case PUBLISH_START:
    - publish_result = avahi_entry_group_add_service_strlst(
    - idata->group, AVAHI_IF_UNSPEC,
    - AVAHI_PROTO_UNSPEC, 0,
    - bonjour_get_jid(data->account),
    - LINK_LOCAL_RECORD_NAME, NULL, NULL, data->port_p2pj, lst);
    - break;
    - case PUBLISH_UPDATE:
    - publish_result = avahi_entry_group_update_service_txt_strlst(
    - idata->group, AVAHI_IF_UNSPEC,
    - AVAHI_PROTO_UNSPEC, 0,
    - bonjour_get_jid(data->account),
    - LINK_LOCAL_RECORD_NAME, NULL, lst);
    - break;
    - }
    -
    - /* Free the memory used by temp data */
    - avahi_string_list_free(lst);
    -
    - if (publish_result < 0) {
    - purple_debug_error("bonjour",
    - "Failed to add the " LINK_LOCAL_RECORD_NAME " service. Error: %s\n",
    - avahi_strerror(publish_result));
    - return FALSE;
    - }
    -
    - if (type == PUBLISH_START
    - && (publish_result = avahi_entry_group_commit(idata->group)) < 0) {
    - purple_debug_error("bonjour",
    - "Failed to commit " LINK_LOCAL_RECORD_NAME " service. Error: %s\n",
    - avahi_strerror(publish_result));
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -avahi_mdns_browse(BonjourDnsSd *data)
    -{
    - AvahiSessionImplData *idata = data->mdns_impl_data;
    -
    - g_return_val_if_fail(idata != NULL, FALSE);
    -
    - idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
    - if (!idata->sb) {
    -
    - purple_debug_error("bonjour",
    - "Unable to initialize service browser. Error: %s.\n",
    - avahi_strerror(avahi_client_errno(idata->client)));
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -avahi_mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len)
    -{
    - AvahiSessionImplData *idata = data->mdns_impl_data;
    -
    - if (idata == NULL || idata->client == NULL)
    - return FALSE;
    -
    - if (avatar_data != NULL) {
    - gboolean new_group = FALSE;
    - gchar *svc_name;
    - int ret;
    - AvahiPublishFlags flags = 0;
    -
    - if (idata->buddy_icon_group == NULL) {
    - purple_debug_info("bonjour", "Setting new buddy icon.\n");
    - new_group = TRUE;
    -
    - idata->buddy_icon_group = avahi_entry_group_new(idata->client,
    - _buddy_icon_group_cb, data);
    - } else {
    - purple_debug_info("bonjour", "Updating existing buddy icon.\n");
    - flags |= AVAHI_PUBLISH_UPDATE;
    - }
    -
    - if (idata->buddy_icon_group == NULL) {
    - purple_debug_error("bonjour",
    - "Unable to initialize the buddy icon group (%s).\n",
    - avahi_strerror(avahi_client_errno(idata->client)));
    - return FALSE;
    - }
    -
    - svc_name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local",
    - bonjour_get_jid(data->account));
    -
    - ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC,
    - AVAHI_PROTO_UNSPEC, flags, svc_name,
    - AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len);
    -
    - g_free(svc_name);
    -
    - if (ret < 0) {
    - purple_debug_error("bonjour",
    - "Failed to register buddy icon. Error: %s\n", avahi_strerror(ret));
    - if (new_group) {
    - avahi_entry_group_free(idata->buddy_icon_group);
    - idata->buddy_icon_group = NULL;
    - }
    - return FALSE;
    - }
    -
    - if (new_group && (ret = avahi_entry_group_commit(idata->buddy_icon_group)) < 0) {
    - purple_debug_error("bonjour",
    - "Failed to commit buddy icon group. Error: %s\n", avahi_strerror(ret));
    - avahi_entry_group_free(idata->buddy_icon_group);
    - idata->buddy_icon_group = NULL;
    - return FALSE;
    - }
    - } else if (idata->buddy_icon_group != NULL) {
    - purple_debug_info("bonjour", "Removing existing buddy icon.\n");
    - avahi_entry_group_free(idata->buddy_icon_group);
    - idata->buddy_icon_group = NULL;
    - }
    -
    - return TRUE;
    -}
    -
    -static void
    -avahi_mdns_stop(BonjourDnsSd *data)
    -{
    - AvahiSessionImplData *idata = data->mdns_impl_data;
    -
    - if (idata == NULL || idata->client == NULL)
    - return;
    -
    - if (idata->sb != NULL)
    - avahi_service_browser_free(idata->sb);
    -
    - avahi_client_free(idata->client);
    - avahi_glib_poll_free(idata->glib_poll);
    -
    - g_free(idata);
    -
    - data->mdns_impl_data = NULL;
    -}
    -
    -static void
    -avahi_mdns_init_buddy(BonjourBuddy *buddy)
    -{
    - buddy->mdns_impl_data = g_new0(AvahiBuddyImplData, 1);
    -}
    -
    -static void
    -avahi_mdns_delete_buddy(BonjourBuddy *buddy)
    -{
    - AvahiBuddyImplData *idata = buddy->mdns_impl_data;
    -
    - g_return_if_fail(idata != NULL);
    -
    - if (idata->buddy_icon_rec_browser != NULL)
    - avahi_record_browser_free(idata->buddy_icon_rec_browser);
    -
    - g_slist_free_full(idata->resolvers, (GDestroyNotify)_cleanup_resolver_data);
    -
    - g_free(idata);
    -
    - buddy->mdns_impl_data = NULL;
    -}
    -
    -static void
    -avahi_mdns_retrieve_buddy_icon(BonjourBuddy* buddy)
    -{
    - PurpleConnection *conn = purple_account_get_connection(buddy->account);
    - BonjourData *bd = purple_connection_get_protocol_data(conn);
    - AvahiSessionImplData *session_idata = bd->dns_sd_data->mdns_impl_data;
    - AvahiBuddyImplData *idata = buddy->mdns_impl_data;
    - gchar *name;
    -
    - g_return_if_fail(idata != NULL);
    -
    - if (idata->buddy_icon_rec_browser != NULL)
    - avahi_record_browser_free(idata->buddy_icon_rec_browser);
    -
    - purple_debug_info("bonjour", "Retrieving buddy icon for '%s'.\n", buddy->name);
    -
    - name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local", buddy->name);
    - idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC,
    - AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
    - _buddy_icon_record_cb, buddy);
    - g_free(name);
    -
    - if (!idata->buddy_icon_rec_browser) {
    - purple_debug_error("bonjour",
    - "Unable to initialize buddy icon record browser. Error: %s.\n",
    - avahi_strerror(avahi_client_errno(session_idata->client)));
    - }
    -
    -}
    -
    -gboolean
    -mdns_available(void)
    -{
    - _mdns_init_session = avahi_mdns_init_session;
    - _mdns_publish = avahi_mdns_publish;
    - _mdns_browse = avahi_mdns_browse;
    - _mdns_stop = avahi_mdns_stop;
    - _mdns_set_buddy_icon_data = avahi_mdns_set_buddy_icon_data;
    - _mdns_init_buddy = avahi_mdns_init_buddy;
    - _mdns_delete_buddy = avahi_mdns_delete_buddy;
    - _mdns_retrieve_buddy_icon = avahi_mdns_retrieve_buddy_icon;
    -
    - return TRUE;
    -}
    --- a/libpurple/protocols/bonjour/mdns_common.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,282 +0,0 @@
    -/*
    - * 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 Library 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 <string.h>
    -
    -#include <purple.h>
    -
    -#include "mdns_common.h"
    -#include "bonjour.h"
    -#include "buddy.h"
    -
    -
    -/****************************
    - * mdns_interface functions *
    - ****************************/
    -
    -mdns_init_session_func *_mdns_init_session = NULL;
    -mdns_publish_func *_mdns_publish = NULL;
    -mdns_browse_func *_mdns_browse = NULL;
    -mdns_stop_func *_mdns_stop = NULL;
    -mdns_set_buddy_icon_data_func *_mdns_set_buddy_icon_data = NULL;
    -mdns_init_buddy_func *_mdns_init_buddy = NULL;
    -mdns_delete_buddy_func *_mdns_delete_buddy = NULL;
    -mdns_retrieve_buddy_icon_func *_mdns_retrieve_buddy_icon = NULL;
    -
    -/**
    - * Allocate space for the dns-sd data.
    - */
    -BonjourDnsSd *
    -bonjour_dns_sd_new(void) {
    - BonjourDnsSd *data = g_new0(BonjourDnsSd, 1);
    - return data;
    -}
    -
    -/**
    - * Deallocate the space of the dns-sd data.
    - */
    -void bonjour_dns_sd_free(BonjourDnsSd *data) {
    - g_free(data->first);
    - g_free(data->last);
    - g_free(data->phsh);
    - g_free(data->status);
    - g_free(data->vc);
    - g_free(data->msg);
    - g_free(data);
    -}
    -
    -#define MAX_TXT_CONSTITUENT_LEN 255
    -
    -/* Make sure that the value isn't longer than it is supposed to be */
    -static const char*
    -get_max_txt_record_value(const char *key, const char *value)
    -{
    - /* "each constituent string of a DNS TXT record is limited to 255 bytes"
    - * This includes the key and the '='
    - */
    - static char buffer[MAX_TXT_CONSTITUENT_LEN + 1];
    - gchar *end_valid = NULL;
    - int len = MIN(strlen(value), MAX_TXT_CONSTITUENT_LEN - (strlen(key) + 2));
    -
    - strncpy(buffer, value, len);
    -
    - buffer[len] = '\0';
    -
    - /* If we've cut part of a utf-8 character, kill it */
    - if (!g_utf8_validate(buffer, -1, (const gchar **)&end_valid))
    - *end_valid = '\0';
    -
    - return buffer;
    -}
    -
    -static inline GSList *
    -_add_txt_record(GSList *list, const gchar *key, const gchar *value)
    -{
    - const char *max_value = get_max_txt_record_value(key, value);
    - PurpleKeyValuePair *kvp = purple_key_value_pair_new_full(key, g_strdup(max_value), g_free);
    - return g_slist_prepend(list, kvp);
    -}
    -
    -static GSList *generate_presence_txt_records(BonjourDnsSd *data) {
    - GSList *ret = NULL;
    - char portstring[6];
    - const char *jid, *aim, *email;
    -
    - /* Convert the port to a string */
    - g_snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj);
    -
    - jid = purple_account_get_string(data->account, "jid", NULL);
    - aim = purple_account_get_string(data->account, "AIM", NULL);
    - email = purple_account_get_string(data->account, "email", NULL);
    -
    - /* We should try to follow XEP-0174, but some clients have "issues", so we humor them.
    - * See http://telepathy.freedesktop.org/wiki/SalutInteroperability
    - */
    -
    - /* Large TXT records are problematic.
    - * While it is technically possible for this to exceed a standard 512-byte
    - * DNS message, it shouldn't happen unless we get wacky data entered for
    - * some of the freeform fields. It is even less likely to exceed the
    - * recommended maximum of 1300 bytes.
    - */
    -
    - /* Needed by iChat */
    - ret = _add_txt_record(ret, "txtvers", "1");
    - /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
    - ret = _add_txt_record(ret, "1st", data->first);
    - /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
    - ret = _add_txt_record(ret, "last", data->last);
    - /* Needed by Adium */
    - ret = _add_txt_record(ret, "port.p2pj", portstring);
    - /* Needed by iChat, Gaim/Pidgin <= 2.0.1 */
    - ret = _add_txt_record(ret, "status", data->status);
    - ret = _add_txt_record(ret, "node", "libpurple");
    - ret = _add_txt_record(ret, "ver", VERSION);
    - /* Currently always set to "!" since we don't support AV and won't ever be in a conference */
    - ret = _add_txt_record(ret, "vc", data->vc);
    - if (email != NULL && *email != '\0') {
    - ret = _add_txt_record(ret, "email", email);
    - }
    - if (jid != NULL && *jid != '\0') {
    - ret = _add_txt_record(ret, "jid", jid);
    - }
    - /* Nonstandard, but used by iChat */
    - if (aim != NULL && *aim != '\0') {
    - ret = _add_txt_record(ret, "AIM", aim);
    - }
    - if (data->msg != NULL && *data->msg != '\0') {
    - ret = _add_txt_record(ret, "msg", data->msg);
    - }
    - if (data->phsh != NULL && *data->phsh != '\0') {
    - ret = _add_txt_record(ret, "phsh", data->phsh);
    - }
    -
    - /* TODO: ext, nick */
    - return ret;
    -}
    -
    -static gboolean publish_presence(BonjourDnsSd *data, PublishType type) {
    - GSList *txt_records;
    - gboolean ret;
    -
    - txt_records = generate_presence_txt_records(data);
    - ret = _mdns_publish(data, type, txt_records);
    - g_slist_free_full(txt_records, (GDestroyNotify)purple_key_value_pair_free);
    -
    - return ret;
    -}
    -
    -/**
    - * Send a new dns-sd packet updating our status.
    - */
    -void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message) {
    - g_free(data->status);
    - g_free(data->msg);
    -
    - data->status = g_strdup(status);
    - data->msg = g_strdup(status_message);
    -
    - /* Update our text record with the new status */
    - publish_presence(data, PUBLISH_UPDATE);
    -}
    -
    -/**
    - * Retrieve the buddy icon blob
    - */
    -void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) {
    - _mdns_retrieve_buddy_icon(buddy);
    -}
    -
    -void bonjour_dns_sd_update_buddy_icon(BonjourDnsSd *data) {
    - PurpleImage *img;
    -
    - if ((img = purple_buddy_icons_find_account_icon(data->account))) {
    - gconstpointer avatar_data;
    - gsize avatar_len;
    -
    - avatar_data = purple_image_get_data(img);
    - avatar_len = purple_image_get_data_size(img);
    -
    - if (_mdns_set_buddy_icon_data(data, avatar_data, avatar_len)) {
    - g_free(data->phsh);
    - data->phsh = NULL;
    -
    - data->phsh = g_compute_checksum_for_data(
    - G_CHECKSUM_SHA1, avatar_data, avatar_len);
    -
    - /* Update our TXT record */
    - publish_presence(data, PUBLISH_UPDATE);
    - }
    -
    - g_object_unref(img);
    - } else {
    - /* We need to do this regardless of whether data->phsh is set so that we
    - * cancel any icons that are currently in the process of being set */
    - _mdns_set_buddy_icon_data(data, NULL, 0);
    - if (data->phsh != NULL) {
    - /* Clear the buddy icon */
    - g_free(data->phsh);
    - data->phsh = NULL;
    - /* Update our TXT record */
    - publish_presence(data, PUBLISH_UPDATE);
    - }
    - }
    -}
    -
    -/**
    - * Advertise our presence within the dns-sd daemon and start browsing
    - * for other bonjour peers.
    - */
    -gboolean bonjour_dns_sd_start(BonjourDnsSd *data) {
    -
    - /* Initialize the dns-sd data and session */
    - if (!_mdns_init_session(data))
    - return FALSE;
    -
    - /* Publish our bonjour IM client at the mDNS daemon */
    - if (!publish_presence(data, PUBLISH_START))
    - return FALSE;
    -
    - /* Advise the daemon that we are waiting for connections */
    - if (!_mdns_browse(data)) {
    - purple_debug_error("bonjour", "Unable to get service.\n");
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Unregister the "_presence._tcp" service at the mDNS daemon.
    - */
    -
    -void bonjour_dns_sd_stop(BonjourDnsSd *data) {
    - _mdns_stop(data);
    -}
    -
    -void
    -bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname)
    -{
    - PurpleConnection *conn = purple_account_get_connection(account);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - BonjourData *bd = purple_connection_get_protocol_data(conn);
    - const char *tmp, *account_name = purple_contact_info_get_username(info);
    -
    - /* Previously we allowed the hostname part of the jid to be set
    - * explicitly when it should always be the current hostname.
    - * That is what this is intended to deal with.
    - */
    - if ((tmp = strchr(account_name, '@'))
    - && strstr(tmp, hostname) == (tmp + 1)
    - && *((tmp + 1) + strlen(hostname)) == '\0')
    - bd->jid = g_strdup(account_name);
    - else {
    - const char *tmp2;
    - GString *str = g_string_new("");
    - /* Escape an '@' in the account name */
    - tmp = account_name;
    - while ((tmp2 = strchr(tmp, '@')) != NULL) {
    - g_string_append_len(str, tmp, tmp2 - tmp);
    - g_string_append(str, "\\40");
    - tmp = tmp2 + 1;
    - }
    - g_string_append(str, tmp);
    - g_string_append_c(str, '@');
    - g_string_append(str, hostname);
    -
    - bd->jid = g_string_free(str, FALSE);
    - }
    -}
    --- a/libpurple/protocols/bonjour/mdns_common.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,92 +0,0 @@
    -/*
    - * 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 Library 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_BONJOUR_MDNS_COMMON_H
    -#define PURPLE_BONJOUR_MDNS_COMMON_H
    -
    -#include "mdns_types.h"
    -
    -#include "buddy.h"
    -
    -/**
    - * Allocate space for the dns-sd data.
    - */
    -BonjourDnsSd *bonjour_dns_sd_new(void);
    -
    -/**
    - * Deallocate the space of the dns-sd data.
    - */
    -void bonjour_dns_sd_free(BonjourDnsSd *data);
    -
    -/**
    - * Send a new dns-sd packet updating our status.
    - */
    -void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message);
    -
    -/**
    - * Retrieve the buddy icon blob
    - */
    -void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy);
    -
    -/**
    - * Deal with a buddy icon update
    - */
    -void bonjour_dns_sd_update_buddy_icon(BonjourDnsSd *data);
    -
    -/**
    - * Advertise our presence within the dns-sd daemon and start
    - * browsing for other bonjour peers.
    - */
    -gboolean bonjour_dns_sd_start(BonjourDnsSd *data);
    -
    -/**
    - * Unregister the "_presence._tcp" service at the mDNS daemon.
    - */
    -void bonjour_dns_sd_stop(BonjourDnsSd *data);
    -
    -void bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname);
    -
    -/****************************
    - * mdns_interface functions *
    - ****************************/
    -
    -typedef gboolean mdns_init_session_func(BonjourDnsSd *data);
    -extern mdns_init_session_func *_mdns_init_session;
    -
    -typedef gboolean mdns_publish_func(BonjourDnsSd *data, PublishType type, GSList *records);
    -extern mdns_publish_func *_mdns_publish;
    -
    -typedef gboolean mdns_browse_func(BonjourDnsSd *data);
    -extern mdns_browse_func *_mdns_browse;
    -
    -typedef void mdns_stop_func(BonjourDnsSd *data);
    -extern mdns_stop_func *_mdns_stop;
    -
    -typedef gboolean mdns_set_buddy_icon_data_func(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len);
    -extern mdns_set_buddy_icon_data_func *_mdns_set_buddy_icon_data;
    -
    -typedef void mdns_init_buddy_func(BonjourBuddy *buddy);
    -extern mdns_init_buddy_func *_mdns_init_buddy;
    -
    -typedef void mdns_delete_buddy_func(BonjourBuddy *buddy);
    -extern mdns_delete_buddy_func *_mdns_delete_buddy;
    -
    -typedef void mdns_retrieve_buddy_icon_func(BonjourBuddy* buddy);
    -extern mdns_retrieve_buddy_icon_func *_mdns_retrieve_buddy_icon;
    -
    -gboolean mdns_available(void);
    -
    -#endif /* PURPLE_BONJOUR_MDNS_COMMON_H */
    --- a/libpurple/protocols/bonjour/mdns_dns_sd.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,756 +0,0 @@
    -/*
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU Library General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#include "buddy.h"
    -#ifdef __APPLE_CC__
    -#include <dns_sd.h>
    -#else
    -#include "dns_sd_proxy.h"
    -#endif
    -#include "mdns_common.h"
    -#include "mdns_dns_sd.h"
    -#include "bonjour.h"
    -
    -static GSList *pending_buddies = NULL;
    -
    -typedef struct {
    - DNSServiceRef sdRef;
    - PurpleAccount *account;
    - guint input_handler;
    -} DnsSDServiceRefHandlerData;
    -
    -/* data used by win32 bonjour implementation */
    -typedef struct {
    - DnsSDServiceRefHandlerData *presence_query;
    - DnsSDServiceRefHandlerData *browser_query;
    - DNSRecordRef buddy_icon_rec;
    -} Win32SessionImplData;
    -
    -typedef struct {
    - DnsSDServiceRefHandlerData *txt_query;
    - uint32_t if_idx;
    - gchar *name;
    - gchar *type;
    - gchar *domain;
    - /* This is a reference to the entry in BonjourBuddy->ips */
    - const char *ip;
    -} Win32SvcResolverData;
    -
    -typedef struct {
    - GSList *resolvers;
    - DnsSDServiceRefHandlerData *null_query;
    -} Win32BuddyImplData;
    -
    -/* data structure for the resolve callback */
    -typedef struct {
    - DnsSDServiceRefHandlerData *resolver_query;
    - PurpleAccount *account;
    - BonjourBuddy *bb;
    - Win32SvcResolverData *res_data;
    - gchar *full_service_name;
    -} ResolveCallbackArgs;
    -
    -
    -static gint
    -_find_resolver_data(gconstpointer a, gconstpointer b) {
    - const Win32SvcResolverData *rd_a = a;
    - const Win32SvcResolverData *rd_b = b;
    - gint ret = 1;
    -
    - if(rd_a->if_idx == rd_b->if_idx
    - && purple_strequal(rd_a->name, rd_b->name)
    - && purple_strequal(rd_a->type, rd_b->type)
    - && purple_strequal(rd_a->domain, rd_b->domain)) {
    - ret = 0;
    - }
    -
    - return ret;
    -}
    -
    -static void
    -_cleanup_resolver_data(Win32SvcResolverData *rd) {
    - if (rd->txt_query != NULL) {
    - g_source_remove(rd->txt_query->input_handler);
    - DNSServiceRefDeallocate(rd->txt_query->sdRef);
    - g_free(rd->txt_query);
    - }
    - g_free(rd->name);
    - g_free(rd->type);
    - g_free(rd->domain);
    - g_free(rd);
    -}
    -
    -static void
    -_mdns_handle_event(gpointer data, G_GNUC_UNUSED gint source,
    - G_GNUC_UNUSED PurpleInputCondition condition)
    -{
    - DnsSDServiceRefHandlerData *srh = data;
    - DNSServiceErrorType errorCode = DNSServiceProcessResult(srh->sdRef);
    - if (errorCode != kDNSServiceErr_NoError) {
    - purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode);
    - /* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */
    - if (errorCode == kDNSServiceErr_Unknown) {
    - purple_connection_error(purple_account_get_connection(srh->account),
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error communicating with local mDNSResponder."));
    - }
    - }
    -}
    -
    -static void
    -_mdns_parse_text_record(BonjourBuddy *buddy, const char *record, uint16_t record_len)
    -{
    - const char *txt_entry;
    - uint8_t txt_len;
    - int i;
    -
    - clear_bonjour_buddy_values(buddy);
    - for (i = 0; buddy_TXT_records[i] != NULL; i++) {
    - txt_entry = TXTRecordGetValuePtr(record_len, record, buddy_TXT_records[i], &txt_len);
    - if (txt_entry != NULL)
    - set_bonjour_buddy_value(buddy, buddy_TXT_records[i], txt_entry, txt_len);
    - }
    -}
    -
    -static void DNSSD_API
    -_mdns_record_query_callback(G_GNUC_UNUSED DNSServiceRef DNSServiceRef,
    - DNSServiceFlags flags,
    - G_GNUC_UNUSED uint32_t interfaceIndex,
    - DNSServiceErrorType errorCode,
    - G_GNUC_UNUSED const char *fullname,
    - uint16_t rrtype, G_GNUC_UNUSED uint16_t rrclass,
    - uint16_t rdlen, const void *rdata,
    - G_GNUC_UNUSED uint32_t ttl, void *context)
    -{
    - if (errorCode != kDNSServiceErr_NoError) {
    - purple_debug_error("bonjour", "record query - callback error (%d).\n", errorCode);
    - /* TODO: Probably should remove the buddy when this happens */
    - } else if (flags & kDNSServiceFlagsAdd) {
    - if (rrtype == kDNSServiceType_TXT) {
    - /* New Buddy */
    - BonjourBuddy *bb = (BonjourBuddy*) context;
    - _mdns_parse_text_record(bb, rdata, rdlen);
    - bonjour_buddy_add_to_purple(bb);
    - } else if (rrtype == kDNSServiceType_NULL) {
    - /* Buddy Icon response */
    - BonjourBuddy *bb = (BonjourBuddy*) context;
    - Win32BuddyImplData *idata = bb->mdns_impl_data;
    -
    - g_return_if_fail(idata != NULL);
    -
    - bonjour_buddy_got_buddy_icon(bb, rdata, rdlen);
    -
    - /* We've got what we need; stop listening */
    - g_source_remove(idata->null_query->input_handler);
    - DNSServiceRefDeallocate(idata->null_query->sdRef);
    - g_free(idata->null_query);
    - idata->null_query = NULL;
    - }
    - }
    -}
    -
    -static void DNSSD_API
    -_mdns_resolve_host_callback(G_GNUC_UNUSED DNSServiceRef sdRef,
    - G_GNUC_UNUSED DNSServiceFlags flags,
    - G_GNUC_UNUSED uint32_t interfaceIndex,
    - DNSServiceErrorType errorCode,
    - G_GNUC_UNUSED const char *hostname,
    - const struct sockaddr *address,
    - G_GNUC_UNUSED uint32_t ttl, void *context)
    -{
    - ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
    - Win32BuddyImplData *idata = args->bb->mdns_impl_data;
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    - gboolean delete_buddy = FALSE;
    -
    - g_source_remove(args->resolver_query->input_handler);
    - DNSServiceRefDeallocate(args->resolver_query->sdRef);
    - g_free(args->resolver_query);
    - args->resolver_query = NULL;
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager, args->account,
    - args->res_data->name);
    -
    - if (PURPLE_IS_CONTACT(contact)) {
    - if (g_object_get_data(G_OBJECT(contact), "bonjour-buddy") != args->bb) {
    - purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record.",
    - args->res_data->name);
    - goto cleanup;
    - }
    - /* Make sure that the BonjourBuddy associated with this request is still around */
    - } else if (g_slist_find(pending_buddies, args->bb) == NULL) {
    - purple_debug_error("bonjour", "host resolution - complete, but buddy no longer pending.\n");
    - goto cleanup;
    - }
    -
    - if (errorCode != kDNSServiceErr_NoError) {
    - purple_debug_error("bonjour", "host resolution - callback error (%d).\n", errorCode);
    - delete_buddy = TRUE;
    - } else {
    - DNSServiceRef txt_query_sr;
    - gchar *ip = NULL;
    -
    - /* finally, set up the continuous txt record watcher, and add the buddy to purple */
    - errorCode = DNSServiceQueryRecord(&txt_query_sr, kDNSServiceFlagsLongLivedQuery,
    - kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
    - kDNSServiceClass_IN, _mdns_record_query_callback, args->bb);
    - if (errorCode == kDNSServiceErr_NoError) {
    - GSocketAddress *addr = g_socket_address_new_from_native(
    - (gpointer)address, sizeof(struct sockaddr_in));
    - if (G_IS_INET_SOCKET_ADDRESS(addr)) {
    - GInetAddress *inet_addr = g_inet_socket_address_get_address(
    - G_INET_SOCKET_ADDRESS(addr));
    - ip = g_inet_address_to_string(inet_addr);
    - }
    - g_clear_object(&addr);
    - }
    -
    - if (ip) {
    - purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
    -
    - args->bb->ips = g_slist_prepend(args->bb->ips, ip);
    - args->res_data->ip = args->bb->ips->data;
    -
    - args->res_data->txt_query = g_new(DnsSDServiceRefHandlerData, 1);
    - args->res_data->txt_query->sdRef = txt_query_sr;
    - args->res_data->txt_query->account = args->account;
    -
    - args->res_data->txt_query->input_handler = purple_input_add(DNSServiceRefSockFD(txt_query_sr),
    - PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query);
    -
    - bonjour_buddy_add_to_purple(args->bb);
    - } else {
    - purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s (%d)\n", args->bb->name, errorCode);
    - delete_buddy = TRUE;
    - }
    - }
    -
    - cleanup:
    -
    - if (delete_buddy) {
    - idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
    - _cleanup_resolver_data(args->res_data);
    -
    - /* If this was the last resolver, remove the buddy */
    - if (idata->resolvers == NULL) {
    - if (PURPLE_IS_CONTACT(contact)) {
    - bonjour_buddy_signed_off(contact);
    - } else {
    - bonjour_buddy_delete(args->bb);
    - }
    -
    - /* Remove from the pending list */
    - pending_buddies = g_slist_remove(pending_buddies, args->bb);
    - }
    - } else {
    - /* Remove from the pending list */
    - pending_buddies = g_slist_remove(pending_buddies, args->bb);
    - }
    -
    - /* free the remaining args memory */
    - g_free(args->full_service_name);
    - g_free(args);
    -
    - g_clear_object(&contact);
    -}
    -
    -static void DNSSD_API
    -_mdns_service_resolve_callback(G_GNUC_UNUSED DNSServiceRef sdRef,
    - G_GNUC_UNUSED DNSServiceFlags flags,
    - uint32_t interfaceIndex,
    - DNSServiceErrorType errorCode,
    - const char *fullname, const char *hosttarget,
    - uint16_t port, G_GNUC_UNUSED uint16_t txtLen,
    - G_GNUC_UNUSED const unsigned char *txtRecord,
    - void *context)
    -{
    - ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
    - Win32BuddyImplData *idata = args->bb->mdns_impl_data;
    -
    - /* remove the input fd and destroy the service ref */
    - g_source_remove(args->resolver_query->input_handler);
    - DNSServiceRefDeallocate(args->resolver_query->sdRef);
    -
    - if (errorCode != kDNSServiceErr_NoError)
    - purple_debug_error("bonjour", "service resolver - callback error. (%d)\n", errorCode);
    - else {
    - DNSServiceRef getaddrinfo_sr;
    - /* set more arguments, and start the host resolver */
    - errorCode = DNSServiceGetAddrInfo(&getaddrinfo_sr, 0, interfaceIndex,
    - kDNSServiceProtocol_IPv4, hosttarget, _mdns_resolve_host_callback, args);
    - if (errorCode != kDNSServiceErr_NoError)
    - purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
    - else {
    - args->resolver_query->sdRef = getaddrinfo_sr;
    - args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(getaddrinfo_sr),
    - PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
    - args->full_service_name = g_strdup(fullname);
    -
    - /* TODO: Should this be per resolver? */
    - args->bb->port_p2pj = g_ntohs(port);
    -
    - /* We don't want to hit the cleanup code */
    - return;
    - }
    - }
    -
    - /* If we get this far, clean up */
    -
    - g_free(args->resolver_query);
    - args->resolver_query = NULL;
    -
    - idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
    - _cleanup_resolver_data(args->res_data);
    -
    - /* If this was the last resolver, remove the buddy */
    - if (idata->resolvers == NULL) {
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager,
    - args->account,
    - args->bb->name);
    -
    - /* See if this is now attached to a PurpleContact */
    - if(PURPLE_IS_CONTACT(contact)) {
    - bonjour_buddy_signed_off(contact);
    - } else {
    - /* Remove from the pending list */
    - pending_buddies = g_slist_remove(pending_buddies, args->bb);
    - bonjour_buddy_delete(args->bb);
    - }
    -
    - g_clear_object(&contact);
    - }
    -
    - g_free(args);
    -
    -}
    -
    -static void DNSSD_API
    -_mdns_service_register_callback(G_GNUC_UNUSED DNSServiceRef sdRef,
    - G_GNUC_UNUSED DNSServiceFlags flags,
    - DNSServiceErrorType errorCode,
    - G_GNUC_UNUSED const char *name,
    - G_GNUC_UNUSED const char *regtype,
    - G_GNUC_UNUSED const char *domain,
    - G_GNUC_UNUSED void *context)
    -{
    - /* TODO: deal with collision */
    - if (errorCode != kDNSServiceErr_NoError)
    - purple_debug_error("bonjour", "service advertisement - callback error (%d).\n", errorCode);
    - else
    - purple_debug_info("bonjour", "service advertisement - callback.\n");
    -}
    -
    -static void DNSSD_API
    -_mdns_service_browse_callback(G_GNUC_UNUSED DNSServiceRef sdRef,
    - DNSServiceFlags flags, uint32_t interfaceIndex,
    - DNSServiceErrorType errorCode,
    - const char *serviceName, const char *regtype,
    - const char *replyDomain, void *context)
    -{
    - PurpleAccount *account = (PurpleAccount*)context;
    -
    - if (errorCode != kDNSServiceErr_NoError)
    - purple_debug_error("bonjour", "service browser - callback error (%d)\n", errorCode);
    - else if (flags & kDNSServiceFlagsAdd) {
    - /* A presence service instance has been discovered... check it isn't us! */
    - if (purple_utf8_strcasecmp(serviceName, bonjour_get_jid(account)) != 0) {
    - DNSServiceErrorType resErrorCode;
    - /* OK, lets go ahead and resolve it to add to the buddy list */
    - ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
    - DNSServiceRef resolver_sr;
    -
    - purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n",
    - serviceName, interfaceIndex, regtype ? regtype : "",
    - replyDomain ? replyDomain : "");
    -
    - resErrorCode = DNSServiceResolve(&resolver_sr, 0, interfaceIndex, serviceName, regtype,
    - replyDomain, _mdns_service_resolve_callback, args);
    - if (resErrorCode == kDNSServiceErr_NoError) {
    - GSList *tmp = pending_buddies;
    - PurpleBuddy *pb;
    - BonjourBuddy* bb = NULL;
    - Win32SvcResolverData *rd;
    - Win32BuddyImplData *idata;
    -
    - /* Is there an existing buddy? */
    - if ((pb = purple_blist_find_buddy(account, serviceName)))
    - bb = purple_buddy_get_protocol_data(pb);
    - /* Is there a pending buddy? */
    - else {
    - while (tmp) {
    - BonjourBuddy *bb_tmp = tmp->data;
    - if (purple_strequal(bb_tmp->name, serviceName)) {
    - bb = bb_tmp;
    - break;
    - }
    - tmp = tmp->next;
    - }
    - }
    -
    - if (bb == NULL) {
    - bb = bonjour_buddy_new(serviceName, account);
    -
    - /* This is only necessary for the wacky case where someone previously manually added a buddy. */
    - if (pb == NULL)
    - pending_buddies = g_slist_prepend(pending_buddies, bb);
    - else
    - purple_buddy_set_protocol_data(pb, bb);
    - }
    -
    - rd = g_new0(Win32SvcResolverData, 1);
    - rd->if_idx = interfaceIndex;
    - rd->name = g_strdup(serviceName);
    - rd->type = g_strdup(regtype);
    - rd->domain = g_strdup(replyDomain);
    -
    - idata = bb->mdns_impl_data;
    - idata->resolvers = g_slist_prepend(idata->resolvers, rd);
    -
    - args->bb = bb;
    - args->res_data = rd;
    - args->account = account;
    -
    - args->resolver_query = g_new(DnsSDServiceRefHandlerData, 1);
    - args->resolver_query->sdRef = resolver_sr;
    - args->resolver_query->account = account;
    - /* get a file descriptor for this service ref, and add it to the input list */
    - args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(resolver_sr),
    - PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
    - } else {
    - purple_debug_error("bonjour", "service browser - failed to resolve service. (%d)\n", resErrorCode);
    - g_free(args);
    - }
    - }
    - } else {
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager, account,
    - serviceName);
    -
    - /* A peer has sent a goodbye packet, remove them from the buddy list */
    - purple_debug_info("bonjour", "Received remove notification for '%s' on iface %u (%s, %s)\n",
    - serviceName, interfaceIndex, regtype ? regtype : "",
    - replyDomain ? replyDomain : "");
    -
    - if(PURPLE_IS_CONTACT(contact)) {
    - GSList *l;
    - /* There may be multiple presences, we should only get rid of this one */
    - Win32SvcResolverData *rd_search;
    - BonjourBuddy *bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - Win32BuddyImplData *idata;
    -
    - g_return_if_fail(bb != NULL);
    -
    - idata = bb->mdns_impl_data;
    -
    - rd_search = g_new0(Win32SvcResolverData, 1);
    - rd_search->if_idx = interfaceIndex;
    - rd_search->name = (gchar *) serviceName;
    - rd_search->type = (gchar *) regtype;
    - rd_search->domain = (gchar *) replyDomain;
    -
    - l = g_slist_find_custom(idata->resolvers, rd_search, _find_resolver_data);
    -
    - g_free(rd_search);
    -
    - if (l != NULL) {
    - Win32SvcResolverData *rd = l->data;
    - idata->resolvers = g_slist_delete_link(idata->resolvers, l);
    - /* This IP is no longer available */
    - if (rd->ip != NULL) {
    - bb->ips = g_slist_delete_link(bb->ips, l);
    - g_free((gchar *) rd->ip);
    - }
    - _cleanup_resolver_data(rd);
    -
    - /* If this was the last resolver, remove the buddy */
    - if (idata->resolvers == NULL) {
    - purple_debug_info("bonjour", "Removed last presence for buddy '%s'; signing off buddy.\n",
    - serviceName);
    - bonjour_buddy_signed_off(contact);
    - }
    - }
    - } else {
    - purple_debug_warning("bonjour", "Unable to find contact (%s) to remove\n", serviceName ? serviceName : "(null)");
    - /* TODO: Should we look in the pending buddies list? */
    - }
    -
    - g_clear_object(&contact);
    - }
    -}
    -
    -/****************************
    - * mdns_interface functions *
    - ****************************/
    -
    -static gboolean
    -dns_sd_mdns_init_session(BonjourDnsSd *data)
    -{
    - data->mdns_impl_data = g_new0(Win32SessionImplData, 1);
    -
    - bonjour_dns_sd_set_jid(data->account, g_get_host_name());
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -dns_sd_mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records)
    -{
    - TXTRecordRef dns_data;
    - gboolean ret = TRUE;
    - DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
    - Win32SessionImplData *idata = data->mdns_impl_data;
    -
    - g_return_val_if_fail(idata != NULL, FALSE);
    -
    - TXTRecordCreate(&dns_data, 256, NULL);
    -
    - while (records) {
    - PurpleKeyValuePair *kvp = records->data;
    - errorCode = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value);
    - if (errorCode != kDNSServiceErr_NoError)
    - break;
    - records = records->next;
    - }
    -
    - if (errorCode != kDNSServiceErr_NoError) {
    - purple_debug_error("bonjour", "Unable to allocate memory for text record.(%d)\n", errorCode);
    - ret = FALSE;
    - } else {
    - /* OK, we're done constructing the text record, (re)publish the service */
    - DNSServiceRef presence_sr;
    -
    - switch (type) {
    - case PUBLISH_START:
    - purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj);
    - errorCode = DNSServiceRegister(
    - &presence_sr, 0, kDNSServiceInterfaceIndexAny,
    - bonjour_get_jid(data->account), LINK_LOCAL_RECORD_NAME,
    - NULL, NULL, g_htons(data->port_p2pj),
    - TXTRecordGetLength(&dns_data),
    - TXTRecordGetBytesPtr(&dns_data),
    - _mdns_service_register_callback, NULL);
    - break;
    -
    - case PUBLISH_UPDATE:
    - purple_debug_info("bonjour", "Updating presence.\n");
    - errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
    - break;
    - }
    -
    - if (errorCode != kDNSServiceErr_NoError) {
    - purple_debug_error("bonjour", "Failed to publish presence service.(%d)\n", errorCode);
    - ret = FALSE;
    - } else if (type == PUBLISH_START) {
    - /* We need to do this because according to the Apple docs:
    - * "the client is responsible for ensuring that DNSServiceProcessResult() is called
    - * whenever there is a reply from the daemon - the daemon may terminate its connection
    - * with a client that does not process the daemon's responses */
    - idata->presence_query = g_new(DnsSDServiceRefHandlerData, 1);
    - idata->presence_query->sdRef = presence_sr;
    - idata->presence_query->account = data->account;
    - idata->presence_query->input_handler = purple_input_add(DNSServiceRefSockFD(presence_sr),
    - PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_query);
    - }
    - }
    -
    - /* Free the memory used by temp data */
    - TXTRecordDeallocate(&dns_data);
    - return ret;
    -}
    -
    -static gboolean
    -dns_sd_mdns_browse(BonjourDnsSd *data)
    -{
    - DNSServiceErrorType errorCode;
    - Win32SessionImplData *idata = data->mdns_impl_data;
    - DNSServiceRef browser_sr;
    -
    - g_return_val_if_fail(idata != NULL, FALSE);
    -
    - errorCode = DNSServiceBrowse(&browser_sr, 0, kDNSServiceInterfaceIndexAny,
    - LINK_LOCAL_RECORD_NAME, NULL,_mdns_service_browse_callback,
    - data->account);
    - if (errorCode == kDNSServiceErr_NoError) {
    - idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1);
    - idata->browser_query->sdRef = browser_sr;
    - idata->browser_query->account = data->account;
    - idata->browser_query->input_handler = purple_input_add(DNSServiceRefSockFD(browser_sr),
    - PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_query);
    - return TRUE;
    - } else
    - purple_debug_error("bonjour", "Error registering Local Link presence browser. (%d)\n", errorCode);
    -
    -
    - return FALSE;
    -}
    -
    -static void
    -dns_sd_mdns_stop(BonjourDnsSd *data)
    -{
    - Win32SessionImplData *idata = data->mdns_impl_data;
    -
    - if (idata == NULL)
    - return;
    -
    - if (idata->presence_query != NULL) {
    - g_source_remove(idata->presence_query->input_handler);
    - DNSServiceRefDeallocate(idata->presence_query->sdRef);
    - g_free(idata->presence_query);
    - }
    -
    - if (idata->browser_query != NULL) {
    - g_source_remove(idata->browser_query->input_handler);
    - DNSServiceRefDeallocate(idata->browser_query->sdRef);
    - g_free(idata->browser_query);
    - }
    -
    - g_free(idata);
    -
    - data->mdns_impl_data = NULL;
    -}
    -
    -static gboolean
    -dns_sd_mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len)
    -{
    - Win32SessionImplData *idata = data->mdns_impl_data;
    - DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
    -
    - g_return_val_if_fail(idata != NULL, FALSE);
    -
    - if (avatar_data != NULL && idata->buddy_icon_rec == NULL) {
    - purple_debug_info("bonjour", "Setting new buddy icon.\n");
    - errorCode = DNSServiceAddRecord(idata->presence_query->sdRef, &idata->buddy_icon_rec,
    - 0, kDNSServiceType_NULL, avatar_len, avatar_data, 0);
    - } else if (avatar_data != NULL) {
    - purple_debug_info("bonjour", "Updating existing buddy icon.\n");
    - errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, idata->buddy_icon_rec,
    - 0, avatar_len, avatar_data, 0);
    - } else if (idata->buddy_icon_rec != NULL) {
    - purple_debug_info("bonjour", "Removing existing buddy icon.\n");
    - errorCode = DNSServiceRemoveRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0);
    - idata->buddy_icon_rec = NULL;
    - }
    -
    - if (errorCode != kDNSServiceErr_NoError) {
    - purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", errorCode);
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -static void
    -dns_sd_mdns_init_buddy(BonjourBuddy *buddy)
    -{
    - buddy->mdns_impl_data = g_new0(Win32BuddyImplData, 1);
    -}
    -
    -static void
    -dns_sd_mdns_delete_buddy(BonjourBuddy *buddy)
    -{
    - Win32BuddyImplData *idata = buddy->mdns_impl_data;
    -
    - g_return_if_fail(idata != NULL);
    -
    - while (idata->resolvers) {
    - Win32SvcResolverData *rd = idata->resolvers->data;
    - _cleanup_resolver_data(rd);
    - idata->resolvers = g_slist_delete_link(idata->resolvers, idata->resolvers);
    - }
    -
    - if (idata->null_query != NULL) {
    - g_source_remove(idata->null_query->input_handler);
    - DNSServiceRefDeallocate(idata->null_query->sdRef);
    - g_free(idata->null_query);
    - }
    -
    - g_free(idata);
    -
    - buddy->mdns_impl_data = NULL;
    -}
    -
    -static void
    -dns_sd_mdns_retrieve_buddy_icon(BonjourBuddy* buddy)
    -{
    - Win32BuddyImplData *idata = buddy->mdns_impl_data;
    - char svc_name[kDNSServiceMaxDomainName];
    -
    - g_return_if_fail(idata != NULL);
    -
    - /* Cancel any existing query */
    - if (idata->null_query != NULL) {
    - g_source_remove(idata->null_query->input_handler);
    - DNSServiceRefDeallocate(idata->null_query->sdRef);
    - g_free(idata->null_query);
    - idata->null_query = NULL;
    - }
    -
    - if (DNSServiceConstructFullName(svc_name, buddy->name, LINK_LOCAL_RECORD_NAME, "local") != 0)
    - purple_debug_error("bonjour", "Unable to construct full name to retrieve buddy icon for %s.\n", buddy->name);
    - else {
    - DNSServiceRef null_query_sr;
    -
    - DNSServiceErrorType errorCode = DNSServiceQueryRecord(&null_query_sr, 0, kDNSServiceInterfaceIndexAny,
    - svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy);
    -
    - if (errorCode == kDNSServiceErr_NoError) {
    - idata->null_query = g_new(DnsSDServiceRefHandlerData, 1);
    -
    - idata->null_query->sdRef = null_query_sr;
    - idata->null_query->account = buddy->account;
    -
    - idata->null_query->input_handler = purple_input_add(DNSServiceRefSockFD(null_query_sr),
    - PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
    - } else
    - purple_debug_error("bonjour", "Unable to query buddy icon record for %s. (%d)\n", buddy->name, errorCode);
    - }
    -}
    -
    -gboolean
    -dns_sd_mdns_available(void)
    -{
    - if (!dns_sd_available()) {
    - return FALSE;
    - }
    -
    - _mdns_init_session = dns_sd_mdns_init_session;
    - _mdns_publish = dns_sd_mdns_publish;
    - _mdns_browse = dns_sd_mdns_browse;
    - _mdns_stop = dns_sd_mdns_stop;
    - _mdns_set_buddy_icon_data = dns_sd_mdns_set_buddy_icon_data;
    - _mdns_init_buddy = dns_sd_mdns_init_buddy;
    - _mdns_delete_buddy = dns_sd_mdns_delete_buddy;
    - _mdns_retrieve_buddy_icon = dns_sd_mdns_retrieve_buddy_icon;
    -
    - return TRUE;
    -}
    --- a/libpurple/protocols/bonjour/mdns_dns_sd.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,25 +0,0 @@
    -/*
    - * Purple - Internet Messaging Library
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#include <glib.h>
    -
    -gboolean dns_sd_mdns_available(void);
    --- a/libpurple/protocols/bonjour/mdns_types.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,46 +0,0 @@
    -/*
    - * 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 Library 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_BONJOUR_MDNS_TYPES_H
    -#define PURPLE_BONJOUR_MDNS_TYPES_H
    -
    -#include <glib.h>
    -
    -#include <purple.h>
    -
    -#define LINK_LOCAL_RECORD_NAME "_presence._tcp."
    -
    -/**
    - * Data to be used by the dns-sd connection.
    - */
    -typedef struct {
    - gpointer mdns_impl_data;
    - PurpleAccount *account;
    - gchar *first;
    - gchar *last;
    - gint port_p2pj;
    - gchar *phsh;
    - gchar *status;
    - gchar *vc;
    - gchar *msg;
    -} BonjourDnsSd;
    -
    -typedef enum {
    - PUBLISH_START,
    - PUBLISH_UPDATE
    -} PublishType;
    -
    -#endif /* PURPLE_BONJOUR_MDNS_TYPES_H */
    --- a/libpurple/protocols/bonjour/mdns_win32.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,40 +0,0 @@
    -/*
    - * Purple - Internet Messaging Library
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#include <purple.h>
    -
    -#include "mdns_common.h"
    -#include "mdns_dns_sd.h"
    -
    -/****************************
    - * mdns_interface functions *
    - ****************************/
    -
    -gboolean
    -mdns_available(void)
    -{
    - if (dns_sd_mdns_available()) {
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    --- a/libpurple/protocols/bonjour/meson.build Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,46 +0,0 @@
    -BONJOUR_SOURCES = [
    - 'bonjour.c',
    - 'bonjour.h',
    - 'buddy.c',
    - 'buddy.h',
    - 'xmpp.c',
    - 'xmpp.h',
    - 'mdns_common.c',
    - 'mdns_common.h',
    - 'mdns_types.h',
    - 'parser.c',
    - 'parser.h',
    - 'bonjour_ft.c',
    - 'bonjour_ft.h'
    -]
    -
    -if IS_WIN32
    - BONJOUR_SOURCES += [
    - 'dns_sd_proxy.c', 'dns_sd_proxy.h', 'mdns_dns_sd.c', 'mdns_dns_sd.h',
    - 'mdns_win32.c'
    - ]
    - gmodule = dependency('gmodule-2.0')
    - bonjour_link_args = ['-lnetapi32']
    -else
    - BONJOUR_SOURCES += ['mdns_avahi.c']
    - gmodule = []
    - bonjour_link_args = []
    -endif
    -
    -if DYNAMIC_BONJOUR
    - bonjour_resources = gnome.compile_resources('bonjourresource',
    - 'resources/bonjour.gresource.xml',
    - source_dir : 'resources',
    - c_name : 'bonjour')
    - BONJOUR_SOURCES += bonjour_resources
    -
    - shared_library('bonjour', BONJOUR_SOURCES,
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-Bonjour"'],
    - gnu_symbol_visibility : 'hidden',
    - link_args : bonjour_link_args,
    - dependencies : [libxml, avahi, libpurple_dep, glib, gmodule, ws2_32],
    - install : true,
    - install_dir : PURPLE_PLUGINDIR)
    -
    - devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir())
    -endif
    --- a/libpurple/protocols/bonjour/parser.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,237 +0,0 @@
    -/*
    - * purple - Bonjour XMPP XML parser stuff
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -#include <purple.h>
    -
    -#include <libxml/parser.h>
    -
    -#include "parser.h"
    -#include "xmpp.h"
    -
    -static gboolean
    -parse_from_attrib_and_find_buddy(BonjourXMPPConversation *bconv, int nb_attributes, const xmlChar **attributes) {
    - int i;
    -
    - /* If the "from" attribute is specified, attach it to the conversation. */
    - for(i=0; i < nb_attributes * 5; i+=5) {
    - if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) {
    - int len = attributes[i+4] - attributes[i+3];
    - bconv->buddy_name = g_strndup((char *)attributes[i+3], len);
    - bonjour_xmpp_conv_match_by_name(bconv);
    -
    - return (PURPLE_IS_CONTACT(bconv->contact));
    - }
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    -bonjour_parser_element_start_libxml(void *user_data,
    - const xmlChar *element_name,
    - const xmlChar *prefix,
    - const xmlChar *namespace,
    - G_GNUC_UNUSED int nb_namespaces,
    - G_GNUC_UNUSED const xmlChar **namespaces,
    - int nb_attributes,
    - G_GNUC_UNUSED int nb_defaulted,
    - const xmlChar **attributes)
    -{
    - BonjourXMPPConversation *bconv = user_data;
    -
    - PurpleXmlNode *node;
    - int i;
    -
    - g_return_if_fail(element_name != NULL);
    -
    - if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
    - if(!bconv->recv_stream_start) {
    - bconv->recv_stream_start = TRUE;
    -
    - if(!PURPLE_IS_CONTACT(bconv->contact)) {
    - parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes);
    - }
    -
    - bonjour_xmpp_stream_started(bconv);
    - }
    - } else {
    -
    - /* If we haven't yet attached a buddy and this isn't "<stream:features />",
    - * try to get a "from" attribute as a last resort to match our buddy. */
    - if(!PURPLE_IS_CONTACT(bconv->contact)
    - && !(prefix && !xmlStrcmp(prefix, (xmlChar*) "stream")
    - && !xmlStrcmp(element_name, (xmlChar*) "features"))
    - && !parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes))
    - {
    - /* We've run out of options for finding who the conversation is from
    - using explicitly specified stuff; see if we can make a good match
    - by using the IP */
    - bonjour_xmpp_conv_match_by_ip(bconv);
    - }
    -
    - if(bconv->current)
    - node = purple_xmlnode_new_child(bconv->current, (const char*) element_name);
    - else
    - node = purple_xmlnode_new((const char*) element_name);
    - purple_xmlnode_set_namespace(node, (const char*) namespace);
    -
    - for(i=0; i < nb_attributes * 5; i+=5) {
    - const char *name = (const char *)attributes[i];
    - const char *prefix = (const char *)attributes[i+1];
    - const char *attrib_ns = (const char *)attributes[i+2];
    - char *txt;
    - int attrib_len = attributes[i+4] - attributes[i+3];
    - char *attrib = g_malloc(attrib_len + 1);
    -
    - memcpy(attrib, attributes[i+3], attrib_len);
    - attrib[attrib_len] = '\0';
    -
    - txt = attrib;
    - attrib = purple_unescape_text(txt);
    - g_free(txt);
    - purple_xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
    - g_free(attrib);
    - }
    -
    - bconv->current = node;
    - }
    -}
    -
    -static void
    -bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
    - G_GNUC_UNUSED const xmlChar *prefix,
    - G_GNUC_UNUSED const xmlChar *namespace)
    -{
    - BonjourXMPPConversation *bconv = user_data;
    -
    - if(!bconv->current) {
    - /* We don't keep a reference to the start stream PurpleXmlNode,
    - * so we have to check for it here to close the conversation */
    - if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
    - /* Asynchronously close the conversation to prevent bonjour_parser_setup()
    - * being called from within this context */
    - async_bonjour_xmpp_close_conversation(bconv);
    - return;
    - }
    -
    - if(bconv->current->parent) {
    - if(!xmlStrcmp((xmlChar*) bconv->current->name, element_name))
    - bconv->current = bconv->current->parent;
    - } else {
    - PurpleXmlNode *packet = bconv->current;
    - bconv->current = NULL;
    - bonjour_xmpp_process_packet(bconv->contact, packet);
    - purple_xmlnode_free(packet);
    - }
    -}
    -
    -static void
    -bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
    -{
    - BonjourXMPPConversation *bconv = user_data;
    -
    - if(!bconv->current)
    - return;
    -
    - if(!text || !text_len)
    - return;
    -
    - purple_xmlnode_insert_data(bconv->current, (const char*) text, text_len);
    -}
    -
    -static void
    -bonjour_parser_structured_error_handler(void *user_data, const xmlError *error)
    -{
    - BonjourXMPPConversation *bconv = user_data;
    -
    - purple_debug_error("bonjour", "XML parser error for BonjourXMPPConversation %p: "
    - "Domain %i, code %i, level %i: %s",
    - bconv,
    - error->domain, error->code, error->level,
    - (error->message ? error->message : "(null)\n"));
    -}
    -
    -static xmlSAXHandler bonjour_parser_libxml = {
    - NULL, /*internalSubset*/
    - NULL, /*isStandalone*/
    - NULL, /*hasInternalSubset*/
    - NULL, /*hasExternalSubset*/
    - NULL, /*resolveEntity*/
    - NULL, /*getEntity*/
    - NULL, /*entityDecl*/
    - NULL, /*notationDecl*/
    - NULL, /*attributeDecl*/
    - NULL, /*elementDecl*/
    - NULL, /*unparsedEntityDecl*/
    - NULL, /*setDocumentLocator*/
    - NULL, /*startDocument*/
    - NULL, /*endDocument*/
    - NULL, /*startElement*/
    - NULL, /*endElement*/
    - NULL, /*reference*/
    - bonjour_parser_element_text_libxml, /*characters*/
    - NULL, /*ignorableWhitespace*/
    - NULL, /*processingInstruction*/
    - NULL, /*comment*/
    - NULL, /*warning*/
    - NULL, /*error*/
    - NULL, /*fatalError*/
    - NULL, /*getParameterEntity*/
    - NULL, /*cdataBlock*/
    - NULL, /*externalSubset*/
    - XML_SAX2_MAGIC, /*initialized*/
    - NULL, /*_private*/
    - bonjour_parser_element_start_libxml, /*startElementNs*/
    - bonjour_parser_element_end_libxml, /*endElementNs*/
    - (xmlStructuredErrorFunc)bonjour_parser_structured_error_handler /*serror*/
    -};
    -
    -void
    -bonjour_parser_setup(BonjourXMPPConversation *bconv)
    -{
    -
    - /* This seems backwards, but it makes sense. The libxml code creates
    - * the parser context when you try to use it (this way, it can figure
    - * out the encoding at creation time. So, setting up the parser is
    - * just a matter of destroying any current parser. */
    - if (bconv->context) {
    - xmlParseChunk(bconv->context, NULL,0,1);
    - xmlFreeParserCtxt(bconv->context);
    - bconv->context = NULL;
    - }
    -}
    -
    -
    -void bonjour_parser_process(BonjourXMPPConversation *bconv, const char *buf, int len)
    -{
    -
    - if (bconv->context == NULL) {
    - /* libxml inconsistently starts parsing on creating the
    - * parser, so do a ParseChunk right afterwards to force it. */
    - bconv->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, bconv, buf, len, NULL);
    - xmlParseChunk(bconv->context, "", 0, 0);
    - } else if (xmlParseChunk(bconv->context, buf, len, 0) < 0)
    - /* TODO: What should we do here - I assume we should display an error or something (maybe just print something to the conv?) */
    - purple_debug_error("bonjour", "Error parsing xml.\n");
    -
    -}
    -
    --- a/libpurple/protocols/bonjour/parser.h Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -/**
    - * @file parser.h Bonjour XMPP XML parser functions
    - *
    - * purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#ifndef PURPLE_BONJOUR_PARSER_H
    -#define PURPLE_BONJOUR_PARSER_H
    -
    -#include "buddy.h"
    -#include "xmpp.h"
    -
    -void bonjour_parser_setup(BonjourXMPPConversation *bconv);
    -void bonjour_parser_process(BonjourXMPPConversation *bconv, const char *buf, int len);
    -
    -#endif /* PURPLE_BONJOUR_PARSER_H */
    --- a/libpurple/protocols/bonjour/resources/bonjour.gresource.xml Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<gresources>
    - <gresource prefix="/im/pidgin/libpurple/bonjour">
    - <file>icons/16x16/apps/im-bonjour.png</file>
    - <file>icons/16x16/apps/scalable/im-bonjour.svg</file>
    - <file>icons/22x22/apps/im-bonjour.png</file>
    - <file>icons/22x22/apps/scalable/im-bonjour.svg</file>
    - <file>icons/48x48/apps/im-bonjour.png</file>
    - <file>icons/scalable/apps/im-bonjour.svg</file>
    - </gresource>
    -</gresources>
    Binary file libpurple/protocols/bonjour/resources/icons/16x16/apps/im-bonjour.png has changed
    --- a/libpurple/protocols/bonjour/resources/icons/16x16/apps/scalable/im-bonjour.svg Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,189 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="16px"
    - height="16px"
    - id="svg4239"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/protocols/16/scalable"
    - sodipodi:docname="bonjour.svg"
    - inkscape:export-filename="/home/hbons/Bureaublad/bonjour.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4241">
    - <inkscape:perspective
    - sodipodi:type="inkscape:persp3d"
    - inkscape:vp_x="0 : 8 : 1"
    - inkscape:vp_y="0 : 1000 : 0"
    - inkscape:vp_z="16 : 8 : 1"
    - inkscape:persp3d-origin="8 : 5.3333333 : 1"
    - id="perspective24" />
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8630">
    - <stop
    - style="stop-color:#ce5c00;stop-opacity:1;"
    - offset="0"
    - id="stop8632" />
    - <stop
    - style="stop-color:#a24700;stop-opacity:1"
    - offset="1"
    - id="stop8634" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8630"
    - id="linearGradient8636"
    - x1="5.3974791"
    - y1="2.3890932"
    - x2="7.286067"
    - y2="5.4647174"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8630"
    - id="linearGradient8640"
    - gradientUnits="userSpaceOnUse"
    - x1="5.3974791"
    - y1="2.3890932"
    - x2="7.286067"
    - y2="5.4647174" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8630"
    - id="linearGradient8644"
    - gradientUnits="userSpaceOnUse"
    - x1="5.3974791"
    - y1="2.3890932"
    - x2="7.286067"
    - y2="5.4647174" />
    - <filter
    - inkscape:collect="always"
    - id="filter8686">
    - <feGaussianBlur
    - inkscape:collect="always"
    - stdDeviation="0.44714331"
    - id="feGaussianBlur8688" />
    - </filter>
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="22.197802"
    - inkscape:cx="19.610287"
    - inkscape:cy="10.016761"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - inkscape:window-width="1440"
    - inkscape:window-height="847"
    - inkscape:window-x="0"
    - inkscape:window-y="0"
    - inkscape:grid-points="true"
    - inkscape:snap-nodes="false"
    - inkscape:snap-bbox="true"
    - objecttolerance="10"
    - gridtolerance="10">
    - <inkscape:grid
    - type="xygrid"
    - id="grid7858"
    - visible="true"
    - enabled="true" />
    - </sodipodi:namedview>
    - <metadata
    - id="metadata4244">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <path
    - style="opacity:1;fill:#888a85;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - d="M 12.25 0.8125 C 11.212904 0.82548147 9.8237981 1.3178148 8 2.75 C 13.624569 0.9376896 12.09375 3.9687501 12.09375 3.96875 C 12.730846 2.6945576 15.125 3.1250001 15.125 3.125 C 15.125 3.125 14.531611 0.78394076 12.25 0.8125 z M 5 1 C 5 1 6.2894977 2.4031671 5.96875 3.875 C 6.2142693 3.9629227 6.4825344 4.1298951 6.75 4.3125 C 6.3025525 4.8585397 4.5708564 7.0310338 3.59375 9.53125 C 1.0655137 6.5798281 2.28125 6.2187501 2.28125 6.21875 C 2.28125 6.21875 0.78185481 5.3348778 0.75 4.15625 C 0.75 4.15625 -2.0476392 7.5220008 3.5 9.78125 C 3.0973823 10.886071 2.8800606 12.018686 3.0625 13 C 3.0625 13 3.6157102 11.426301 5.03125 10.96875 C 4.9701968 10.621698 4.9938478 10.192279 5.0625 9.71875 C 5.3532166 7.7135288 6.5068895 5.1127489 6.84375 4.375 C 8.4782617 5.5373726 10.241354 7.9344916 10.75 8.65625 C 8.5490333 9.3974261 5.2127054 9.7062603 5.0625 9.71875 C 5.2473055 9.7481019 12.134313 10.824709 14.9375 8.625 C 14.9375 8.625 13.27052 8.9201184 12.15625 8 C 11.847198 8.2378763 11.367484 8.4425412 10.84375 8.625 C 10.458227 7.6220151 8.2341467 2.1234583 5 1 z M 11 10 C 10.948461 14.034482 8.8749996 13.6875 8.875 13.6875 C 8.875 13.6875 9.2741924 15.554033 8 16 C 8 16 14.4206 15.813404 11 10 z "
    - id="path8669" />
    - <path
    - sodipodi:type="arc"
    - style="fill:#ffac5b;fill-opacity:1;stroke:url(#linearGradient8636);stroke-width:1.5861057;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3210"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077,3.7607069 A 2.7512043,2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043,2.0574222 0 1 1 8.421077,3.7607069 z"
    - transform="matrix(0.5452158,0,0,0.7290669,-9.1304146e-2,0.2581915)" />
    - <path
    - sodipodi:type="arc"
    - style="fill:#ffac5b;fill-opacity:1;stroke:url(#linearGradient8640);stroke-width:1.58610499;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path8638"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077,3.7607069 A 2.7512043,2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043,2.0574222 0 1 1 8.421077,3.7607069 z"
    - transform="matrix(0.5452157,0,0,0.7290676,10.908697,3.2581906)" />
    - <path
    - sodipodi:type="arc"
    - style="fill:#ffac5b;fill-opacity:1;stroke:url(#linearGradient8644);stroke-width:1.58610499;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path8642"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077,3.7607069 A 2.7512043,2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043,2.0574222 0 1 1 8.421077,3.7607069 z"
    - transform="matrix(0.5452157,0,0,0.7290676,2.9086961,11.25819)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.47685185;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="path8682"
    - sodipodi:cx="2.3731835"
    - sodipodi:cy="2.1909397"
    - sodipodi:rx="0.90786207"
    - sodipodi:ry="1.0352813"
    - d="M 3.2810456,2.1909397 A 0.90786207,1.0352813 0 1 1 1.4653214,2.1909397 A 0.90786207,1.0352813 0 1 1 3.2810456,2.1909397 z"
    - transform="matrix(1.1014889,0,0,0.9659211,-0.6140352,-0.1162747)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.47685188;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="path8684"
    - sodipodi:cx="2.3731835"
    - sodipodi:cy="2.1909397"
    - sodipodi:rx="0.90786207"
    - sodipodi:ry="1.0352813"
    - d="M 3.2810456,2.1909397 A 0.90786207,1.0352813 0 1 1 1.4653214,2.1909397 A 0.90786207,1.0352813 0 1 1 3.2810456,2.1909397 z"
    - transform="matrix(1.1014889,0,0,0.9659211,10.385965,2.8837252)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.47685188;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="path8686"
    - sodipodi:cx="2.3731835"
    - sodipodi:cy="2.1909397"
    - sodipodi:rx="0.90786207"
    - sodipodi:ry="1.0352813"
    - d="M 3.2810456,2.1909397 A 0.90786207,1.0352813 0 1 1 1.4653214,2.1909397 A 0.90786207,1.0352813 0 1 1 3.2810456,2.1909397 z"
    - transform="matrix(1.1014889,0,0,0.9659211,2.3859647,10.883725)" />
    - </g>
    -</svg>
    Binary file libpurple/protocols/bonjour/resources/icons/22x22/apps/im-bonjour.png has changed
    --- a/libpurple/protocols/bonjour/resources/icons/22x22/apps/scalable/im-bonjour.svg Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,198 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="24"
    - height="24"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols"
    - sodipodi:docname="bonjour.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/bonjour.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="24.007726"
    - inkscape:cx="19.418659"
    - inkscape:cy="14.593926"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#babdb6"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <image
    - id="image2218"
    - height="24.071428"
    - width="23.131142"
    - sodipodi:absref="/home/hbons/Desktop/indextop20050412.jpg"
    - xlink:href="/home/hbons/Desktop/indextop20050412.jpg"
    - x="0"
    - y="1.0807102"
    - style="opacity:0" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.35;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(1.805894,0,0,1.478326,-7.410926,5.0402)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:1.18495774;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3210"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077 3.7607069 A 2.7512043 2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043 2.0574222 0 1 1 8.421077 3.7607069 z"
    - transform="matrix(0.728546,0,0,0.977547,0.373616,1.964643)" />
    - <path
    - style="fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 26.5,4.3717336 C 30.06901,-1.9135499 35.080181,2.3148691 35.080181,5.6521385 L 32.664416,4.7456069 C 31.625041,2.6021767 26.5,4.3717336 26.5,4.3717336 z "
    - id="rect3251"
    - sodipodi:nodetypes="cccc" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:1.18495774;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3265"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077 3.7607069 A 2.7512043 2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043 2.0574222 0 1 1 8.421077 3.7607069 z"
    - transform="matrix(0.728546,0,0,0.977547,16.36486,1.964643)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:1.18495774;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3267"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077 3.7607069 A 2.7512043 2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043 2.0574222 0 1 1 8.421077 3.7607069 z"
    - transform="matrix(0.728546,0,0,0.977547,8.383099,14.96465)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.35;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.36390281;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3278"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077 3.7607069 A 2.7512043 2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043 2.0574222 0 1 1 8.421077 3.7607069 z"
    - transform="matrix(0.3652,0,0,0.490017,2.442502,3.775647)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.35;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.36390281;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3280"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077 3.7607069 A 2.7512043 2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043 2.0574222 0 1 1 8.421077 3.7607069 z"
    - transform="matrix(0.3652,0,0,0.490017,18.41586,3.801155)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.35;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.36390281;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3282"
    - sodipodi:cx="5.6698728"
    - sodipodi:cy="3.7607069"
    - sodipodi:rx="2.7512043"
    - sodipodi:ry="2.0574222"
    - d="M 8.421077 3.7607069 A 2.7512043 2.0574222 0 1 1 2.9186685,3.7607069 A 2.7512043 2.0574222 0 1 1 8.421077 3.7607069 z"
    - transform="matrix(0.3652,0,0,0.490017,10.4341,16.79505)" />
    - <path
    - style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:0.99999976;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.7318759,2.7396133 C 12.169337,3.9286259 14.955511,8.0423718 15.280153,9.2539565 C 14.057144,8.5478522 8.4371398,5.666617 8.4371398,5.666617 L 7.7318759,2.7396133 z "
    - id="rect3098"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:0.99999893;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 8.2402219,17.350151 C 6.3014525,13.204266 8.7140226,9.106829 9.6009641,8.2198893 C 9.6009631,9.6320977 10.392827,15.689612 10.392827,15.689612 L 8.2402219,17.350151 z "
    - id="path3101"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:0.99999946;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 20.999419,10.287708 C 17.750972,13.53615 13.261202,14.092622 12.049619,13.767978 C 13.272629,13.061875 18.044934,9.5274806 18.044934,9.5274806 L 20.999419,10.287708 z "
    - id="path3103"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:0.99999946;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 1.5549628,8.708934 C 1.5526429,11.954488 4.7660086,13.487253 6.3046441,13.487253 C 5.4626496,12.028876 3.7044795,9.4471457 3.7044795,9.4471457 L 1.5549628,8.708934 z "
    - id="path3105"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:0.99999946;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 19.83722,1.3615728 C 16.493168,0.56581196 13.018,2.1889619 12.593131,3.5931305 C 14.361353,3.5931305 16.849492,3.4926544 16.849492,3.4926544 L 19.83722,1.3615728 z "
    - id="path3107"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16.5,20.072196 C 19.311894,18.451427 18.826975,15.382116 18.057657,14.049618 C 17.215662,15.507995 16.53488,17.120673 16.53488,17.120673 L 16.5,20.072196 z "
    - id="path3109"
    - sodipodi:nodetypes="cccc" />
    - </g>
    -</svg>
    Binary file libpurple/protocols/bonjour/resources/icons/48x48/apps/im-bonjour.png has changed
    --- a/libpurple/protocols/bonjour/resources/icons/scalable/apps/im-bonjour.svg Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,207 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="48"
    - height="48"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/48/scalable"
    - sodipodi:docname="bonjour.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/48/bonjour.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2314">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop2316" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop2318" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.934099,4.045903e-17,-5.060684e-9,-0.395926,20.78896,14.59809)"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2314"
    - id="linearGradient2320"
    - x1="11.237947"
    - y1="2.7057509"
    - x2="11.237947"
    - y2="10.98068"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2314"
    - id="linearGradient2324"
    - gradientUnits="userSpaceOnUse"
    - x1="11.237947"
    - y1="2.6204424"
    - x2="11.237947"
    - y2="10.810062" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2314"
    - id="linearGradient2328"
    - gradientUnits="userSpaceOnUse"
    - x1="11.237947"
    - y1="2.876368"
    - x2="11.237947"
    - y2="10.89537" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="12.003863"
    - inkscape:cx="41.170813"
    - inkscape:cy="23.799748"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#f57900"
    - inkscape:window-width="1268"
    - inkscape:window-height="972"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.35;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(3.611787,0,0,2.323081,-14.82185,18.20605)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#f57900;fill-opacity:1;fill-rule:evenodd;stroke:#ce5c00;stroke-width:0.77285618;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path1341"
    - sodipodi:cx="11.237947"
    - sodipodi:cy="7.9095716"
    - sodipodi:rx="3.0935922"
    - sodipodi:ry="3.0935922"
    - d="M 14.33154 7.9095716 A 3.0935922 3.0935922 0 1 1 8.1443553,7.9095716 A 3.0935922 3.0935922 0 1 1 14.33154 7.9095716 z"
    - transform="matrix(1.290432,0,0,1.297381,-3.009737,-2.680289)" />
    - <path
    - style="fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:#7d7f7a;stroke-width:0.99999958;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 35.46875,4.5366208 C 31.635379,4.6522391 27.705058,6.4906706 25.53125,9.7866208 C 26.103022,9.4743488 35.303923,4.6093611 38.34375,12.599121 C 39.472527,11.34018 43.22049,11.040461 45.5,12.224121 C 44.713989,6.8257933 40.179035,4.3945539 35.46875,4.5366208 z M 17.46875,4.5678708 C 18.127573,6.5529792 17.95859,8.1732436 17.53125,9.1616208 C 21.771815,8.0014671 29.858933,21.430311 30.5,22.505371 C 25.376692,11.592709 20.897066,5.8681195 17.46875,4.5678708 z M 5.34375,7.5678708 C -3.2027906,14.070713 3.9432311,26.177264 13.4375,26.536621 C 12.842607,26.198616 3.4390694,20.691845 9.4375,13.724121 C 7.6531589,13.401091 5.4098365,10.216725 5.34375,7.5678708 z M 21.46875,14.599121 C 14.518079,24.752705 11.892887,31.742633 12.65625,35.567871 C 14.072321,33.976734 15.618903,33.346912 16.75,33.255371 C 13.282151,29.863924 20.985214,15.493469 21.46875,14.599121 z M 37.40625,21.630371 C 36.029184,26.189083 21.006896,27.252123 18.78125,27.380371 C 31.047662,28.025568 38.491655,26.618231 41.5,23.942871 C 39.411924,23.554843 38.044876,22.56197 37.40625,21.630371 z M 33.125,28.599121 C 33.123038,29.265224 33.537967,40.230123 24.458146,38.651097 C 25.058081,40.306995 23.321409,43.5863 21.031089,44.953234 C 30.971079,48.901331 37.527529,36.737136 33.125,28.599121 z "
    - id="rect2206"
    - sodipodi:nodetypes="ccccccccccccccccccccccccc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.97818917"
    - inkscape:original="M 35.46875 4.53125 C 31.635379 4.6468685 27.705058 6.4852998 25.53125 9.78125 C 26.103022 9.4689777 35.303923 4.6039901 38.34375 12.59375 C 39.472527 11.334809 43.22049 11.03509 45.5 12.21875 C 44.713989 6.8204222 40.179035 4.3891831 35.46875 4.53125 z M 17.46875 4.5625 C 18.127573 6.5476086 17.95859 8.1678728 17.53125 9.15625 C 21.771815 7.996096 29.858933 21.42494 30.5 22.5 C 25.376692 11.587338 20.897066 5.8627487 17.46875 4.5625 z M 5.34375 7.5625 C -3.2027906 14.065342 3.9432311 26.171893 13.4375 26.53125 C 12.842607 26.193245 3.4390694 20.686474 9.4375 13.71875 C 7.6531589 13.39572 5.4098365 10.211354 5.34375 7.5625 z M 21.46875 14.59375 C 14.518079 24.747334 11.892887 31.737262 12.65625 35.5625 C 14.072321 33.971363 15.618903 33.341541 16.75 33.25 C 13.282151 29.858553 20.985214 15.488098 21.46875 14.59375 z M 37.40625 21.625 C 36.029184 26.183712 21.006896 27.246752 18.78125 27.375 C 31.047662 28.020197 38.491655 26.61286 41.5 23.9375 C 39.411924 23.549472 38.044876 22.556599 37.40625 21.625 z M 33.125 28.59375 C 33.123038 29.259853 33.548571 40.235276 24.46875 38.65625 C 25.068686 40.312149 23.32157 43.601816 21.03125 44.96875 C 30.971239 48.916845 37.527529 36.731765 33.125 28.59375 z "
    - xlink:href="#rect2206"
    - style="opacity:0.4;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99999958;stroke-miterlimit:4;stroke-opacity:1"
    - id="path2265"
    - d="M 35.5,6.4375 C 33.368939,6.5017749 31.254587,7.2183109 29.4375,8.375 C 29.955273,8.2616185 30.342793,8.0837857 30.90625,8.03125 C 32.406836,7.8913382 34.078294,8.0394993 35.59375,8.8125 C 36.831972,9.4440897 37.86888,10.628822 38.6875,12.1875 C 39.489624,11.770011 40.411361,11.500994 41.4375,11.4375 C 42.258666,11.386689 43.10461,11.462473 43.9375,11.625 C 43.379009,10.119471 42.589365,8.8531272 41.40625,8.03125 C 39.790488,6.908825 37.677617,6.3718209 35.5,6.4375 z M 18.78125,7.53125 C 18.821476,8.1885616 18.763233,8.7406401 18.65625,9.28125 C 19.760339,9.4560073 20.842148,9.9390765 21.875,10.78125 C 22.166416,11.018866 22.428639,11.416981 22.71875,11.6875 C 22.66814,11.618108 22.612837,11.505519 22.5625,11.4375 C 21.155108,9.5357202 19.912201,8.3834952 18.78125,7.53125 z M 4.78125,10.5 C 2.1367338,13.32142 1.9732812,16.73063 3.5,19.84375 C 4.6796069,22.249077 6.8918614,24.272293 9.5,25.4375 C 9.0508422,24.984485 8.5946267,24.68144 8.1875,24.125 C 7.2714393,22.872975 6.524154,21.350596 6.46875,19.59375 C 6.4232564,18.151162 7.0064835,16.584718 8.0625,15.03125 C 7.2533732,14.545104 6.5354947,13.881423 5.9375,13 C 5.4271977,12.247833 5.0629652,11.390597 4.78125,10.5 z M 37.5625,24 C 36.85358,24.922553 35.872279,25.684009 34.625,26.25 C 33.981881,26.541835 33.133881,26.670217 32.40625,26.90625 C 35.442669,26.486299 37.633021,25.855821 39.1875,25.0625 C 38.533201,24.753288 38.014785,24.406514 37.5625,24 z M 15.59375,27.6875 C 15.447519,28.018151 15.191854,28.433804 15.0625,28.75 C 14.093097,31.119641 13.782747,32.928132 13.6875,34.4375 C 14.296929,34.034852 14.855783,33.77311 15.4375,33.59375 C 14.976511,32.494336 14.815677,31.243494 15,29.84375 C 15.08934,29.165305 15.417606,28.406637 15.59375,27.6875 z M 33.3125,33.78125 C 33.133192,34.418356 33.085183,34.999527 32.78125,35.65625 C 32.13654,37.049308 31.160565,38.398872 29.65625,39.3125 C 28.414521,40.06665 26.766693,40.371954 24.875,40.25 C 24.843897,41.165669 24.622124,42.10692 24.15625,43.03125 C 23.766181,43.805178 23.234663,44.527693 22.625,45.1875 C 26.385249,45.969725 29.45049,44.399 31.40625,41.59375 C 32.935546,39.400201 33.59997,36.546168 33.3125,33.78125 z "
    - inkscape:href="#rect2206" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#f57900;fill-opacity:1;fill-rule:evenodd;stroke:#ce5c00;stroke-width:0.77265418;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2216"
    - sodipodi:cx="11.237947"
    - sodipodi:cy="7.9095716"
    - sodipodi:rx="3.0935922"
    - sodipodi:ry="3.0935922"
    - d="M 14.33154 7.9095716 A 3.0935922 3.0935922 0 1 1 8.1443553,7.9095716 A 3.0935922 3.0935922 0 1 1 14.33154 7.9095716 z"
    - transform="matrix(-0.645216,1.118132,-1.123564,-0.649031,58.6294,10.13592)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#f57900;fill-opacity:1;fill-rule:evenodd;stroke:#ce5c00;stroke-width:0.77285618;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2222"
    - sodipodi:cx="11.237947"
    - sodipodi:cy="7.9095716"
    - sodipodi:rx="3.0935922"
    - sodipodi:ry="3.0935922"
    - d="M 14.33154 7.9095716 A 3.0935922 3.0935922 0 1 1 8.1443553,7.9095716 A 3.0935922 3.0935922 0 1 1 14.33154 7.9095716 z"
    - transform="matrix(-0.645216,-1.117547,1.123565,-0.64869,15.8724,57.25531)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.6;fill:url(#linearGradient2320);fill-opacity:1.0;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.02678573;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2228"
    - sodipodi:cx="11.237947"
    - sodipodi:cy="7.9095716"
    - sodipodi:rx="3.0935922"
    - sodipodi:ry="3.0935922"
    - d="M 14.33154 7.9095716 A 3.0935922 3.0935922 0 1 1 8.1443553,7.9095716 A 3.0935922 3.0935922 0 1 1 14.33154 7.9095716 z"
    - transform="matrix(0.971301,0,0,0.976532,0.576636,-0.142509)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.6;fill:url(#linearGradient2324);fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.02678573;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2322"
    - sodipodi:cx="11.237947"
    - sodipodi:cy="7.9095716"
    - sodipodi:rx="3.0935922"
    - sodipodi:ry="3.0935922"
    - d="M 14.33154 7.9095716 A 3.0935922 3.0935922 0 1 1 8.1443553,7.9095716 A 3.0935922 3.0935922 0 1 1 14.33154 7.9095716 z"
    - transform="matrix(0.971301,0,0,0.976532,31.58938,9.755058)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.6;fill:url(#linearGradient2328);fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.02678573;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2326"
    - sodipodi:cx="11.237947"
    - sodipodi:cy="7.9095716"
    - sodipodi:rx="3.0935922"
    - sodipodi:ry="3.0935922"
    - d="M 14.33154 7.9095716 A 3.0935922 3.0935922 0 1 1 8.1443553,7.9095716 A 3.0935922 3.0935922 0 1 1 14.33154 7.9095716 z"
    - transform="matrix(0.971301,0,0,0.976532,6.579761,31.79704)" />
    - </g>
    -</svg>
    --- a/libpurple/protocols/bonjour/xmpp.c Tue Apr 09 23:11:12 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1423 +0,0 @@
    -/*
    - * purple - Bonjour Protocol Plugin
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#include <sys/types.h>
    -
    -#include <glib.h>
    -#ifdef HAVE_UNISTD_H
    -#include <unistd.h>
    -#endif
    -
    -#include "xmpp.h"
    -#include "parser.h"
    -#include "bonjour.h"
    -#include "buddy.h"
    -#include "bonjour_ft.h"
    -
    -#define STREAM_END "</stream:stream>"
    -/* TODO: specify version='1.0' and send stream features */
    -#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
    - "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
    -
    -enum sent_stream_start_types {
    - NOT_SENT = 0,
    - PARTIALLY_SENT = 1,
    - FULLY_SENT = 2
    -};
    -
    -static void
    -xep_iq_parse(PurpleXmlNode *packet, PurpleContact *contact);
    -
    -static BonjourXMPPConversation *
    -bonjour_xmpp_conv_new(PurpleContact *contact, PurpleAccount *account,
    - const char *ip)
    -{
    -
    - BonjourXMPPConversation *bconv = g_new0(BonjourXMPPConversation, 1);
    - bconv->cancellable = g_cancellable_new();
    - bconv->tx_buf = purple_circular_buffer_new(512);
    - bconv->tx_handler = 0;
    - bconv->rx_handler = 0;
    - bconv->contact = contact;
    - bconv->account = account;
    - bconv->ip = g_strdup(ip);
    -
    - bonjour_parser_setup(bconv);
    -
    - return bconv;
    -}
    -
    -static const char *
    -_font_size_ichat_to_purple(int size)
    -{
    - if (size > 24) {
    - return "7";
    - } else if (size >= 21) {
    - return "6";
    - } else if (size >= 17) {
    - return "5";
    - } else if (size >= 14) {
    - return "4";
    - } else if (size >= 12) {
    - return "3";
    - } else if (size >= 10) {
    - return "2";
    - }
    -
    - return "1";
    -}
    -
    -static gchar *
    -get_xmlnode_contents(PurpleXmlNode *node)
    -{
    - gchar *contents;
    -
    - contents = purple_xmlnode_to_str(node, NULL);
    -
    - /* we just want the stuff inside <font></font>
    - * There isn't stuff exposed in PurpleXmlNode.c to do this more cleanly. */
    -
    - if (contents) {
    - char *bodystart = strchr(contents, '>');
    - char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL;
    - if (bodystart && bodyend && (bodystart + 1) != bodyend) {
    - *bodyend = '\0';
    - memmove(contents, bodystart + 1, (bodyend - bodystart));
    - }
    - }
    -
    - return contents;
    -}
    -
    -static void
    -_xmpp_parse_and_write_message_to_ui(PurpleXmlNode *message_node,
    - PurpleContact *contact)
    -{
    - PurpleXmlNode *body_node, *html_node, *events_node;
    - PurpleAccount *account = NULL;
    - PurpleConnection *gc = NULL;
    - gchar *body = NULL;
    - const char *username = NULL;
    -
    - account = purple_contact_get_account(contact);
    - gc = purple_account_get_connection(account);
    -
    - body_node = purple_xmlnode_get_child(message_node, "body");
    - html_node = purple_xmlnode_get_child(message_node, "html");
    -
    - if (body_node == NULL && html_node == NULL) {
    - purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
    - return;
    - }
    -
    - events_node = purple_xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
    - if (events_node != NULL) {
    - if (purple_xmlnode_get_child(events_node, "id") != NULL) {
    - /* The user is just typing */
    - /* TODO: Deal with typing notification */
    - return;
    - }
    - }
    -
    - if (html_node != NULL) {
    - PurpleXmlNode *html_body_node;
    -
    - html_body_node = purple_xmlnode_get_child(html_node, "body");
    - if (html_body_node != NULL) {
    - PurpleXmlNode *html_body_font_node;
    -
    - html_body_font_node = purple_xmlnode_get_child(html_body_node, "font");
    - /* Types of messages sent by iChat */
    - if (html_body_font_node != NULL) {
    - gchar *html_body;
    - const char *font_face, *font_size, *font_color,
    - *ichat_balloon_color, *ichat_text_color;
    -
    - font_face = purple_xmlnode_get_attrib(html_body_font_node, "face");
    - /* The absolute iChat font sizes should be converted to 1..7 range */
    - font_size = purple_xmlnode_get_attrib(html_body_font_node, "ABSZ");
    - if (font_size != NULL)
    - font_size = _font_size_ichat_to_purple(atoi(font_size));
    - font_color = purple_xmlnode_get_attrib(html_body_font_node, "color");
    - ichat_balloon_color = purple_xmlnode_get_attrib(html_body_node, "ichatballooncolor");
    - ichat_text_color = purple_xmlnode_get_attrib(html_body_node, "ichattextcolor");
    -
    - html_body = get_xmlnode_contents(html_body_font_node);
    -
    - if (html_body == NULL)
    - /* This is the kind of formatted messages that Purple creates */
    - html_body = purple_xmlnode_to_str(html_body_font_node, NULL);
    -
    - if (html_body != NULL) {
    - GString *str = g_string_new("<font");
    -
    - if (font_face)
    - g_string_append_printf(str, " face='%s'", font_face);
    - if (font_size)
    - g_string_append_printf(str, " size='%s'", font_size);
    - if (font_color)
    - g_string_append_printf(str, " color='%s'", font_color);
    - else if (ichat_text_color)
    - g_string_append_printf(str, " color='%s'", ichat_text_color);
    - if (ichat_balloon_color)
    - g_string_append_printf(str, " back='%s'", ichat_balloon_color);
    - g_string_append_printf(str, ">%s</font>", html_body);
    -
    - body = g_string_free(str, FALSE);
    -
    - g_free(html_body);
    - }
    - }
    - }
    - }
    -
    - /* Compose the message */
    - if (body == NULL && body_node != NULL)
    - body = purple_xmlnode_get_data(body_node);
    -
    - if (body == NULL) {
    - purple_debug_error("bonjour", "No html body or regular body found.\n");
    - return;
    - }
    -
    - /* Send the message to the UI */
    - username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact));
    - purple_serv_got_im(gc, username, body, 0, time(NULL));
    -
    - g_free(body);
    -}
    -
    -static GSList *
    -_find_match_buddies_by_address(const BonjourXMPP *jdata, const char *address)
    -{
    - PurpleContactManager *manager = NULL;
    - GListModel *contacts = NULL;
    - GSList *ret = NULL;
    -
    - manager = purple_contact_manager_get_default();
    - contacts = purple_contact_manager_get_all(manager, jdata->account);
    - for(guint i = 0; i < g_list_model_get_n_items(contacts); i++) {
    - PurpleContact *contact = NULL;
    - BonjourBuddy *bb = NULL;
    -
    - contact = g_list_model_get_item(contacts, i);
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - if(bb != NULL) {
    - if(g_slist_find_custom(bb->ips, address,
    - (GCompareFunc)g_ascii_strcasecmp))
    - {
    - ret = g_slist_prepend(ret, contact);
    - }
    - }
    -
    - g_clear_object(&contact);
    - }
    -
    - return ret;
    -}
    -
    -static void
    -_send_data_write_cb(GObject *stream, gpointer data)
    -{
    - PurpleContact *contact = data;
    - BonjourBuddy *bb = NULL;
    - BonjourXMPPConversation *bconv = NULL;
    - gsize writelen;
    - gssize ret;
    - GError *error = NULL;
    -
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - bconv = bb->conversation;
    - writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
    -
    - if (writelen == 0) {
    - g_clear_handle_id(&bconv->tx_handler, g_source_remove);
    - return;
    - }
    -
    - ret = g_pollable_output_stream_write_nonblocking(
    - G_POLLABLE_OUTPUT_STREAM(stream),
    - purple_circular_buffer_get_output(bconv->tx_buf), writelen,
    - bconv->cancellable, &error);
    -
    - if (ret < 0 && error->code == G_IO_ERROR_WOULD_BLOCK) {
    - g_clear_error(&error);
    - return;
    - } else if (ret <= 0) {
    - PurpleAccount *account = NULL;
    - PurpleConversation *conv = NULL;
    - PurpleConversationManager *manager = NULL;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - purple_debug_error(
    - "bonjour",
    - "Error sending message to buddy %s error: %s",
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact)),
    - error ? error->message : "(null)");
    -
    - account = purple_contact_get_account(contact);
    -
    - conv = purple_conversation_manager_find_im(manager, account, bb->name);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send message."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - bonjour_xmpp_close_conversation(bb->conversation);
    - bb->conversation = NULL;
    - g_clear_error(&error);
    -
    - return;
    - }
    -
    - purple_circular_buffer_mark_read(bconv->tx_buf, ret);
    -}
    -
    -static gint
    -_send_data(PurpleContact *contact, char *message)
    -{
    - BonjourBuddy *bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - BonjourXMPPConversation *bconv = bb->conversation;
    - gsize len = strlen(message);
    - gssize ret;
    - GError *error = NULL;
    -
    - /* If we're not ready to actually send, append it to the buffer */
    - if (bconv->tx_handler != 0
    - || bconv->sent_stream_start != FULLY_SENT
    - || !bconv->recv_stream_start
    - || purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
    - ret = -1;
    - g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
    - "Not yet ready to send.");
    - } else {
    - ret = g_pollable_output_stream_write_nonblocking(
    - G_POLLABLE_OUTPUT_STREAM(bconv->output), message, len,
    - bconv->cancellable, &error);
    - }
    -
    - if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
    - ret = 0;
    - g_clear_error(&error);
    - } else if (ret <= 0) {
    - PurpleAccount *account;
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - purple_debug_error(
    - "bonjour",
    - "Error sending message to buddy %s error: %s",
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact)),
    - error ? error->message : "(null)");
    -
    - account = purple_contact_get_account(contact);
    -
    - conv = purple_conversation_manager_find_im(manager, account, bb->name);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send message."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - bonjour_xmpp_close_conversation(bb->conversation);
    - bb->conversation = NULL;
    - g_clear_error(&error);
    - return -1;
    - }
    -
    - if ((gsize)ret < len) {
    - /* Don't interfere with the stream starting */
    - if (bconv->sent_stream_start == FULLY_SENT &&
    - bconv->recv_stream_start && bconv->tx_handler == 0) {
    - GSource *source =
    - g_pollable_output_stream_create_source(
    - G_POLLABLE_OUTPUT_STREAM(bconv->output),
    - bconv->cancellable);
    - g_source_set_callback(source,
    - G_SOURCE_FUNC(_send_data_write_cb),
    - g_object_ref(contact), g_object_unref);
    - bconv->tx_handler = g_source_attach(source, NULL);
    - g_source_unref(source);
    - }
    - purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
    - }
    -
    - return ret;
    -}
    -
    -void
    -bonjour_xmpp_process_packet(PurpleContact *contact, PurpleXmlNode *packet) {
    - g_return_if_fail(PURPLE_IS_CONTACT(contact));
    - g_return_if_fail(packet != NULL);
    -
    - if (purple_strequal(packet->name, "message")) {
    - _xmpp_parse_and_write_message_to_ui(packet, contact);
    - } else if (purple_strequal(packet->name, "iq")) {
    - xep_iq_parse(packet, contact);
    - } else {
    - purple_debug_warning("bonjour", "Unknown packet: %s\n",
    - packet->name ? packet->name : "(null)");
    - }
    -}
    -
    -static void
    -bonjour_xmpp_stream_ended(BonjourXMPPConversation *bconv) {
    - /* Inform the user that the conversation has been closed */
    - BonjourBuddy *bb = NULL;
    - const gchar *name = "(unknown)";
    -
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(bconv->contact);
    -
    - name = purple_contact_info_get_username(info);
    - bb = g_object_get_data(G_OBJECT(bconv->contact), "bonjour-buddy");
    - }
    -
    - purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
    -
    - /* Close the socket, clear the watcher and free memory */
    - bonjour_xmpp_close_conversation(bconv);
    - if(bb != NULL) {
    - bb->conversation = NULL;
    - }
    -}
    -
    -static gboolean
    -_client_socket_handler(GObject *stream, gpointer data)
    -{
    - BonjourXMPPConversation *bconv = data;
    - GError *error = NULL;
    - gssize len;
    - static char message[4096];
    -
    - /* Read the data from the socket */
    - len = g_pollable_input_stream_read_nonblocking(
    - G_POLLABLE_INPUT_STREAM(stream), message, sizeof(message) - 1,
    - bconv->cancellable, &error);
    - if (len == -1) {
    - /* There has been an error reading from the socket */
    - if (error == NULL || (error->code != G_IO_ERROR_WOULD_BLOCK &&
    - error->code != G_IO_ERROR_CANCELLED)) {
    - purple_debug_warning(
    - "bonjour",
    - "receive of %" G_GSSIZE_FORMAT " error: %s",
    - len, error ? error->message : "(null)");
    -
    - bonjour_xmpp_close_conversation(bconv);
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - BonjourBuddy *bb = NULL;
    -
    - bb = g_object_get_data(G_OBJECT(bconv->contact),
    - "bonjour-buddy");
    - if(bb != NULL) {
    - bb->conversation = NULL;
    - }
    - }
    -
    - /* I guess we really don't need to notify the user.
    - * If they try to send another message it'll reconnect */
    - }
    - g_clear_error(&error);
    - return FALSE;
    - } else if (len == 0) { /* The other end has closed the socket */
    - const gchar *name = NULL;
    - name = purple_contact_info_get_username(PURPLE_CONTACT_INFO(bconv->contact));
    - purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)");
    - bonjour_xmpp_stream_ended(bconv);
    - return FALSE;
    - }
    -
    - message[len] = '\0';
    -
    - purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT " bytes\n", message, len);
    - bonjour_parser_process(bconv, message, len);
    -
    - return TRUE;
    -}
    -
    -struct _stream_start_data {
    - char *msg;
    -};
    -
    -static void
    -_start_stream(GObject *stream, gpointer data)
    -{
    - BonjourXMPPConversation *bconv = data;
    - struct _stream_start_data *ss = bconv->stream_data;
    - GError *error = NULL;
    - gsize len;
    - gssize ret;
    -
    - len = strlen(ss->msg);
    -
    - /* Start Stream */
    - ret = g_pollable_output_stream_write_nonblocking(
    - G_POLLABLE_OUTPUT_STREAM(stream), ss->msg, len,
    - bconv->cancellable, &error);
    -
    - if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
    - g_clear_error(&error);
    - return;
    - } else if (ret <= 0) {
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    - BonjourBuddy *bb = NULL;
    - const char *bname = bconv->buddy_name;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - bb = g_object_get_data(G_OBJECT(bconv->contact), "bonjour-buddy");
    - bname = purple_contact_info_get_username(PURPLE_CONTACT_INFO(bconv->contact));
    - }
    -
    - purple_debug_error(
    - "bonjour",
    - "Error starting stream with buddy %s at %s error: %s",
    - bname ? bname : "(unknown)", bconv->ip,
    - error ? error->message : "(null)");
    -
    - conv = purple_conversation_manager_find_im(manager, bconv->account,
    - bname);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send the message, the conversation couldn't be started."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - bonjour_xmpp_close_conversation(bconv);
    - if(bb != NULL) {
    - bb->conversation = NULL;
    - }
    -
    - g_clear_error(&error);
    - return;
    - }
    -
    - /* This is EXTREMELY unlikely to happen */
    - if (G_UNLIKELY((gsize)ret < len)) {
    - char *tmp = g_strdup(ss->msg + ret);
    - g_free(ss->msg);
    - ss->msg = tmp;
    - return;
    - }
    -
    - g_free(ss->msg);
    - g_free(ss);
    - bconv->stream_data = NULL;
    -
    - /* Stream started; process the send buffer if there is one */
    - g_clear_handle_id(&bconv->tx_handler, g_source_remove);
    - bconv->sent_stream_start = FULLY_SENT;
    -
    - bonjour_xmpp_stream_started(bconv);
    -}
    -
    -static gboolean
    -bonjour_xmpp_send_stream_init(BonjourXMPPConversation *bconv,
    - GError **error)
    -{
    - gchar *stream_start;
    - gsize len;
    - gssize ret;
    - const char *bname = bconv->buddy_name;
    -
    - g_return_val_if_fail(error != NULL, FALSE);
    -
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - bname = purple_contact_info_get_username(PURPLE_CONTACT_INFO(bconv->contact));
    - }
    -
    - /* If we have no idea who "to" is, use an empty string.
    - * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
    - if (bname == NULL)
    - bname = "";
    -
    - stream_start = g_strdup_printf(DOCTYPE, bonjour_get_jid(bconv->account), bname);
    - len = strlen(stream_start);
    -
    - bconv->sent_stream_start = PARTIALLY_SENT;
    -
    - /* Start the stream */
    - ret = g_pollable_output_stream_write_nonblocking(
    - G_POLLABLE_OUTPUT_STREAM(bconv->output), stream_start, len,
    - bconv->cancellable, error);
    - if (ret == -1 && (*error)->code == G_IO_ERROR_WOULD_BLOCK) {
    - ret = 0;
    - g_clear_error(error);
    - } else if (ret <= 0) {
    - purple_debug_error(
    - "bonjour",
    - "Error starting stream with buddy %s at %s error: %s",
    - (*bname) ? bname : "(unknown)", bconv->ip,
    - *error ? (*error)->message : "(null)");
    -
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - conv = purple_conversation_manager_find_im(manager, bconv->account,
    - bname);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send the message, the conversation couldn't be started."),
    - PURPLE_MESSAGE_ERROR);
    - }
    - }
    -
    - purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
    - G_INPUT_STREAM(bconv->input),
    - G_OUTPUT_STREAM(bconv->output));
    - g_clear_object(&bconv->socket);
    - g_clear_object(&bconv->input);
    - g_clear_object(&bconv->output);
    - g_free(stream_start);
    -
    - return FALSE;
    - }
    -
    - /* This is unlikely to happen */
    - if ((gsize)ret < len) {
    - GSource *source;
    - struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
    - ss->msg = g_strdup(stream_start + ret);
    - bconv->stream_data = ss;
    - /* Finish sending the stream start */
    - source = g_pollable_output_stream_create_source(
    - G_POLLABLE_OUTPUT_STREAM(bconv->output),
    - bconv->cancellable);
    - g_source_set_callback(source, G_SOURCE_FUNC(_start_stream), bconv,
    - NULL);
    - bconv->tx_handler = g_source_attach(source, NULL);
    - g_source_unref(source);
    - } else {
    - bconv->sent_stream_start = FULLY_SENT;
    - }
    -
    - g_free(stream_start);
    -
    - return TRUE;
    -}
    -
    -/* This gets called when we've successfully sent our <stream:stream />
    - * AND when we've received a <stream:stream /> */
    -void
    -bonjour_xmpp_stream_started(BonjourXMPPConversation *bconv)
    -{
    - GError *error = NULL;
    -
    - if (bconv->sent_stream_start == NOT_SENT &&
    - !bonjour_xmpp_send_stream_init(bconv, &error)) {
    - const char *bname = bconv->buddy_name;
    -
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - bname = purple_contact_info_get_username(PURPLE_CONTACT_INFO(bconv->contact));
    - }
    -
    - purple_debug_error(
    - "bonjour",
    - "Error starting stream with buddy %s at %s error: %s",
    - bname ? bname : "(unknown)", bconv->ip,
    - error ? error->message : "(null)");
    -
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - conv = purple_conversation_manager_find_im(manager, bconv->account,
    - bname);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send the message, the conversation couldn't be started."),
    - PURPLE_MESSAGE_ERROR);
    - }
    - }
    -
    - /* We don't want to receive anything else */
    - purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
    - G_INPUT_STREAM(bconv->input),
    - G_OUTPUT_STREAM(bconv->output));
    - g_clear_object(&bconv->socket);
    - g_clear_object(&bconv->input);
    - g_clear_object(&bconv->output);
    -
    - /* This must be asynchronous because it destroys the parser and we
    - * may be in the middle of parsing.
    - */
    - async_bonjour_xmpp_close_conversation(bconv);
    - g_clear_error(&error);
    - return;
    - }
    -
    - /* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
    - /* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
    - if(bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start &&
    - PURPLE_IS_CONTACT(bconv->contact) &&
    - purple_circular_buffer_get_max_read(bconv->tx_buf) > 0)
    - {
    - /* Watch for when we can write the buffered messages */
    - GSource *source = g_pollable_output_stream_create_source(
    - G_POLLABLE_OUTPUT_STREAM(bconv->output),
    - bconv->cancellable);
    - g_source_set_callback(source, G_SOURCE_FUNC(_send_data_write_cb),
    - g_object_ref(bconv->contact), g_object_unref);
    - bconv->tx_handler = g_source_attach(source, NULL);
    - g_source_unref(source);
    - /* We can probably write the data right now. */
    - g_object_ref(bconv->contact);
    - _send_data_write_cb(G_OBJECT(bconv->output), bconv->contact);
    - g_object_unref(bconv->contact);
    - }
    -}
    -
    -#ifndef INET6_ADDRSTRLEN
    -#define INET6_ADDRSTRLEN 46
    -#endif
    -
    -static void
    -_server_socket_handler(G_GNUC_UNUSED GSocketService *service,
    - GSocketConnection *connection,
    - G_GNUC_UNUSED GObject *source_object, gpointer data)
    -{
    - BonjourXMPP *jdata = data;
    - GSocketAddress *their_addr; /* connector's address information */
    - GInetAddress *their_inet_addr;
    - gchar *address_text;
    - BonjourXMPPConversation *bconv;
    - GSList *contacts;
    - GSource *source;
    -
    - their_addr = g_socket_connection_get_remote_address(connection, NULL);
    - if (their_addr == NULL) {
    - return;
    - }
    - their_inet_addr = g_inet_socket_address_get_address(
    - G_INET_SOCKET_ADDRESS(their_addr));
    -
    - /* Look for the buddy that has opened the conversation and fill information */
    - address_text = g_inet_address_to_string(their_inet_addr);
    - if (g_inet_address_get_family(their_inet_addr) ==
    - G_SOCKET_FAMILY_IPV6 &&
    - g_inet_address_get_is_link_local(their_inet_addr)) {
    - gchar *tmp = g_strdup_printf(
    - "%s%%%d", address_text,
    - g_inet_socket_address_get_scope_id(
    - G_INET_SOCKET_ADDRESS(their_addr)));
    - g_free(address_text);
    - address_text = tmp;
    - }
    - g_object_unref(their_addr);
    -
    - purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
    -
    - contacts = _find_match_buddies_by_address(jdata, address_text);
    - if (contacts == NULL) {
    - purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
    - g_free(address_text);
    - return;
    - }
    -
    - g_slist_free(contacts);
    -
    - /* We've established that this *could* be from one of our buddies.
    - * Wait for the stream open to see if that matches too before assigning it.
    - */
    - bconv = bonjour_xmpp_conv_new(NULL, jdata->account, address_text);
    -
    - /* We wait for the stream start before doing anything else */
    - bconv->socket = g_object_ref(connection);
    - bconv->input = g_object_ref(
    - g_io_stream_get_input_stream(G_IO_STREAM(bconv->socket)));
    - bconv->output = g_object_ref(
    - g_io_stream_get_output_stream(G_IO_STREAM(bconv->socket)));
    - source = g_pollable_input_stream_create_source(
    - G_POLLABLE_INPUT_STREAM(bconv->input), bconv->cancellable);
    - g_source_set_callback(source, G_SOURCE_FUNC(_client_socket_handler),
    - bconv, NULL);
    - bconv->rx_handler = g_source_attach(source, NULL);
    - g_source_unref(source);
    - g_free(address_text);
    -}
    -
    -gint
    -bonjour_xmpp_start(BonjourXMPP *jdata)
    -{
    - GError *error = NULL;
    - guint16 port;
    -
    - purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
    - jdata->port);
    -
    - /* Open a listening server for incoming conversations */
    - jdata->service = g_socket_service_new();
    - g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata->service), 10);
    - port = jdata->port;
    - if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata->service),
    - port, NULL, &error)) {
    - purple_debug_info("bonjour",
    - "Unable to bind to specified port %i: %s",
    - port, error ? error->message : "(unknown)");
    - g_clear_error(&error);
    - port = g_socket_listener_add_any_inet_port(
    - G_SOCKET_LISTENER(jdata->service), NULL, &error);
    - if (port == 0) {
    - purple_debug_error(
    - "bonjour", "Unable to create socket: %s",
    - error ? error->message : "(unknown)");
    - g_clear_error(&error);
    - return -1;
    - }
    - }
    - purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
    - jdata->port = port;
    -
    - g_signal_connect(G_OBJECT(jdata->service), "incoming",
    - G_CALLBACK(_server_socket_handler), jdata);
    -
    - return jdata->port;
    -}
    -
    -static void
    -_connected_to_buddy(GObject *source, GAsyncResult *res, gpointer user_data)
    -{
    - BonjourBuddy *bb = NULL;
    - PurpleAccount *account = NULL;
    - PurpleContact *contact = user_data;
    - GSocketConnection *conn;
    - GSource *rx_source;
    - GError *error = NULL;
    - const char *username = NULL;
    -
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    - res, &error);
    - username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact));
    - account = purple_contact_get_account(contact);
    -
    - if (conn == NULL) {
    - PurpleConversation *conv = NULL;
    - PurpleConversationManager *manager = NULL;
    - GSList *tmp;
    -
    - if (error && error->code == G_IO_ERROR_CANCELLED) {
    - /* This conversation was closed before it started. */
    - g_error_free(error);
    - g_clear_object(&contact);
    -
    - return;
    - }
    -
    - purple_debug_error("bonjour",
    - "Error connecting to buddy %s at %s:%d "
    - "(%s); Trying next IP address",
    - username,
    - bb->conversation->ip, bb->port_p2pj,
    - error ? error->message : "(unknown)");
    - g_clear_error(&error);
    -
    - /* There may be multiple entries for the same IP - one per
    - * presence received (e.g. multiple interfaces).
    - * We need to make sure that we find the previously used entry.
    - */
    - tmp = g_slist_find(bb->ips, bb->conversation->ip_link);
    - if (tmp)
    - tmp = g_slist_next(tmp);
    -
    - if (tmp != NULL) {
    - const gchar *ip;
    - GSocketClient *client;
    -
    - bb->conversation->ip_link = ip = tmp->data;
    -
    - purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
    - username, ip, bb->port_p2pj);
    -
    - /* Make sure to connect without a proxy. */
    - client = g_socket_client_new();
    - if (client != NULL) {
    - g_free(bb->conversation->ip);
    - bb->conversation->ip = g_strdup(ip);
    - /* We pass our reference on contact to the callback. */
    - g_socket_client_connect_to_host_async(
    - client, ip, bb->port_p2pj,
    - bb->conversation->cancellable,
    - _connected_to_buddy, contact);
    - g_object_unref(client);
    - return;
    - }
    - }
    -
    - purple_debug_error("bonjour",
    - "No more addresses for buddy %s. Aborting",
    - username);
    -
    - manager = purple_conversation_manager_get_default();
    -
    - conv = purple_conversation_manager_find_im(manager, account, bb->name);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send the message, the conversation couldn't be started."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - bonjour_xmpp_close_conversation(bb->conversation);
    - bb->conversation = NULL;
    -
    - g_clear_object(&contact);
    -
    - return;
    - }
    -
    - bb->conversation->socket = conn;
    - bb->conversation->input =
    - g_object_ref(g_io_stream_get_input_stream(G_IO_STREAM(conn)));
    - bb->conversation->output =
    - g_object_ref(g_io_stream_get_output_stream(G_IO_STREAM(conn)));
    -
    - if (!bonjour_xmpp_send_stream_init(bb->conversation, &error)) {
    - PurpleConversation *conv = NULL;
    - PurpleConversationManager *manager = NULL;
    -
    - purple_debug_error("bonjour",
    - "Error starting stream with buddy %s at "
    - "%s:%d error: %s",
    - username,
    - bb->conversation->ip, bb->port_p2pj,
    - error ? error->message : "(null)");
    -
    - manager = purple_conversation_manager_get_default();
    -
    - conv = purple_conversation_manager_find_im(manager, account, bb->name);
    - if (conv != NULL) {
    - purple_conversation_write_system_message(conv,
    - _("Unable to send the message, the conversation couldn't be started."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - bonjour_xmpp_close_conversation(bb->conversation);
    - bb->conversation = NULL;
    - g_clear_error(&error);
    - g_clear_object(&contact);
    -
    - return;
    - }
    -
    - /* Start listening for the stream acknowledgement */
    - rx_source = g_pollable_input_stream_create_source(
    - G_POLLABLE_INPUT_STREAM(bb->conversation->input),
    - bb->conversation->cancellable);
    - g_source_set_callback(rx_source, G_SOURCE_FUNC(_client_socket_handler),
    - bb->conversation, NULL);
    - bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
    - g_source_unref(rx_source);
    - g_clear_object(&contact);
    -}
    -
    -void
    -bonjour_xmpp_conv_match_by_name(BonjourXMPPConversation *bconv) {
    - BonjourBuddy *bb = NULL;
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    -
    - g_return_if_fail(bconv->ip != NULL);
    - g_return_if_fail(!PURPLE_IS_CONTACT(bconv->contact));
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager,
    - bconv->account,
    - bconv->buddy_name);
    -
    - if(contact && (bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy"))) {
    - const char *username = NULL;
    -
    - username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact));
    -
    - purple_debug_info("bonjour",
    - "Found buddy %s for incoming conversation \"from\" attrib.\n",
    - username);
    -
    - /* Check that one of the buddy's IPs matches */
    - if (g_slist_find_custom(bb->ips, bconv->ip, (GCompareFunc)g_ascii_strcasecmp)) {
    - PurpleConnection *pc = purple_account_get_connection(bconv->account);
    - BonjourData *bd = purple_connection_get_protocol_data(pc);
    - BonjourXMPP *jdata = bd->xmpp_data;
    -
    - purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)",
    - username, bconv->ip);
    -
    - /* Attach conv. to buddy and remove from pending list */
    - jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
    -
    - /* Check if the buddy already has a conversation and, if so, replace it */
    - if (bb->conversation != NULL && bb->conversation != bconv) {
    - bonjour_xmpp_close_conversation(bb->conversation);
    - }
    -
    - bconv->contact = contact;
    - bb->conversation = bconv;
    - }
    - }
    -
    - /* We've failed to match a buddy - give up */
    - if(!PURPLE_IS_CONTACT(bconv->contact)) {
    - /* This must be asynchronous because it destroys the parser and we
    - * may be in the middle of parsing.
    - */
    - async_bonjour_xmpp_close_conversation(bconv);
    - }
    -
    - g_clear_object(&contact);
    -}
    -
    -
    -void
    -bonjour_xmpp_conv_match_by_ip(BonjourXMPPConversation *bconv) {
    - PurpleConnection *pc = purple_account_get_connection(bconv->account);
    - BonjourData *bd = purple_connection_get_protocol_data(pc);
    - BonjourXMPP *jdata = bd->xmpp_data;
    - GSList *contacts;
    -
    - contacts = _find_match_buddies_by_address(jdata, bconv->ip);
    -
    - /* If there is exactly one match, use it */
    - if (!contacts) {
    - purple_debug_error("bonjour", "No buddies matched for ip %s.", bconv->ip);
    - } else if (contacts->next != NULL) {
    - purple_debug_error("bonjour", "More than one buddy matched for ip %s.", bconv->ip);
    - } else {
    - PurpleContact *contact = contacts->data;
    - BonjourBuddy *bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    -
    - purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)",
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(contact)),
    - bconv->ip);
    -
    - /* Attach conv. to buddy and remove from pending list */
    - jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
    -
    - /* Check if the buddy already has a conversation and, if so, replace it */
    - if (bb->conversation != NULL && bb->conversation != bconv) {
    - bonjour_xmpp_close_conversation(bb->conversation);
    - }
    -
    - bconv->contact = contact;
    - bb->conversation = bconv;
    - }
    -
    - /* We've failed to match a buddy - give up */
    - if(!PURPLE_IS_CONTACT(bconv->contact)) {
    - /* This must be asynchronous because it destroys the parser and we
    - * may be in the middle of parsing.
    - */
    - async_bonjour_xmpp_close_conversation(bconv);
    - }
    -
    - g_slist_free(contacts);
    -}
    -
    -static PurpleContact *
    -_find_or_start_conversation(BonjourXMPP *jdata, const gchar *to)
    -{
    - PurpleContact *contact = NULL;
    - PurpleContactManager *manager = NULL;
    - BonjourBuddy *bb = NULL;
    -
    - g_return_val_if_fail(jdata != NULL, NULL);
    - g_return_val_if_fail(to != NULL, NULL);
    -
    - manager = purple_contact_manager_get_default();
    - contact = purple_contact_manager_find_with_username(manager,
    - jdata->account, to);
    -
    - if(!PURPLE_IS_CONTACT(contact)) {
    - return NULL;
    - }
    -
    - bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy");
    - if(bb == NULL) {
    - g_clear_object(&contact);
    - return NULL;
    - }
    -
    - /* Check if there is a previously open conversation */
    - if (bb->conversation == NULL) {
    - GSocketClient *client;
    - /* Start with the first IP address. */
    - const gchar *ip = bb->ips->data;
    -
    - purple_debug_info("bonjour",
    - "Starting conversation with %s at %s:%d", to,
    - ip, bb->port_p2pj);
    -
    - /* Make sure to connect without a proxy. */
    - client = g_socket_client_new();
    - if (client == NULL) {
    - purple_debug_error("bonjour",
    - "Unable to connect to buddy (%s).",
    - to);
    - g_clear_object(&contact);
    - return NULL;
    - }
    -
    - bb->conversation = bonjour_xmpp_conv_new(contact, jdata->account, ip);
    - bb->conversation->ip_link = ip;
    -
    - g_socket_client_connect_to_host_async(
    - client, ip, bb->port_p2pj,
    - bb->conversation->cancellable, _connected_to_buddy,
    - g_object_ref(contact));
    - g_object_unref(client);
    - }
    - return contact;
    -}
    -
    -int
    -bonjour_xmpp_send_message(BonjourXMPP *jdata, const gchar *to, const gchar *body)
    -{
    - PurpleXmlNode *message_node, *node, *node2;
    - gchar *message, *xhtml;
    - PurpleContact *contact = NULL;
    - BonjourBuddy *bb;
    - int ret;
    -
    - contact = _find_or_start_conversation(jdata, to);
    - if(!PURPLE_IS_CONTACT(contact) ||
    - (bb = g_object_get_data(G_OBJECT(contact), "bonjour-buddy")) == NULL)
    - {
    - purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
    - g_clear_object(&contact);
    - /* You can not send a message to an offline buddy */
    - return -10000;
    - }
    -
    - purple_markup_html_to_xhtml(body, &xhtml, &message);
    -
    - message_node = purple_xmlnode_new("message");
    - purple_xmlnode_set_attrib(message_node, "to", bb->name);
    - purple_xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
    - purple_xmlnode_set_attrib(message_node, "type", "chat");
    -
    - /* Enclose the message from the UI within a "font" node */
    - node = purple_xmlnode_new_child(message_node, "body");
    - purple_xmlnode_insert_data(node, message, strlen(message));
    - g_free(message);
    -
    - node = purple_xmlnode_new_child(message_node, "html");
    - purple_xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
    -
    - node = purple_xmlnode_new_child(node, "body");
    - message = g_strdup_printf("<font>%s</font>", xhtml);
    - node2 = purple_xmlnode_from_str(message, strlen(message));
    - g_free(xhtml);
    - g_free(message);
    - purple_xmlnode_insert_child(node, node2);
    -
    - node = purple_xmlnode_new_child(message_node, "x");
    - purple_xmlnode_set_namespace(node, "jabber:x:event");
    - purple_xmlnode_insert_child(node, purple_xmlnode_new("composing"));
    -
    - message = purple_xmlnode_to_str(message_node, NULL);
    - purple_xmlnode_free(message_node);
    -
    - ret = _send_data(contact, message) >= 0;
    -
    - g_free(message);
    -
    - g_clear_object(&contact);
    -
    - return ret;
    -}
    -
    -static gboolean
    -_async_bonjour_xmpp_close_conversation_cb(gpointer data) {
    - BonjourXMPPConversation *bconv = data;
    - bonjour_xmpp_close_conversation(bconv);
    - return FALSE;
    -}
    -
    -void
    -async_bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv) {
    - PurpleConnection *pc = purple_account_get_connection(bconv->account);
    - BonjourData *bd = purple_connection_get_protocol_data(pc);
    - BonjourXMPP *jdata = bd->xmpp_data;
    -
    - jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
    -
    - /* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
    - if(PURPLE_IS_CONTACT(bconv->contact)) {
    - BonjourBuddy *bb = g_object_get_data(G_OBJECT(bconv->contact), "bonjour-buddy");
    - if (bb->conversation == bconv)
    - bb->conversation = NULL;
    - }
    -
    - bconv->close_timeout = g_timeout_add(0, _async_bonjour_xmpp_close_conversation_cb, bconv);
    -}
    -
    -void
    -bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv)
    -{
    - BonjourData *bd = NULL;
    - PurpleConnection *pc = NULL;
    -
    - if (bconv == NULL) {
    - return;
    - }
    -
    - pc = purple_account_get_connection(bconv->account);
    - PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
    -
    - bd = purple_connection_get_protocol_data(pc);
    - if (bd) {
    - bd->xmpp_data->pending_conversations = g_slist_remove(
    - bd->xmpp_data->pending_conversations, bconv);
    - }
    -
    - /* Cancel any file transfers that are waiting to begin */
    - /* There won't be any transfers if it hasn't been attached to a buddy */
    - if (PURPLE_IS_CONTACT(bconv->contact) && bd != NULL) {
    - GSList *xfers, *tmp_next;
    - const char *username = NULL;
    -
    - xfers = bd->xfer_lists;
    - username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(bconv->contact));
    -
    - while (xfers != NULL) {
    - PurpleXfer *xfer = xfers->data;
    -
    - tmp_next = xfers->next;
    -
    - /* We only need to cancel this if it hasn't actually started transferring. */
    - /* This will change if we ever support IBB transfers. */
    - if (purple_strequal(purple_xfer_get_remote_user(xfer), username)
    - && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
    - || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN))
    - {
    - purple_xfer_cancel_remote(xfer);
    - }
    - xfers = tmp_next;
    - }
    - }
    -
    - /* Close th