pidgin/pidgin

a58c06469198
Parents 3b61e89f42bd
Children d45730609bcc
Rename Jabber to XMPP in Bonjour, as the official name is now XMPP and has been
for many years.
--- a/libpurple/protocols/bonjour/bonjour.c Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/bonjour.c Thu Oct 24 22:28:48 2019 -0400
@@ -35,9 +35,9 @@
#include "bonjour.h"
#include "mdns_common.h"
-#include "jabber.h"
#include "buddy.h"
#include "bonjour_ft.h"
+#include "xmpp.h"
static PurpleProtocol *my_protocol = NULL;
@@ -104,12 +104,12 @@
bd = g_new0(BonjourData, 1);
purple_connection_set_protocol_data(gc, bd);
- /* Start waiting for jabber connections (iChat style) */
- bd->jabber_data = g_new0(BonjourJabber, 1);
- bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
- bd->jabber_data->account = account;
+ /* 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_jabber_start(bd->jabber_data) == -1) {
+ if (bonjour_xmpp_start(bd->xmpp_data) == -1) {
/* Send a message about the connection error */
purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -121,7 +121,7 @@
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->jabber_data->port;
+ bd->dns_sd_data->port_p2pj = bd->xmpp_data->port;
/* Not engaged in AV conference */
bd->dns_sd_data->vc = g_strdup("!");
@@ -168,11 +168,11 @@
bonjour_dns_sd_free(bd->dns_sd_data);
}
- if (bd != NULL && bd->jabber_data != NULL)
+ if (bd != NULL && bd->xmpp_data != NULL)
{
/* Stop waiting for conversations */
- bonjour_jabber_stop(bd->jabber_data);
- g_free(bd->jabber_data);
+ bonjour_xmpp_stop(bd->xmpp_data);
+ g_free(bd->xmpp_data);
}
/* Delete the bonjour group
@@ -206,7 +206,7 @@
if (purple_message_is_empty(msg) || !purple_message_get_recipient(msg))
return 0;
- return bonjour_jabber_send_message(bd->jabber_data,
+ return bonjour_xmpp_send_message(bd->xmpp_data,
purple_message_get_recipient(msg),
purple_message_get_contents(msg));
}
@@ -319,7 +319,7 @@
return;
}
- bonjour_jabber_close_conversation(bb->conversation);
+ bonjour_xmpp_close_conversation(bb->conversation);
bb->conversation = NULL;
}
--- a/libpurple/protocols/bonjour/bonjour.h Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/bonjour.h Thu Oct 24 22:28:48 2019 -0400
@@ -1,5 +1,5 @@
/**
- * @file bonjour.h The Purple interface to mDNS and peer to peer Jabber.
+ * @file bonjour.h The Purple interface to mDNS and peer to peer XMPP.
*
* purple
*
@@ -32,7 +32,7 @@
#include <purple.h>
#include "mdns_common.h"
-#include "jabber.h"
+#include "xmpp.h"
#define BONJOUR_GROUP_NAME _("Bonjour")
#define BONJOUR_PROTOCOL_NAME "bonjour"
@@ -64,7 +64,7 @@
typedef struct
{
BonjourDnsSd *dns_sd_data;
- BonjourJabber *jabber_data;
+ BonjourXMPP *xmpp_data;
GSList *xfer_lists;
gchar *jid;
} BonjourData;
--- a/libpurple/protocols/bonjour/bonjour_ft.c Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/bonjour_ft.c Thu Oct 24 22:28:48 2019 -0400
@@ -80,7 +80,7 @@
return;
}
- iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id);
+ iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->xmpp_data->account), id);
if(iq == NULL)
return;
@@ -213,29 +213,29 @@
/* 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->jabber_data->account), xf->iq_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");
+ purple_xmlnode_set_namespace(si_node, "http://xmpp.org/protocol/si");
+ purple_xmlnode_set_attrib(si_node, "profile", "http://xmpp.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_namespace(file, "http://xmpp.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");
+ purple_xmlnode_set_namespace(feature, "http://xmpp.org/protocol/feature-neg");
x = purple_xmlnode_new_child(feature, "x");
- purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_namespace(x, "xmpp:x:data");
purple_xmlnode_set_attrib(x, "type", "form");
field = purple_xmlnode_new_child(x, "field");
@@ -245,12 +245,12 @@
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);
+ purple_xmlnode_insert_data(value, "http://xmpp.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);
+ purple_xmlnode_insert_data(value, "http://xmpp.org/protocol/ibb", -1);
}
xep_iq_send_and_free(iq);
@@ -271,26 +271,26 @@
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->jabber_data->account), xf->iq_id);
+ 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");*/
+ purple_xmlnode_set_namespace(si_node, "http://xmpp.org/protocol/si");
+ /*purple_xmlnode_set_attrib(si_node, "profile", "http://xmpp.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");
+ purple_xmlnode_set_namespace(feature, "http://xmpp.org/protocol/feature-neg");
x = purple_xmlnode_new_child(feature, "x");
- purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_namespace(x, "xmpp: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);
+ purple_xmlnode_insert_data(value, "http://xmpp.org/protocol/bytestreams", -1);
xep_iq_send_and_free(iq);
}
@@ -438,7 +438,7 @@
profile = purple_xmlnode_get_attrib(si, "profile");
- if (purple_strequal(profile, "http://jabber.org/protocol/si/profile/file-transfer")) {
+ if (purple_strequal(profile, "http://xmpp.org/protocol/si/profile/file-transfer")) {
const char *filename = NULL, *filesize_str = NULL;
goffset filesize = 0;
PurpleXmlNode *file;
@@ -904,16 +904,16 @@
bd = xf->data;
- iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->sid);
+ 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_namespace(query, "http://xmpp.org/protocol/bytestreams");
purple_xmlnode_set_attrib(query, "sid", xf->sid);
purple_xmlnode_set_attrib(query, "mode", "tcp");
purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
- local_ips = bonjour_jabber_get_local_ips(sock);
+ local_ips = bonjour_xmpp_get_local_ips(sock);
port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer));
while(local_ips) {
@@ -981,9 +981,9 @@
/* 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->jabber_data->account), xf->iq_id);
+ 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");
+ purple_xmlnode_set_namespace(q_node, "http://xmpp.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);
--- a/libpurple/protocols/bonjour/buddy.c Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/buddy.c Thu Oct 24 22:28:48 2019 -0400
@@ -258,7 +258,7 @@
g_free(buddy->node);
g_free(buddy->ver);
- bonjour_jabber_close_conversation(buddy->conversation);
+ bonjour_xmpp_close_conversation(buddy->conversation);
buddy->conversation = NULL;
/* Clean up any mdns implementation data */
--- a/libpurple/protocols/bonjour/buddy.h Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/buddy.h Thu Oct 24 22:28:48 2019 -0400
@@ -21,7 +21,7 @@
#include <purple.h>
-#include "jabber.h"
+#include "xmpp.h"
typedef struct
{
@@ -45,7 +45,7 @@
gchar *node;
gchar *ver;
- BonjourJabberConversation *conversation;
+ BonjourXMPPConversation *conversation;
gpointer mdns_impl_data;
} BonjourBuddy;
--- a/libpurple/protocols/bonjour/jabber.c Sun Oct 20 00:24:28 2019 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1472 +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 "internal.h"
-#include <purple.h>
-
-#ifndef _WIN32
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-#include <sys/types.h>
-
-/* Solaris */
-#if defined (__SVR4) && defined (__sun)
-#include <sys/sockio.h>
-#endif
-
-#include <glib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-
-#ifdef HAVE_GETIFADDRS
-#include <ifaddrs.h>
-#endif
-
-#include "jabber.h"
-#include "parser.h"
-#include "bonjour.h"
-#include "buddy.h"
-#include "bonjour_ft.h"
-
-#ifdef _SIZEOF_ADDR_IFREQ
-# define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
-#else
-# define HX_SIZE_OF_IFREQ(a) sizeof(a)
-#endif
-
-#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, PurpleBuddy *pb);
-
-static BonjourJabberConversation *
-bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
-
- BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
- bconv->cancellable = g_cancellable_new();
- bconv->tx_buf = purple_circular_buffer_new(512);
- bconv->tx_handler = 0;
- bconv->rx_handler = 0;
- bconv->pb = pb;
- 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
-_jabber_parse_and_write_message_to_ui(PurpleXmlNode *message_node, PurpleBuddy *pb)
-{
- PurpleXmlNode *body_node, *html_node, *events_node;
- PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
- gchar *body = NULL;
-
- 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 */
- purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
-
- g_free(body);
-}
-
-struct _match_buddies_by_address {
- const char *address;
- GSList *matched_buddies;
-};
-
-static void
-_match_buddies_by_address(gpointer value, gpointer data)
-{
- PurpleBuddy *pb = value;
- BonjourBuddy *bb = NULL;
- struct _match_buddies_by_address *mbba = data;
-
- bb = purple_buddy_get_protocol_data(pb);
-
- /*
- * If the current PurpleBuddy's data is not null, then continue to determine
- * whether one of the buddies IPs matches the target IP.
- */
- if (bb != NULL)
- {
- const char *ip;
- GSList *tmp = bb->ips;
-
- while(tmp) {
- ip = tmp->data;
- if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
- mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
- break;
- }
- tmp = tmp->next;
- }
- }
-}
-
-static void
-_send_data_write_cb(GObject *stream, gpointer data)
-{
- PurpleBuddy *pb = data;
- BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
- BonjourJabberConversation *bconv = bb->conversation;
- gsize writelen;
- gssize ret;
- GError *error = NULL;
-
- writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
-
- if (writelen == 0) {
- g_source_remove(bconv->tx_handler);
- bconv->tx_handler = 0;
- 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) {
- PurpleConversation *conv = NULL;
- PurpleAccount *account = NULL;
-
- purple_debug_error(
- "bonjour",
- "Error sending message to buddy %s error: %s",
- purple_buddy_get_name(pb),
- error ? error->message : "(null)");
-
- account = purple_buddy_get_account(pb);
-
- conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
- if (conv != NULL)
- purple_conversation_write_system_message(conv,
- _("Unable to send message."),
- PURPLE_MESSAGE_ERROR);
-
- bonjour_jabber_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(PurpleBuddy *pb, char *message)
-{
- BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
- BonjourJabberConversation *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) {
- PurpleConversation *conv;
- PurpleAccount *account;
-
- purple_debug_error(
- "bonjour",
- "Error sending message to buddy %s error: %s",
- purple_buddy_get_name(pb),
- error ? error->message : "(null)");
-
- account = purple_buddy_get_account(pb);
-
- conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
- if (conv != NULL)
- purple_conversation_write_system_message(conv,
- _("Unable to send message."),
- PURPLE_MESSAGE_ERROR);
-
- bonjour_jabber_close_conversation(bb->conversation);
- bb->conversation = NULL;
- g_clear_error(&error);
- return -1;
- }
-
- if (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,
- (GSourceFunc)_send_data_write_cb,
- pb, NULL);
- bconv->tx_handler = g_source_attach(source, NULL);
- }
- purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
- }
-
- return ret;
-}
-
-void bonjour_jabber_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet) {
-
- g_return_if_fail(packet != NULL);
- g_return_if_fail(pb != NULL);
-
- if (purple_strequal(packet->name, "message"))
- _jabber_parse_and_write_message_to_ui(packet, pb);
- else if (purple_strequal(packet->name, "iq"))
- xep_iq_parse(packet, pb);
- else {
- purple_debug_warning("bonjour", "Unknown packet: %s\n",
- packet->name ? packet->name : "(null)");
- }
-}
-
-static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
-
- /* Inform the user that the conversation has been closed */
- BonjourBuddy *bb = NULL;
- const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
-
- purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
-
- if(bconv->pb != NULL)
- bb = purple_buddy_get_protocol_data(bconv->pb);
-
- /* Close the socket, clear the watcher and free memory */
- bonjour_jabber_close_conversation(bconv);
- if(bb)
- bb->conversation = NULL;
-}
-
-static gboolean
-_client_socket_handler(GObject *stream, gpointer data)
-{
- BonjourJabberConversation *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_jabber_close_conversation(bconv);
- if (bconv->pb != NULL) {
- BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
-
- 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 = purple_buddy_get_name(bconv->pb);
- purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)");
- bonjour_jabber_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)
-{
- BonjourJabberConversation *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;
- const char *bname = bconv->buddy_name;
- BonjourBuddy *bb = NULL;
-
- if(bconv->pb) {
- bb = purple_buddy_get_protocol_data(bconv->pb);
- bname = purple_buddy_get_name(bconv->pb);
- }
-
- 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(purple_conversations_find_im_with_account(bname, bconv->account));
- if (conv != NULL)
- purple_conversation_write_system_message(conv,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_ERROR);
-
- bonjour_jabber_close_conversation(bconv);
- if(bb != NULL)
- bb->conversation = NULL;
-
- g_clear_error(&error);
- return;
- }
-
- /* This is EXTREMELY unlikely to happen */
- if (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_source_remove(bconv->tx_handler);
- bconv->tx_handler = 0;
- bconv->sent_stream_start = FULLY_SENT;
-
- bonjour_jabber_stream_started(bconv);
-}
-
-static gboolean
-bonjour_jabber_send_stream_init(BonjourJabberConversation *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 (bconv->pb != NULL)
- bname = purple_buddy_get_name(bconv->pb);
-
- /* 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 (bconv->pb) {
- PurpleConversation *conv;
- conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
- 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);
- bconv->input = NULL;
- bconv->output = NULL;
- g_free(stream_start);
-
- return FALSE;
- }
-
- /* This is unlikely to happen */
- if (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, (GSourceFunc)_start_stream, bconv,
- NULL);
- bconv->tx_handler = g_source_attach(source, NULL);
- } 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_jabber_stream_started(BonjourJabberConversation *bconv)
-{
- GError *error = NULL;
-
- if (bconv->sent_stream_start == NOT_SENT &&
- !bonjour_jabber_send_stream_init(bconv, &error)) {
- const char *bname = bconv->buddy_name;
-
- if (bconv->pb)
- bname = purple_buddy_get_name(bconv->pb);
-
- purple_debug_error(
- "bonjour",
- "Error starting stream with buddy %s at %s error: %s",
- bname ? bname : "(unknown)", bconv->ip,
- error ? error->message : "(null)");
-
- if (bconv->pb) {
- PurpleConversation *conv;
- conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
- 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 recieve 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);
- bconv->input = NULL;
- bconv->output = NULL;
-
- /* This must be asynchronous because it destroys the parser and we
- * may be in the middle of parsing.
- */
- async_bonjour_jabber_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
- && bconv->pb && 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, (GSourceFunc)_send_data_write_cb,
- bconv->pb, NULL);
- bconv->tx_handler = g_source_attach(source, NULL);
- /* We can probably write the data right now. */
- _send_data_write_cb(G_OBJECT(bconv->output), bconv->pb);
- }
-}
-
-#ifndef INET6_ADDRSTRLEN
-#define INET6_ADDRSTRLEN 46
-#endif
-
-static void
-_server_socket_handler(GSocketService *service, GSocketConnection *connection,
- GObject *source_object, gpointer data)
-{
- BonjourJabber *jdata = data;
- GSocketAddress *their_addr; /* connector's address information */
- GInetAddress *their_inet_addr;
- gchar *address_text;
- struct _match_buddies_by_address *mbba;
- BonjourJabberConversation *bconv;
- GSList *buddies;
- 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);
- mbba = g_new0(struct _match_buddies_by_address, 1);
- mbba->address = address_text;
-
- buddies = purple_blist_find_buddies(jdata->account, NULL);
- g_slist_foreach(buddies, _match_buddies_by_address, mbba);
- g_slist_free(buddies);
-
- if (mbba->matched_buddies == NULL) {
- purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
- g_free(address_text);
- g_free(mbba);
- return;
- }
-
- g_slist_free(mbba->matched_buddies);
- g_free(mbba);
-
- /* 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_jabber_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_io_stream_get_input_stream(G_IO_STREAM(bconv->socket));
- bconv->output =
- 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, (GSourceFunc)_client_socket_handler,
- bconv, NULL);
- bconv->rx_handler = g_source_attach(source, NULL);
- g_free(address_text);
-}
-
-gint
-bonjour_jabber_start(BonjourJabber *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)
-{
- PurpleBuddy *pb = user_data;
- BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
- GSocketConnection *conn;
- GSource *rx_source;
- GError *error = NULL;
-
- conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
- res, &error);
-
- if (conn == NULL) {
- PurpleConversation *conv = NULL;
- PurpleAccount *account = NULL;
- GSList *tmp;
-
- if (error && error->code == G_IO_ERROR_CANCELLED) {
- /* This conversation was closed before it started. */
- g_error_free(error);
- return;
- }
-
- purple_debug_error("bonjour",
- "Error connecting to buddy %s at %s:%d "
- "(%s); Trying next IP address",
- purple_buddy_get_name(pb),
- 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 recieved (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);
-
- account = purple_buddy_get_account(pb);
-
- 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",
- purple_buddy_get_name(pb), 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);
- g_socket_client_connect_to_host_async(
- client, ip, bb->port_p2pj,
- bb->conversation->cancellable,
- _connected_to_buddy, pb);
- g_object_unref(client);
- return;
- }
- }
-
- purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
-
- conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
- if (conv != NULL)
- purple_conversation_write_system_message(conv,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_ERROR);
-
- bonjour_jabber_close_conversation(bb->conversation);
- bb->conversation = NULL;
- return;
- }
-
- bb->conversation->socket = conn;
- bb->conversation->input =
- g_io_stream_get_input_stream(G_IO_STREAM(conn));
- bb->conversation->output =
- g_io_stream_get_output_stream(G_IO_STREAM(conn));
-
- if (!bonjour_jabber_send_stream_init(bb->conversation, &error)) {
- PurpleConversation *conv = NULL;
- PurpleAccount *account = NULL;
-
- purple_debug_error("bonjour",
- "Error starting stream with buddy %s at "
- "%s:%d error: %s",
- purple_buddy_get_name(pb),
- bb->conversation->ip, bb->port_p2pj,
- error ? error->message : "(null)");
-
- account = purple_buddy_get_account(pb);
-
- conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
- if (conv != NULL)
- purple_conversation_write_system_message(conv,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_ERROR);
-
- bonjour_jabber_close_conversation(bb->conversation);
- bb->conversation = NULL;
- g_clear_error(&error);
- 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, (GSourceFunc)_client_socket_handler,
- bb->conversation, NULL);
- bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
-}
-
-void
-bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
- PurpleBuddy *pb = NULL;
- BonjourBuddy *bb = NULL;
-
- g_return_if_fail(bconv->ip != NULL);
- g_return_if_fail(bconv->pb == NULL);
-
- pb = purple_blist_find_buddy(bconv->account, bconv->buddy_name);
- if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
- const char *ip;
- GSList *tmp = bb->ips;
-
- purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
- purple_buddy_get_name(pb));
-
- /* Check that one of the buddy's IPs matches */
- while(tmp) {
- ip = tmp->data;
- if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
- PurpleConnection *pc = purple_account_get_connection(bconv->account);
- BonjourData *bd = purple_connection_get_protocol_data(pc);
- BonjourJabber *jdata = bd->jabber_data;
-
- purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
- purple_buddy_get_name(pb), 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_jabber_close_conversation(bb->conversation);
-
- bconv->pb = pb;
- bb->conversation = bconv;
-
- break;
- }
- tmp = tmp->next;
- }
- }
-
- /* We've failed to match a buddy - give up */
- if (bconv->pb == NULL) {
- /* This must be asynchronous because it destroys the parser and we
- * may be in the middle of parsing.
- */
- async_bonjour_jabber_close_conversation(bconv);
- }
-}
-
-
-void
-bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
- PurpleConnection *pc = purple_account_get_connection(bconv->account);
- BonjourData *bd = purple_connection_get_protocol_data(pc);
- BonjourJabber *jdata = bd->jabber_data;
- struct _match_buddies_by_address *mbba;
- GSList *buddies;
-
- mbba = g_new0(struct _match_buddies_by_address, 1);
- mbba->address = bconv->ip;
-
- buddies = purple_blist_find_buddies(jdata->account, NULL);
- g_slist_foreach(buddies, _match_buddies_by_address, mbba);
- g_slist_free(buddies);
-
- /* If there is exactly one match, use it */
- if(mbba->matched_buddies != NULL) {
- if(mbba->matched_buddies->next != NULL)
- purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
- else {
- PurpleBuddy *pb = mbba->matched_buddies->data;
- BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
-
- purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
- purple_buddy_get_name(pb), 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_jabber_close_conversation(bb->conversation);
-
- bconv->pb = pb;
- bb->conversation = bconv;
- }
- } else
- purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
-
- /* We've failed to match a buddy - give up */
- if (bconv->pb == NULL) {
- /* This must be asynchronous because it destroys the parser and we
- * may be in the middle of parsing.
- */
- async_bonjour_jabber_close_conversation(bconv);
- }
-
- g_slist_free(mbba->matched_buddies);
- g_free(mbba);
-}
-
-static PurpleBuddy *
-_find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
-{
- PurpleBuddy *pb = NULL;
- BonjourBuddy *bb = NULL;
-
- g_return_val_if_fail(jdata != NULL, NULL);
- g_return_val_if_fail(to != NULL, NULL);
-
- pb = purple_blist_find_buddy(jdata->account, to);
- if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
- /* You can not send a message to an offline buddy */
- 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);
- return NULL;
- }
-
- bb->conversation = bonjour_jabber_conv_new(pb, 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, pb);
- g_object_unref(client);
- }
- return pb;
-}
-
-int
-bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
-{
- PurpleXmlNode *message_node, *node, *node2;
- gchar *message, *xhtml;
- PurpleBuddy *pb;
- BonjourBuddy *bb;
- int ret;
-
- pb = _find_or_start_conversation(jdata, to);
- if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) {
- purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
- /* 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(pb, message) >= 0;
-
- g_free(message);
-
- return ret;
-}
-
-static gboolean
-_async_bonjour_jabber_close_conversation_cb(gpointer data) {
- BonjourJabberConversation *bconv = data;
- bonjour_jabber_close_conversation(bconv);
- return FALSE;
-}
-
-void
-async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
- PurpleConnection *pc = purple_account_get_connection(bconv->account);
- BonjourData *bd = purple_connection_get_protocol_data(pc);
- BonjourJabber *jdata = bd->jabber_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(bconv->pb != NULL) {
- BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
- if (bb->conversation == bconv)
- bb->conversation = NULL;
- }
-
- bconv->close_timeout = g_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv);
-}
-
-void
-bonjour_jabber_close_conversation(BonjourJabberConversation *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->jabber_data->pending_conversations = g_slist_remove(
- bd->jabber_data->pending_conversations, bconv);
- }
-
- /* Cancel any file transfers that are waiting to begin */
- /* There wont be any transfers if it hasn't been attached to a buddy */
- if (bconv->pb != NULL && bd != NULL) {
- GSList *xfers, *tmp_next;
- xfers = bd->xfer_lists;
- 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), purple_buddy_get_name(bconv->pb))
- && (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 the socket and remove the watcher */
- if (bconv->socket != NULL) {
- /* Send the end of the stream to the other end of the conversation */
- if (bconv->sent_stream_start == FULLY_SENT) {
- size_t len = strlen(STREAM_END);
- if (g_pollable_output_stream_write_nonblocking(
- G_POLLABLE_OUTPUT_STREAM(bconv->output),
- STREAM_END, len, bconv->cancellable,
- NULL) != (gssize)len) {
- purple_debug_error("bonjour",
- "bonjour_jabber_close_conversation: "
- "couldn't send data\n");
- }
- }
- /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
- purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
- G_INPUT_STREAM(bconv->input),
- G_OUTPUT_STREAM(bconv->output));
- }
- if (bconv->rx_handler != 0) {
- g_source_remove(bconv->rx_handler);
- bconv->rx_handler = 0;
- }
- if (bconv->tx_handler != 0) {
- g_source_remove(bconv->tx_handler);
- bconv->tx_handler = 0;
- }
-
- /* Cancel any pending operations. */
- if (bconv->cancellable != NULL) {
- g_cancellable_cancel(bconv->cancellable);
- g_clear_object(&bconv->cancellable);
- }
-
- /* Free all the data related to the conversation */
- g_clear_object(&bconv->socket);
- bconv->input = NULL;
- bconv->output = NULL;
-
- g_object_unref(G_OBJECT(bconv->tx_buf));
- if (bconv->stream_data != NULL) {
- struct _stream_start_data *ss = bconv->stream_data;
- g_free(ss->msg);
- g_free(ss);
- }
-
- if (bconv->context != NULL) {
- bonjour_parser_setup(bconv);
- }
-
- if (bconv->close_timeout != 0) {
- g_source_remove(bconv->close_timeout);
- }
-
- g_free(bconv->buddy_name);
- g_free(bconv->ip);
- g_free(bconv);
-}
-
-void
-bonjour_jabber_stop(BonjourJabber *jdata)
-{
- /* Close the server socket and remove the watcher */
- if (jdata->service) {
- g_socket_service_stop(jdata->service);
- g_socket_listener_close(G_SOCKET_LISTENER(jdata->service));
- g_clear_object(&jdata->service);
- }
-
- /* Close all the conversation sockets and remove all the watchers after sending end streams */
- if (!purple_account_is_disconnected(jdata->account)) {
- GSList *buddies, *l;
-
- buddies = purple_blist_find_buddies(jdata->account, NULL);
- for (l = buddies; l; l = l->next) {
- BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
- if (bb && bb->conversation) {
- /* Any ongoing connection attempt is cancelled
- * when a connection is destroyed */
- bonjour_jabber_close_conversation(bb->conversation);
- bb->conversation = NULL;
- }
- }
-
- g_slist_free(buddies);
- }
-
- g_slist_free_full(jdata->pending_conversations, (GDestroyNotify)bonjour_jabber_close_conversation);
-}
-
-XepIq *
-xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
-{
- PurpleXmlNode *iq_node = NULL;
- XepIq *iq = NULL;
-
- g_return_val_if_fail(data != NULL, NULL);
- g_return_val_if_fail(to != NULL, NULL);
- g_return_val_if_fail(id != NULL, NULL);
-
- iq_node = purple_xmlnode_new("iq");
-
- purple_xmlnode_set_attrib(iq_node, "to", to);
- purple_xmlnode_set_attrib(iq_node, "from", from);
- purple_xmlnode_set_attrib(iq_node, "id", id);
- switch (type) {
- case XEP_IQ_SET:
- purple_xmlnode_set_attrib(iq_node, "type", "set");
- break;
- case XEP_IQ_GET:
- purple_xmlnode_set_attrib(iq_node, "type", "get");
- break;
- case XEP_IQ_RESULT:
- purple_xmlnode_set_attrib(iq_node, "type", "result");
- break;
- case XEP_IQ_ERROR:
- purple_xmlnode_set_attrib(iq_node, "type", "error");
- break;
- case XEP_IQ_NONE:
- default:
- purple_xmlnode_set_attrib(iq_node, "type", "none");
- break;
- }
-
- iq = g_new0(XepIq, 1);
- iq->node = iq_node;
- iq->type = type;
- iq->data = ((BonjourData*)data)->jabber_data;
- iq->to = (char*)to;
-
- return iq;
-}
-
-static gboolean
-check_if_blocked(PurpleBuddy *pb)
-{
- gboolean blocked = FALSE;
- GSList *l = NULL;
- PurpleAccount *acc = purple_buddy_get_account(pb);
- const gchar *name;
-
- if(acc == NULL)
- return FALSE;
-
- l = purple_account_privacy_get_denied(acc);
- name = purple_buddy_get_name(pb);
-
- if(g_slist_find_custom(l, name, (GCompareFunc)purple_utf8_strcasecmp) != NULL) {
- const gchar *username = bonjour_get_jid(acc);
-
- purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
- blocked = TRUE;
- }
- return blocked;
-}
-
-static void
-xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
-{
- PurpleAccount *account;
- PurpleConnection *gc;
-
- if(check_if_blocked(pb))
- return;
-
- account = purple_buddy_get_account(pb);
- gc = purple_account_get_connection(account);
-
- if (purple_xmlnode_get_child(packet, "si") != NULL || purple_xmlnode_get_child(packet, "error") != NULL)
- xep_si_parse(gc, packet, pb);
- else
- xep_bytestreams_parse(gc, packet, pb);
-}
-
-int
-xep_iq_send_and_free(XepIq *iq)
-{
- int ret = -1;
- PurpleBuddy *pb = NULL;
-
- /* start the talk, reuse the message socket */
- pb = _find_or_start_conversation((BonjourJabber*) iq->data, iq->to);
- /* Send the message */
- if (pb != NULL) {
- /* Convert xml node into stream */
- gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
- ret = _send_data(pb, msg);
- g_free(msg);
- }
-
- purple_xmlnode_free(iq->node);
- iq->node = NULL;
- g_free(iq);
-
- return (ret >= 0) ? 0 : -1;
-}
-
-/* This returns a list containing all non-localhost IPs */
-GSList *
-bonjour_jabber_get_local_ips(int fd)
-{
- GSList *ips = NULL;
- const char *address_text;
- int ret;
-
-#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
- struct ifaddrs *ifap, *ifa;
- common_sockaddr_t addr;
- char addrstr[INET6_ADDRSTRLEN];
-
- ret = getifaddrs(&ifap);
- if (ret != 0) {
- const char *error = g_strerror(errno);
- purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
- return NULL;
- }
-
- for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
- if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
- continue;
-
- memcpy(&addr, ifa->ifa_addr, sizeof(addr));
- address_text = NULL;
- switch (addr.sa.sa_family) {
- case AF_INET:
- address_text = inet_ntop(addr.sa.sa_family,
- &addr.in.sin_addr,
- addrstr, sizeof(addrstr));
- break;
-#ifdef PF_INET6
- case AF_INET6:
- address_text = inet_ntop(addr.sa.sa_family,
- &addr.in6.sin6_addr,
- addrstr, sizeof(addrstr));
- break;
-#endif
- }
-
- if (address_text != NULL) {
- if (addr.sa.sa_family == AF_INET)
- ips = g_slist_append(ips, g_strdup(address_text));
- else
- ips = g_slist_prepend(ips, g_strdup(address_text));
- }
- }
-
- freeifaddrs(ifap);
-#else
- char *tmp;
- struct ifconf ifc;
- struct ifreq *ifr;
- char buffer[1024];
- struct sockaddr_in *sinptr;
- int source = fd;
-
- if (fd < 0)
- source = socket(PF_INET, SOCK_STREAM, 0);
-
- ifc.ifc_len = sizeof(buffer);
- ifc.ifc_req = (struct ifreq *)buffer;
- ret = ioctl(source, SIOCGIFCONF, &ifc);
-
- if (fd < 0)
- close(source);
-
- if (ret < 0) {
- const char *error = g_strerror(errno);
- purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
- return NULL;
- }
-
- tmp = buffer;
- while (tmp < buffer + ifc.ifc_len) {
- ifr = (struct ifreq *)tmp;
- tmp += HX_SIZE_OF_IFREQ(*ifr);
-
- if (ifr->ifr_addr.sa_family == AF_INET) {
- sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
- if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
- address_text = inet_ntoa(sinptr->sin_addr);
- ips = g_slist_prepend(ips, g_strdup(address_text));
- }
- }
- }
-#endif
-
- return ips;
-}
-
-void
-append_iface_if_linklocal(char *ip, guint32 interface_param) {
- struct in6_addr in6_addr;
- int len_remain = INET6_ADDRSTRLEN - strlen(ip);
-
- if (len_remain <= 1)
- return;
-
- if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
- !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
- return;
-
- snprintf(ip + strlen(ip), len_remain, "%%%d",
- interface_param);
-}
--- a/libpurple/protocols/bonjour/jabber.h Sun Oct 20 00:24:28 2019 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/**
- * @file jabber.h The Purple interface to mDNS and peer to peer Jabber.
- *
- * 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_JABBER_H
-#define PURPLE_BONJOUR_JABBER_H
-
-#include <libxml/parser.h>
-
-#include <purple.h>
-
-typedef struct
-{
- GSocketService *service;
- guint16 port;
- PurpleAccount *account;
- GSList *pending_conversations;
-} BonjourJabber;
-
-typedef struct
-{
- GCancellable *cancellable;
- GSocketConnection *socket;
- GInputStream *input;
- GOutputStream *output;
- guint rx_handler;
- guint tx_handler;
- guint close_timeout;
- PurpleCircularBuffer *tx_buf;
- int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
- gboolean recv_stream_start;
- gpointer stream_data;
- xmlParserCtxt *context;
- PurpleXmlNode *current;
- PurpleBuddy *pb;
- PurpleAccount *account;
-
- /* The following are only needed before attaching to a PurpleBuddy */
- gchar *buddy_name;
- gchar *ip;
- /* This points to a data entry in BonjourBuddy->ips */
- const gchar *ip_link;
-} BonjourJabberConversation;
-
-/**
- * Start listening for jabber connections.
- *
- * @return -1 if there was a problem, else returns the listening
- * port number.
- */
-gint bonjour_jabber_start(BonjourJabber *data);
-
-int bonjour_jabber_send_message(BonjourJabber *data, const char *to, const char *body);
-
-void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
-
-void async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
-
-void bonjour_jabber_stream_started(BonjourJabberConversation *bconv);
-
-void bonjour_jabber_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet);
-
-void bonjour_jabber_stop(BonjourJabber *data);
-
-void bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv);
-
-void bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv);
-
-typedef enum {
- XEP_IQ_SET,
- XEP_IQ_GET,
- XEP_IQ_RESULT,
- XEP_IQ_ERROR,
- XEP_IQ_NONE
-} XepIqType;
-
-typedef struct {
- XepIqType type;
- char *id;
- PurpleXmlNode *node;
- char *to;
- void *data;
-} XepIq;
-
-XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
-int xep_iq_send_and_free(XepIq *iq);
-GSList * bonjour_jabber_get_local_ips(int fd);
-
-void append_iface_if_linklocal(char *ip, guint32 interface_param);
-
-#endif /* PURPLE_BONJOUR_JABBER_H */
--- a/libpurple/protocols/bonjour/meson.build Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/meson.build Thu Oct 24 22:28:48 2019 -0400
@@ -3,8 +3,8 @@
'bonjour.h',
'buddy.c',
'buddy.h',
- 'jabber.c',
- 'jabber.h',
+ 'xmpp.c',
+ 'xmpp.h',
'mdns_common.c',
'mdns_common.h',
'mdns_interface.h',
--- a/libpurple/protocols/bonjour/parser.c Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/parser.c Thu Oct 24 22:28:48 2019 -0400
@@ -1,5 +1,5 @@
/*
- * purple - Bonjour Jabber XML parser stuff
+ * 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
@@ -25,11 +25,11 @@
#include <libxml/parser.h>
-#include "jabber.h"
#include "parser.h"
+#include "xmpp.h"
static gboolean
-parse_from_attrib_and_find_buddy(BonjourJabberConversation *bconv, int nb_attributes, const xmlChar **attributes) {
+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. */
@@ -37,7 +37,7 @@
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_jabber_conv_match_by_name(bconv);
+ bonjour_xmpp_conv_match_by_name(bconv);
return (bconv->pb != NULL);
}
@@ -52,7 +52,7 @@
int nb_namespaces, const xmlChar **namespaces,
int nb_attributes, int nb_defaulted, const xmlChar **attributes)
{
- BonjourJabberConversation *bconv = user_data;
+ BonjourXMPPConversation *bconv = user_data;
PurpleXmlNode *node;
int i;
@@ -66,7 +66,7 @@
if (bconv->pb == NULL)
parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes);
- bonjour_jabber_stream_started(bconv);
+ bonjour_xmpp_stream_started(bconv);
}
} else {
@@ -79,7 +79,7 @@
/* 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_jabber_conv_match_by_ip(bconv);
+ bonjour_xmpp_conv_match_by_ip(bconv);
if(bconv->current)
node = purple_xmlnode_new_child(bconv->current, (const char*) element_name);
@@ -113,7 +113,7 @@
bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
const xmlChar *prefix, const xmlChar *namespace)
{
- BonjourJabberConversation *bconv = user_data;
+ BonjourXMPPConversation *bconv = user_data;
if(!bconv->current) {
/* We don't keep a reference to the start stream PurpleXmlNode,
@@ -121,7 +121,7 @@
if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
/* Asynchronously close the conversation to prevent bonjour_parser_setup()
* being called from within this context */
- async_bonjour_jabber_close_conversation(bconv);
+ async_bonjour_xmpp_close_conversation(bconv);
return;
}
@@ -131,7 +131,7 @@
} else {
PurpleXmlNode *packet = bconv->current;
bconv->current = NULL;
- bonjour_jabber_process_packet(bconv->pb, packet);
+ bonjour_xmpp_process_packet(bconv->pb, packet);
purple_xmlnode_free(packet);
}
}
@@ -139,7 +139,7 @@
static void
bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
{
- BonjourJabberConversation *bconv = user_data;
+ BonjourXMPPConversation *bconv = user_data;
if(!bconv->current)
return;
@@ -153,9 +153,9 @@
static void
bonjour_parser_structured_error_handler(void *user_data, xmlErrorPtr error)
{
- BonjourJabberConversation *bconv = user_data;
+ BonjourXMPPConversation *bconv = user_data;
- purple_debug_error("jabber", "XML parser error for BonjourJabberConversation %p: "
+ purple_debug_error("xmpp", "XML parser error for BonjourXMPPConversation %p: "
"Domain %i, code %i, level %i: %s",
bconv,
error->domain, error->code, error->level,
@@ -198,7 +198,7 @@
};
void
-bonjour_parser_setup(BonjourJabberConversation *bconv)
+bonjour_parser_setup(BonjourXMPPConversation *bconv)
{
/* This seems backwards, but it makes sense. The libxml code creates
@@ -213,7 +213,7 @@
}
-void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len)
+void bonjour_parser_process(BonjourXMPPConversation *bconv, const char *buf, int len)
{
if (bconv->context == NULL) {
--- a/libpurple/protocols/bonjour/parser.h Sun Oct 20 00:24:28 2019 +0300
+++ b/libpurple/protocols/bonjour/parser.h Thu Oct 24 22:28:48 2019 -0400
@@ -1,5 +1,5 @@
/**
- * @file parser.h Bonjour Jabber XML parser functions
+ * @file parser.h Bonjour XMPP XML parser functions
*
* purple
*
@@ -26,9 +26,9 @@
#define PURPLE_BONJOUR_PARSER_H
#include "buddy.h"
-#include "jabber.h"
+#include "xmpp.h"
-void bonjour_parser_setup(BonjourJabberConversation *bconv);
-void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len);
+void bonjour_parser_setup(BonjourXMPPConversation *bconv);
+void bonjour_parser_process(BonjourXMPPConversation *bconv, const char *buf, int len);
#endif /* PURPLE_BONJOUR_PARSER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/xmpp.c Thu Oct 24 22:28:48 2019 -0400
@@ -0,0 +1,1472 @@
+/*
+ * 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 "internal.h"
+#include <purple.h>
+
+#ifndef _WIN32
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+
+/* Solaris */
+#if defined (__SVR4) && defined (__sun)
+#include <sys/sockio.h>
+#endif
+
+#include <glib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+#include "xmpp.h"
+#include "parser.h"
+#include "bonjour.h"
+#include "buddy.h"
+#include "bonjour_ft.h"
+
+#ifdef _SIZEOF_ADDR_IFREQ
+# define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
+#else
+# define HX_SIZE_OF_IFREQ(a) sizeof(a)
+#endif
+
+#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=\"xmpp:client\" xmlns:stream=\"http://etherx.xmpp.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, PurpleBuddy *pb);
+
+static BonjourXMPPConversation *
+bonjour_xmpp_conv_new(PurpleBuddy *pb, 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->pb = pb;
+ 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, PurpleBuddy *pb)
+{
+ PurpleXmlNode *body_node, *html_node, *events_node;
+ PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
+ gchar *body = NULL;
+
+ 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", "xmpp: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 */
+ purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
+
+ g_free(body);
+}
+
+struct _match_buddies_by_address {
+ const char *address;
+ GSList *matched_buddies;
+};
+
+static void
+_match_buddies_by_address(gpointer value, gpointer data)
+{
+ PurpleBuddy *pb = value;
+ BonjourBuddy *bb = NULL;
+ struct _match_buddies_by_address *mbba = data;
+
+ bb = purple_buddy_get_protocol_data(pb);
+
+ /*
+ * If the current PurpleBuddy's data is not null, then continue to determine
+ * whether one of the buddies IPs matches the target IP.
+ */
+ if (bb != NULL)
+ {
+ const char *ip;
+ GSList *tmp = bb->ips;
+
+ while(tmp) {
+ ip = tmp->data;
+ if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
+ mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
+ break;
+ }
+ tmp = tmp->next;
+ }
+ }
+}
+
+static void
+_send_data_write_cb(GObject *stream, gpointer data)
+{
+ PurpleBuddy *pb = data;
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+ BonjourXMPPConversation *bconv = bb->conversation;
+ gsize writelen;
+ gssize ret;
+ GError *error = NULL;
+
+ writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
+
+ if (writelen == 0) {
+ g_source_remove(bconv->tx_handler);
+ bconv->tx_handler = 0;
+ 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) {
+ PurpleConversation *conv = NULL;
+ PurpleAccount *account = NULL;
+
+ purple_debug_error(
+ "bonjour",
+ "Error sending message to buddy %s error: %s",
+ purple_buddy_get_name(pb),
+ error ? error->message : "(null)");
+
+ account = purple_buddy_get_account(pb);
+
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+ 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(PurpleBuddy *pb, char *message)
+{
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+ 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) {
+ PurpleConversation *conv;
+ PurpleAccount *account;
+
+ purple_debug_error(
+ "bonjour",
+ "Error sending message to buddy %s error: %s",
+ purple_buddy_get_name(pb),
+ error ? error->message : "(null)");
+
+ account = purple_buddy_get_account(pb);
+
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+ 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 (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,
+ (GSourceFunc)_send_data_write_cb,
+ pb, NULL);
+ bconv->tx_handler = g_source_attach(source, NULL);
+ }
+ purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
+ }
+
+ return ret;
+}
+
+void bonjour_xmpp_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet) {
+
+ g_return_if_fail(packet != NULL);
+ g_return_if_fail(pb != NULL);
+
+ if (purple_strequal(packet->name, "message"))
+ _xmpp_parse_and_write_message_to_ui(packet, pb);
+ else if (purple_strequal(packet->name, "iq"))
+ xep_iq_parse(packet, pb);
+ 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 = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
+
+ purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
+
+ if(bconv->pb != NULL)
+ bb = purple_buddy_get_protocol_data(bconv->pb);
+
+ /* Close the socket, clear the watcher and free memory */
+ bonjour_xmpp_close_conversation(bconv);
+ if(bb)
+ 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 (bconv->pb != NULL) {
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
+
+ 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 = purple_buddy_get_name(bconv->pb);
+ 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;
+ const char *bname = bconv->buddy_name;
+ BonjourBuddy *bb = NULL;
+
+ if(bconv->pb) {
+ bb = purple_buddy_get_protocol_data(bconv->pb);
+ bname = purple_buddy_get_name(bconv->pb);
+ }
+
+ 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(purple_conversations_find_im_with_account(bname, bconv->account));
+ 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 (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_source_remove(bconv->tx_handler);
+ bconv->tx_handler = 0;
+ 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 (bconv->pb != NULL)
+ bname = purple_buddy_get_name(bconv->pb);
+
+ /* 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 (bconv->pb) {
+ PurpleConversation *conv;
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
+ 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);
+ bconv->input = NULL;
+ bconv->output = NULL;
+ g_free(stream_start);
+
+ return FALSE;
+ }
+
+ /* This is unlikely to happen */
+ if (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, (GSourceFunc)_start_stream, bconv,
+ NULL);
+ bconv->tx_handler = g_source_attach(source, NULL);
+ } 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 (bconv->pb)
+ bname = purple_buddy_get_name(bconv->pb);
+
+ purple_debug_error(
+ "bonjour",
+ "Error starting stream with buddy %s at %s error: %s",
+ bname ? bname : "(unknown)", bconv->ip,
+ error ? error->message : "(null)");
+
+ if (bconv->pb) {
+ PurpleConversation *conv;
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
+ 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 recieve 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);
+ bconv->input = NULL;
+ bconv->output = NULL;
+
+ /* 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
+ && bconv->pb && 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, (GSourceFunc)_send_data_write_cb,
+ bconv->pb, NULL);
+ bconv->tx_handler = g_source_attach(source, NULL);
+ /* We can probably write the data right now. */
+ _send_data_write_cb(G_OBJECT(bconv->output), bconv->pb);
+ }
+}
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+static void
+_server_socket_handler(GSocketService *service, GSocketConnection *connection,
+ GObject *source_object, gpointer data)
+{
+ BonjourXMPP *jdata = data;
+ GSocketAddress *their_addr; /* connector's address information */
+ GInetAddress *their_inet_addr;
+ gchar *address_text;
+ struct _match_buddies_by_address *mbba;
+ BonjourXMPPConversation *bconv;
+ GSList *buddies;
+ 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);
+ mbba = g_new0(struct _match_buddies_by_address, 1);
+ mbba->address = address_text;
+
+ buddies = purple_blist_find_buddies(jdata->account, NULL);
+ g_slist_foreach(buddies, _match_buddies_by_address, mbba);
+ g_slist_free(buddies);
+
+ if (mbba->matched_buddies == NULL) {
+ purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
+ g_free(address_text);
+ g_free(mbba);
+ return;
+ }
+
+ g_slist_free(mbba->matched_buddies);
+ g_free(mbba);
+
+ /* 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_io_stream_get_input_stream(G_IO_STREAM(bconv->socket));
+ bconv->output =
+ 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, (GSourceFunc)_client_socket_handler,
+ bconv, NULL);
+ bconv->rx_handler = g_source_attach(source, NULL);
+ 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)
+{
+ PurpleBuddy *pb = user_data;
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+ GSocketConnection *conn;
+ GSource *rx_source;
+ GError *error = NULL;
+
+ conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
+ res, &error);
+
+ if (conn == NULL) {
+ PurpleConversation *conv = NULL;
+ PurpleAccount *account = NULL;
+ GSList *tmp;
+
+ if (error && error->code == G_IO_ERROR_CANCELLED) {
+ /* This conversation was closed before it started. */
+ g_error_free(error);
+ return;
+ }
+
+ purple_debug_error("bonjour",
+ "Error connecting to buddy %s at %s:%d "
+ "(%s); Trying next IP address",
+ purple_buddy_get_name(pb),
+ 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 recieved (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);
+
+ account = purple_buddy_get_account(pb);
+
+ 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",
+ purple_buddy_get_name(pb), 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);
+ g_socket_client_connect_to_host_async(
+ client, ip, bb->port_p2pj,
+ bb->conversation->cancellable,
+ _connected_to_buddy, pb);
+ g_object_unref(client);
+ return;
+ }
+ }
+
+ purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
+
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+ 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;
+ return;
+ }
+
+ bb->conversation->socket = conn;
+ bb->conversation->input =
+ g_io_stream_get_input_stream(G_IO_STREAM(conn));
+ bb->conversation->output =
+ g_io_stream_get_output_stream(G_IO_STREAM(conn));
+
+ if (!bonjour_xmpp_send_stream_init(bb->conversation, &error)) {
+ PurpleConversation *conv = NULL;
+ PurpleAccount *account = NULL;
+
+ purple_debug_error("bonjour",
+ "Error starting stream with buddy %s at "
+ "%s:%d error: %s",
+ purple_buddy_get_name(pb),
+ bb->conversation->ip, bb->port_p2pj,
+ error ? error->message : "(null)");
+
+ account = purple_buddy_get_account(pb);
+
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+ 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);
+ 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, (GSourceFunc)_client_socket_handler,
+ bb->conversation, NULL);
+ bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
+}
+
+void
+bonjour_xmpp_conv_match_by_name(BonjourXMPPConversation *bconv) {
+ PurpleBuddy *pb = NULL;
+ BonjourBuddy *bb = NULL;
+
+ g_return_if_fail(bconv->ip != NULL);
+ g_return_if_fail(bconv->pb == NULL);
+
+ pb = purple_blist_find_buddy(bconv->account, bconv->buddy_name);
+ if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
+ const char *ip;
+ GSList *tmp = bb->ips;
+
+ purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
+ purple_buddy_get_name(pb));
+
+ /* Check that one of the buddy's IPs matches */
+ while(tmp) {
+ ip = tmp->data;
+ if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
+ 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)\n",
+ purple_buddy_get_name(pb), 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->pb = pb;
+ bb->conversation = bconv;
+
+ break;
+ }
+ tmp = tmp->next;
+ }
+ }
+
+ /* We've failed to match a buddy - give up */
+ if (bconv->pb == NULL) {
+ /* This must be asynchronous because it destroys the parser and we
+ * may be in the middle of parsing.
+ */
+ async_bonjour_xmpp_close_conversation(bconv);
+ }
+}
+
+
+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;
+ struct _match_buddies_by_address *mbba;
+ GSList *buddies;
+
+ mbba = g_new0(struct _match_buddies_by_address, 1);
+ mbba->address = bconv->ip;
+
+ buddies = purple_blist_find_buddies(jdata->account, NULL);
+ g_slist_foreach(buddies, _match_buddies_by_address, mbba);
+ g_slist_free(buddies);
+
+ /* If there is exactly one match, use it */
+ if(mbba->matched_buddies != NULL) {
+ if(mbba->matched_buddies->next != NULL)
+ purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
+ else {
+ PurpleBuddy *pb = mbba->matched_buddies->data;
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+
+ purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
+ purple_buddy_get_name(pb), 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->pb = pb;
+ bb->conversation = bconv;
+ }
+ } else
+ purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
+
+ /* We've failed to match a buddy - give up */
+ if (bconv->pb == NULL) {
+ /* 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(mbba->matched_buddies);
+ g_free(mbba);
+}
+
+static PurpleBuddy *
+_find_or_start_conversation(BonjourXMPP *jdata, const gchar *to)
+{
+ PurpleBuddy *pb = NULL;
+ BonjourBuddy *bb = NULL;
+
+ g_return_val_if_fail(jdata != NULL, NULL);
+ g_return_val_if_fail(to != NULL, NULL);
+
+ pb = purple_blist_find_buddy(jdata->account, to);
+ if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
+ /* You can not send a message to an offline buddy */
+ 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);
+ return NULL;
+ }
+
+ bb->conversation = bonjour_xmpp_conv_new(pb, 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, pb);
+ g_object_unref(client);
+ }
+ return pb;
+}
+
+int
+bonjour_xmpp_send_message(BonjourXMPP *jdata, const gchar *to, const gchar *body)
+{
+ PurpleXmlNode *message_node, *node, *node2;
+ gchar *message, *xhtml;
+ PurpleBuddy *pb;
+ BonjourBuddy *bb;
+ int ret;
+
+ pb = _find_or_start_conversation(jdata, to);
+ if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) {
+ purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
+ /* 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, "xmpp: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(pb, message) >= 0;
+
+ g_free(message);
+
+ 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(bconv->pb != NULL) {
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
+ 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 wont be any transfers if it hasn't been attached to a buddy */
+ if (bconv->pb != NULL && bd != NULL) {
+ GSList *xfers, *tmp_next;
+ xfers = bd->xfer_lists;
+ 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), purple_buddy_get_name(bconv->pb))
+ && (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 the socket and remove the watcher */
+ if (bconv->socket != NULL) {
+ /* Send the end of the stream to the other end of the conversation */
+ if (bconv->sent_stream_start == FULLY_SENT) {
+ size_t len = strlen(STREAM_END);
+ if (g_pollable_output_stream_write_nonblocking(
+ G_POLLABLE_OUTPUT_STREAM(bconv->output),
+ STREAM_END, len, bconv->cancellable,
+ NULL) != (gssize)len) {
+ purple_debug_error("bonjour",
+ "bonjour_xmpp_close_conversation: "
+ "couldn't send data\n");
+ }
+ }
+ /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
+ purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
+ G_INPUT_STREAM(bconv->input),
+ G_OUTPUT_STREAM(bconv->output));
+ }
+ if (bconv->rx_handler != 0) {
+ g_source_remove(bconv->rx_handler);
+ bconv->rx_handler = 0;
+ }
+ if (bconv->tx_handler != 0) {
+ g_source_remove(bconv->tx_handler);
+ bconv->tx_handler = 0;
+ }
+
+ /* Cancel any pending operations. */
+ if (bconv->cancellable != NULL) {
+ g_cancellable_cancel(bconv->cancellable);
+ g_clear_object(&bconv->cancellable);
+ }
+
+ /* Free all the data related to the conversation */
+ g_clear_object(&bconv->socket);
+ bconv->input = NULL;
+ bconv->output = NULL;
+
+ g_object_unref(G_OBJECT(bconv->tx_buf));
+ if (bconv->stream_data != NULL) {
+ struct _stream_start_data *ss = bconv->stream_data;
+ g_free(ss->msg);
+ g_free(ss);
+ }
+
+ if (bconv->context != NULL) {
+ bonjour_parser_setup(bconv);
+ }
+
+ if (bconv->close_timeout != 0) {
+ g_source_remove(bconv->close_timeout);
+ }
+
+ g_free(bconv->buddy_name);
+ g_free(bconv->ip);
+ g_free(bconv);
+}
+
+void
+bonjour_xmpp_stop(BonjourXMPP *jdata)
+{
+ /* Close the server socket and remove the watcher */
+ if (jdata->service) {
+ g_socket_service_stop(jdata->service);
+ g_socket_listener_close(G_SOCKET_LISTENER(jdata->service));
+ g_clear_object(&jdata->service);
+ }
+
+ /* Close all the conversation sockets and remove all the watchers after sending end streams */
+ if (!purple_account_is_disconnected(jdata->account)) {
+ GSList *buddies, *l;
+
+ buddies = purple_blist_find_buddies(jdata->account, NULL);
+ for (l = buddies; l; l = l->next) {
+ BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
+ if (bb && bb->conversation) {
+ /* Any ongoing connection attempt is cancelled
+ * when a connection is destroyed */
+ bonjour_xmpp_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+ }
+ }
+
+ g_slist_free(buddies);
+ }
+
+ g_slist_free_full(jdata->pending_conversations, (GDestroyNotify)bonjour_xmpp_close_conversation);
+}
+
+XepIq *
+xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
+{
+ PurpleXmlNode *iq_node = NULL;
+ XepIq *iq = NULL;
+
+ g_return_val_if_fail(data != NULL, NULL);
+ g_return_val_if_fail(to != NULL, NULL);
+ g_return_val_if_fail(id != NULL, NULL);
+
+ iq_node = purple_xmlnode_new("iq");
+
+ purple_xmlnode_set_attrib(iq_node, "to", to);
+ purple_xmlnode_set_attrib(iq_node, "from", from);
+ purple_xmlnode_set_attrib(iq_node, "id", id);
+ switch (type) {
+ case XEP_IQ_SET:
+ purple_xmlnode_set_attrib(iq_node, "type", "set");
+ break;
+ case XEP_IQ_GET:
+ purple_xmlnode_set_attrib(iq_node, "type", "get");
+ break;
+ case XEP_IQ_RESULT:
+ purple_xmlnode_set_attrib(iq_node, "type", "result");
+ break;
+ case XEP_IQ_ERROR:
+ purple_xmlnode_set_attrib(iq_node, "type", "error");
+ break;
+ case XEP_IQ_NONE:
+ default:
+ purple_xmlnode_set_attrib(iq_node, "type", "none");
+ break;
+ }
+
+ iq = g_new0(XepIq, 1);
+ iq->node = iq_node;
+ iq->type = type;
+ iq->data = ((BonjourData*)data)->xmpp_data;
+ iq->to = (char*)to;
+
+ return iq;
+}
+
+static gboolean
+check_if_blocked(PurpleBuddy *pb)
+{
+ gboolean blocked = FALSE;
+ GSList *l = NULL;
+ PurpleAccount *acc = purple_buddy_get_account(pb);
+ const gchar *name;
+
+ if(acc == NULL)
+ return FALSE;
+
+ l = purple_account_privacy_get_denied(acc);
+ name = purple_buddy_get_name(pb);
+
+ if(g_slist_find_custom(l, name, (GCompareFunc)purple_utf8_strcasecmp) != NULL) {
+ const gchar *username = bonjour_get_jid(acc);
+
+ purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
+ blocked = TRUE;
+ }
+ return blocked;
+}
+
+static void
+xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
+{
+ PurpleAccount *account;
+ PurpleConnection *gc;
+
+ if(check_if_blocked(pb))
+ return;
+
+ account = purple_buddy_get_account(pb);
+ gc = purple_account_get_connection(account);
+
+ if (purple_xmlnode_get_child(packet, "si") != NULL || purple_xmlnode_get_child(packet, "error") != NULL)
+ xep_si_parse(gc, packet, pb);
+ else
+ xep_bytestreams_parse(gc, packet, pb);
+}
+
+int
+xep_iq_send_and_free(XepIq *iq)
+{
+ int ret = -1;
+ PurpleBuddy *pb = NULL;
+
+ /* start the talk, reuse the message socket */
+ pb = _find_or_start_conversation((BonjourXMPP*) iq->data, iq->to);
+ /* Send the message */
+ if (pb != NULL) {
+ /* Convert xml node into stream */
+ gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
+ ret = _send_data(pb, msg);
+ g_free(msg);
+ }
+
+ purple_xmlnode_free(iq->node);
+ iq->node = NULL;
+ g_free(iq);
+
+ return (ret >= 0) ? 0 : -1;
+}
+
+/* This returns a list containing all non-localhost IPs */
+GSList *
+bonjour_xmpp_get_local_ips(int fd)
+{
+ GSList *ips = NULL;
+ const char *address_text;
+ int ret;
+
+#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
+ struct ifaddrs *ifap, *ifa;
+ common_sockaddr_t addr;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ ret = getifaddrs(&ifap);
+ if (ret != 0) {
+ const char *error = g_strerror(errno);
+ purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
+ return NULL;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
+ continue;
+
+ memcpy(&addr, ifa->ifa_addr, sizeof(addr));
+ address_text = NULL;
+ switch (addr.sa.sa_family) {
+ case AF_INET:
+ address_text = inet_ntop(addr.sa.sa_family,
+ &addr.in.sin_addr,
+ addrstr, sizeof(addrstr));
+ break;
+#ifdef PF_INET6
+ case AF_INET6:
+ address_text = inet_ntop(addr.sa.sa_family,
+ &addr.in6.sin6_addr,
+ addrstr, sizeof(addrstr));
+ break;
+#endif
+ }
+
+ if (address_text != NULL) {
+ if (addr.sa.sa_family == AF_INET)
+ ips = g_slist_append(ips, g_strdup(address_text));
+ else
+ ips = g_slist_prepend(ips, g_strdup(address_text));
+ }
+ }
+
+ freeifaddrs(ifap);
+#else
+ char *tmp;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ char buffer[1024];
+ struct sockaddr_in *sinptr;
+ int source = fd;
+
+ if (fd < 0)
+ source = socket(PF_INET, SOCK_STREAM, 0);
+
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_req = (struct ifreq *)buffer;
+ ret = ioctl(source, SIOCGIFCONF, &ifc);
+
+ if (fd < 0)
+ close(source);
+
+ if (ret < 0) {
+ const char *error = g_strerror(errno);
+ purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
+ return NULL;
+ }
+
+ tmp = buffer;
+ while (tmp < buffer + ifc.ifc_len) {
+ ifr = (struct ifreq *)tmp;
+ tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+ if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
+ address_text = inet_ntoa(sinptr->sin_addr);
+ ips = g_slist_prepend(ips, g_strdup(address_text));
+ }
+ }
+ }
+#endif
+
+ return ips;
+}
+
+void
+append_iface_if_linklocal(char *ip, guint32 interface_param) {
+ struct in6_addr in6_addr;
+ int len_remain = INET6_ADDRSTRLEN - strlen(ip);
+
+ if (len_remain <= 1)
+ return;
+
+ if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
+ !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
+ return;
+
+ snprintf(ip + strlen(ip), len_remain, "%%%d",
+ interface_param);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/xmpp.h Thu Oct 24 22:28:48 2019 -0400
@@ -0,0 +1,112 @@
+/**
+ * @file xmpp.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_XMPP_H
+#define PURPLE_BONJOUR_XMPP_H
+
+#include <libxml/parser.h>
+
+#include <purple.h>
+
+typedef struct
+{
+ GSocketService *service;
+ guint16 port;
+ PurpleAccount *account;
+ GSList *pending_conversations;
+} BonjourXMPP;
+
+typedef struct
+{
+ GCancellable *cancellable;
+ GSocketConnection *socket;
+ GInputStream *input;
+ GOutputStream *output;
+ guint rx_handler;
+ guint tx_handler;
+ guint close_timeout;
+ PurpleCircularBuffer *tx_buf;
+ int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
+ gboolean recv_stream_start;
+ gpointer stream_data;
+ xmlParserCtxt *context;
+ PurpleXmlNode *current;
+ PurpleBuddy *pb;
+ PurpleAccount *account;
+
+ /* The following are only needed before attaching to a PurpleBuddy */
+ gchar *buddy_name;
+ gchar *ip;
+ /* This points to a data entry in BonjourBuddy->ips */
+ const gchar *ip_link;
+} BonjourXMPPConversation;
+
+/**
+ * Start listening for xmpp connections.
+ *
+ * @return -1 if there was a problem, else returns the listening
+ * port number.
+ */
+gint bonjour_xmpp_start(BonjourXMPP *data);
+
+int bonjour_xmpp_send_message(BonjourXMPP *data, const char *to, const char *body);
+
+void bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv);
+
+void async_bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv);
+
+void bonjour_xmpp_stream_started(BonjourXMPPConversation *bconv);
+
+void bonjour_xmpp_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet);
+
+void bonjour_xmpp_stop(BonjourXMPP *data);
+
+void bonjour_xmpp_conv_match_by_ip(BonjourXMPPConversation *bconv);
+
+void bonjour_xmpp_conv_match_by_name(BonjourXMPPConversation *bconv);
+
+typedef enum {
+ XEP_IQ_SET,
+ XEP_IQ_GET,
+ XEP_IQ_RESULT,
+ XEP_IQ_ERROR,
+ XEP_IQ_NONE
+} XepIqType;
+
+typedef struct {
+ XepIqType type;
+ char *id;
+ PurpleXmlNode *node;
+ char *to;
+ void *data;
+} XepIq;
+
+XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
+int xep_iq_send_and_free(XepIq *iq);
+GSList * bonjour_xmpp_get_local_ips(int fd);
+
+void append_iface_if_linklocal(char *ip, guint32 interface_param);
+
+#endif /* PURPLE_BONJOUR_XMPP_H */
--- a/po/POTFILES.in Sun Oct 20 00:24:28 2019 +0300
+++ b/po/POTFILES.in Thu Oct 24 22:28:48 2019 -0400
@@ -100,11 +100,11 @@
libpurple/protocols/bonjour/bonjour_ft.c
libpurple/protocols/bonjour/buddy.c
libpurple/protocols/bonjour/dns_sd_proxy.c
-libpurple/protocols/bonjour/jabber.c
libpurple/protocols/bonjour/mdns_avahi.c
libpurple/protocols/bonjour/mdns_common.c
libpurple/protocols/bonjour/mdns_dns_sd.c
libpurple/protocols/bonjour/parser.c
+libpurple/protocols/bonjour/xmpp.c
libpurple/protocols.c
libpurple/protocols/facebook/api.c
libpurple/protocols/facebook/data.c