pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Route GLib debug logging directly to the Finch debug window
2021-10-18, Elliott Sales de Andrade
1896a80ff8e3
Route GLib debug logging directly to the Finch debug window
Instead of flowing through purple debug, this merges some bits of the existing GLib log handler, and the purple debug printer.
Testing Done:
Open the Debug window an see some `GLib-*` outputs.
Reviewed at https://reviews.imfreedom.org/r/1057/
/*
* purple - Bonjour Protocol Plugin
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include
<config.h>
#include
<sys/types.h>
#include
<nice.h>
#include
<purple.h>
#include
"bonjour.h"
#include
"bonjour_ft.h"
static
void
bonjour_bytestreams_init
(
PurpleXfer
*
xfer
);
static
void
bonjour_bytestreams_connect
(
PurpleXfer
*
xfer
);
static
void
bonjour_xfer_init
(
PurpleXfer
*
xfer
);
static
void
bonjour_xfer_receive
(
PurpleConnection
*
pc
,
const
char
*
id
,
const
char
*
sid
,
const
char
*
from
,
goffset
filesize
,
const
char
*
filename
,
int
option
);
/* Look for specific xfer handle */
static
unsigned
int
next_id
=
0
;
struct
_XepXfer
{
PurpleXfer
parent
;
GCancellable
*
cancellable
;
void
*
data
;
char
*
filename
;
int
filesize
;
char
*
iq_id
;
char
*
sid
;
char
*
recv_id
;
char
*
buddy_ip
;
int
mode
;
GSocketClient
*
client
;
GSocketService
*
service
;
GSocketConnection
*
conn
;
char
rx_buf
[
0x500
];
char
tx_buf
[
0x500
];
char
*
jid
;
char
*
proxy_host
;
int
proxy_port
;
PurpleXmlNode
*
streamhost
;
PurpleBuddy
*
pb
;
};
G_DEFINE_DYNAMIC_TYPE
(
XepXfer
,
xep_xfer
,
PURPLE_TYPE_XFER
);
static
void
xep_ft_si_reject
(
BonjourData
*
bd
,
const
char
*
id
,
const
char
*
to
,
const
char
*
error_code
,
const
char
*
error_type
)
{
PurpleXmlNode
*
error_node
;
XepIq
*
iq
;
g_return_if_fail
(
error_code
!=
NULL
);
g_return_if_fail
(
error_type
!=
NULL
);
if
(
!
to
||
!
id
)
{
purple_debug_info
(
"bonjour"
,
"xep file transfer stream initialization error.
\n
"
);
return
;
}
iq
=
xep_iq_new
(
bd
,
XEP_IQ_ERROR
,
to
,
bonjour_get_jid
(
bd
->
xmpp_data
->
account
),
id
);
if
(
iq
==
NULL
)
return
;
error_node
=
purple_xmlnode_new_child
(
iq
->
node
,
"error"
);
purple_xmlnode_set_attrib
(
error_node
,
"code"
,
error_code
);
purple_xmlnode_set_attrib
(
error_node
,
"type"
,
error_type
);
/* TODO: Make this better */
if
(
purple_strequal
(
error_code
,
"403"
))
{
PurpleXmlNode
*
tmp_node
=
purple_xmlnode_new_child
(
error_node
,
"forbidden"
);
purple_xmlnode_set_namespace
(
tmp_node
,
"urn:ietf:params:xml:ns:xmpp-stanzas"
);
tmp_node
=
purple_xmlnode_new_child
(
error_node
,
"text"
);
purple_xmlnode_set_namespace
(
tmp_node
,
"urn:ietf:params:xml:ns:xmpp-stanzas"
);
purple_xmlnode_insert_data
(
tmp_node
,
"Offer Declined"
,
-1
);
}
else
if
(
purple_strequal
(
error_code
,
"404"
))
{
PurpleXmlNode
*
tmp_node
=
purple_xmlnode_new_child
(
error_node
,
"item-not-found"
);
purple_xmlnode_set_namespace
(
tmp_node
,
"urn:ietf:params:xml:ns:xmpp-stanzas"
);
}
xep_iq_send_and_free
(
iq
);
}
static
void
bonjour_xfer_cancel_send
(
PurpleXfer
*
xfer
)
{
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
purple_debug_info
(
"bonjour"
,
"Bonjour-xfer-cancel-send.
\n
"
);
g_cancellable_cancel
(
xf
->
cancellable
);
}
static
void
bonjour_xfer_request_denied
(
PurpleXfer
*
xfer
)
{
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
purple_debug_info
(
"bonjour"
,
"Bonjour-xfer-request-denied.
\n
"
);
if
(
xf
)
{
xep_ft_si_reject
(
xf
->
data
,
xf
->
sid
,
purple_xfer_get_remote_user
(
xfer
),
"403"
,
"cancel"
);
}
}
static
void
bonjour_xfer_cancel_recv
(
PurpleXfer
*
xfer
)
{
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
purple_debug_info
(
"bonjour"
,
"Bonjour-xfer-cancel-recv.
\n
"
);
g_cancellable_cancel
(
xf
->
cancellable
);
}
static
void
bonjour_xfer_end
(
PurpleXfer
*
xfer
)
{
purple_debug_info
(
"bonjour"
,
"Bonjour-xfer-end for xfer %p"
,
xfer
);
/* We can't allow the server side to close the connection until the client is complete,
* otherwise there is a RST resulting in an error on the client side */
if
(
purple_xfer_get_xfer_type
(
xfer
)
==
PURPLE_XFER_TYPE_SEND
&&
purple_xfer_is_completed
(
xfer
))
{
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
g_io_stream_close_async
(
G_IO_STREAM
(
xf
->
conn
),
G_PRIORITY_DEFAULT
,
xf
->
cancellable
,
NULL
,
NULL
);
purple_xfer_set_fd
(
xfer
,
-1
);
}
}
static
PurpleXfer
*
bonjour_si_xfer_find
(
BonjourData
*
bd
,
const
char
*
sid
,
const
char
*
from
)
{
GSList
*
xfers
;
PurpleXfer
*
xfer
;
XepXfer
*
xf
;
if
(
!
sid
||
!
from
||
!
bd
)
return
NULL
;
purple_debug_info
(
"bonjour"
,
"Look for sid=%s from=%s xferlists.
\n
"
,
sid
,
from
);
for
(
xfers
=
bd
->
xfer_lists
;
xfers
;
xfers
=
xfers
->
next
)
{
xfer
=
xfers
->
data
;
if
(
xfer
==
NULL
)
break
;
xf
=
XEP_XFER
(
xfer
);
if
(
xf
==
NULL
)
break
;
if
(
xf
->
sid
&&
purple_xfer_get_remote_user
(
xfer
)
&&
purple_strequal
(
xf
->
sid
,
sid
)
&&
purple_strequal
(
purple_xfer_get_remote_user
(
xfer
),
from
))
return
xfer
;
}
purple_debug_info
(
"bonjour"
,
"Look for xfer list fail
\n
"
);
return
NULL
;
}
static
void
xep_ft_si_offer
(
PurpleXfer
*
xfer
,
const
gchar
*
to
)
{
PurpleXmlNode
*
si_node
,
*
feature
,
*
field
,
*
file
,
*
x
;
XepIq
*
iq
;
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
BonjourData
*
bd
=
NULL
;
char
buf
[
32
];
if
(
!
xf
)
return
;
bd
=
xf
->
data
;
if
(
!
bd
)
return
;
purple_debug_info
(
"bonjour"
,
"xep file transfer stream initialization offer-id=%d.
\n
"
,
next_id
);
/* Assign stream id. */
g_free
(
xf
->
iq_id
);
xf
->
iq_id
=
g_strdup_printf
(
"%u"
,
next_id
++
);
iq
=
xep_iq_new
(
xf
->
data
,
XEP_IQ_SET
,
to
,
bonjour_get_jid
(
bd
->
xmpp_data
->
account
),
xf
->
iq_id
);
if
(
iq
==
NULL
)
return
;
/*Construct Stream initialization offer message.*/
si_node
=
purple_xmlnode_new_child
(
iq
->
node
,
"si"
);
purple_xmlnode_set_namespace
(
si_node
,
"http://jabber.org/protocol/si"
);
purple_xmlnode_set_attrib
(
si_node
,
"profile"
,
"http://jabber.org/protocol/si/profile/file-transfer"
);
g_free
(
xf
->
sid
);
xf
->
sid
=
g_strdup
(
xf
->
iq_id
);
purple_xmlnode_set_attrib
(
si_node
,
"id"
,
xf
->
sid
);
file
=
purple_xmlnode_new_child
(
si_node
,
"file"
);
purple_xmlnode_set_namespace
(
file
,
"http://jabber.org/protocol/si/profile/file-transfer"
);
purple_xmlnode_set_attrib
(
file
,
"name"
,
purple_xfer_get_filename
(
xfer
));
g_snprintf
(
buf
,
sizeof
(
buf
),
"%"
G_GOFFSET_FORMAT
,
purple_xfer_get_size
(
xfer
));
purple_xmlnode_set_attrib
(
file
,
"size"
,
buf
);
feature
=
purple_xmlnode_new_child
(
si_node
,
"feature"
);
purple_xmlnode_set_namespace
(
feature
,
"http://jabber.org/protocol/feature-neg"
);
x
=
purple_xmlnode_new_child
(
feature
,
"x"
);
purple_xmlnode_set_namespace
(
x
,
"jabber:x:data"
);
purple_xmlnode_set_attrib
(
x
,
"type"
,
"form"
);
field
=
purple_xmlnode_new_child
(
x
,
"field"
);
purple_xmlnode_set_attrib
(
field
,
"var"
,
"stream-method"
);
purple_xmlnode_set_attrib
(
field
,
"type"
,
"list-single"
);
if
(
xf
->
mode
&
XEP_BYTESTREAMS
)
{
PurpleXmlNode
*
option
=
purple_xmlnode_new_child
(
field
,
"option"
);
PurpleXmlNode
*
value
=
purple_xmlnode_new_child
(
option
,
"value"
);
purple_xmlnode_insert_data
(
value
,
"http://jabber.org/protocol/bytestreams"
,
-1
);
}
if
(
xf
->
mode
&
XEP_IBB
)
{
PurpleXmlNode
*
option
=
purple_xmlnode_new_child
(
field
,
"option"
);
PurpleXmlNode
*
value
=
purple_xmlnode_new_child
(
option
,
"value"
);
purple_xmlnode_insert_data
(
value
,
"http://jabber.org/protocol/ibb"
,
-1
);
}
xep_iq_send_and_free
(
iq
);
}
static
void
xep_ft_si_result
(
PurpleXfer
*
xfer
,
const
char
*
to
)
{
PurpleXmlNode
*
si_node
,
*
feature
,
*
field
,
*
value
,
*
x
;
XepIq
*
iq
;
XepXfer
*
xf
;
BonjourData
*
bd
;
if
(
!
to
||
!
xfer
)
return
;
xf
=
XEP_XFER
(
xfer
);
bd
=
xf
->
data
;
purple_debug_info
(
"bonjour"
,
"xep file transfer stream initialization result.
\n
"
);
iq
=
xep_iq_new
(
bd
,
XEP_IQ_RESULT
,
to
,
bonjour_get_jid
(
bd
->
xmpp_data
->
account
),
xf
->
iq_id
);
if
(
iq
==
NULL
)
return
;
si_node
=
purple_xmlnode_new_child
(
iq
->
node
,
"si"
);
purple_xmlnode_set_namespace
(
si_node
,
"http://jabber.org/protocol/si"
);
/*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
feature
=
purple_xmlnode_new_child
(
si_node
,
"feature"
);
purple_xmlnode_set_namespace
(
feature
,
"http://jabber.org/protocol/feature-neg"
);
x
=
purple_xmlnode_new_child
(
feature
,
"x"
);
purple_xmlnode_set_namespace
(
x
,
"jabber:x:data"
);
purple_xmlnode_set_attrib
(
x
,
"type"
,
"submit"
);
field
=
purple_xmlnode_new_child
(
x
,
"field"
);
purple_xmlnode_set_attrib
(
field
,
"var"
,
"stream-method"
);
value
=
purple_xmlnode_new_child
(
field
,
"value"
);
purple_xmlnode_insert_data
(
value
,
"http://jabber.org/protocol/bytestreams"
,
-1
);
xep_iq_send_and_free
(
iq
);
}
/**
* Frees the whole tree of an xml node
*
* First determines the root of the xml tree and then frees the whole tree
* from there.
*
* @param node The node to free the tree from
*/
static
void
purple_xmlnode_free_tree
(
PurpleXmlNode
*
node
)
{
g_return_if_fail
(
node
!=
NULL
);
while
(
purple_xmlnode_get_parent
(
node
))
node
=
purple_xmlnode_get_parent
(
node
);
purple_xmlnode_free
(
node
);
}
PurpleXfer
*
bonjour_new_xfer
(
PurpleProtocolXfer
*
prplxfer
,
PurpleConnection
*
gc
,
const
char
*
who
)
{
PurpleXfer
*
xfer
;
XepXfer
*
xep_xfer
;
BonjourData
*
bd
;
if
(
who
==
NULL
||
gc
==
NULL
)
return
NULL
;
purple_debug_info
(
"bonjour"
,
"Bonjour-new-xfer to %s.
\n
"
,
who
);
bd
=
purple_connection_get_protocol_data
(
gc
);
if
(
bd
==
NULL
)
return
NULL
;
/* Build the file transfer handle */
xep_xfer
=
g_object_new
(
XEP_TYPE_XFER
,
"account"
,
purple_connection_get_account
(
gc
),
"type"
,
PURPLE_XFER_TYPE_SEND
,
"remote-user"
,
who
,
NULL
);
xfer
=
PURPLE_XFER
(
xep_xfer
);
xep_xfer
->
data
=
bd
;
purple_debug_info
(
"bonjour"
,
"Bonjour-new-xfer bd=%p data=%p.
\n
"
,
bd
,
xep_xfer
->
data
);
/* We don't support IBB yet */
/*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
xep_xfer
->
mode
=
XEP_BYTESTREAMS
;
xep_xfer
->
sid
=
NULL
;
bd
->
xfer_lists
=
g_slist_append
(
bd
->
xfer_lists
,
xfer
);
return
xfer
;
}
void
bonjour_send_file
(
PurpleProtocolXfer
*
prplxfer
,
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
file
)
{
PurpleXfer
*
xfer
;
g_return_if_fail
(
gc
!=
NULL
);
g_return_if_fail
(
who
!=
NULL
);
purple_debug_info
(
"bonjour"
,
"Bonjour-send-file to=%s.
\n
"
,
who
);
xfer
=
bonjour_new_xfer
(
prplxfer
,
gc
,
who
);
if
(
file
)
purple_xfer_request_accepted
(
xfer
,
file
);
else
purple_xfer_request
(
xfer
);
}
static
void
bonjour_xfer_init
(
PurpleXfer
*
xfer
)
{
PurpleBuddy
*
buddy
;
BonjourBuddy
*
bb
;
XepXfer
*
xf
;
xf
=
XEP_XFER
(
xfer
);
purple_debug_info
(
"bonjour"
,
"Bonjour-xfer-init.
\n
"
);
buddy
=
purple_blist_find_buddy
(
purple_xfer_get_account
(
xfer
),
purple_xfer_get_remote_user
(
xfer
));
/* this buddy is offline. */
if
(
buddy
==
NULL
||
(
bb
=
purple_buddy_get_protocol_data
(
buddy
))
==
NULL
)
return
;
/* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
if
(
bb
->
ips
)
xf
->
buddy_ip
=
g_strdup
(
bb
->
ips
->
data
);
if
(
purple_xfer_get_xfer_type
(
xfer
)
==
PURPLE_XFER_TYPE_SEND
)
{
/* initiate file transfer, send SI offer. */
purple_debug_info
(
"bonjour"
,
"Bonjour xfer type is PURPLE_XFER_TYPE_SEND.
\n
"
);
xep_ft_si_offer
(
xfer
,
purple_xfer_get_remote_user
(
xfer
));
}
else
{
/* accept file transfer request, send SI result. */
xep_ft_si_result
(
xfer
,
purple_xfer_get_remote_user
(
xfer
));
purple_debug_info
(
"bonjour"
,
"Bonjour xfer type is PURPLE_XFER_TYPE_RECEIVE.
\n
"
);
}
}
void
xep_si_parse
(
PurpleConnection
*
pc
,
PurpleXmlNode
*
packet
,
PurpleBuddy
*
pb
)
{
const
char
*
type
,
*
id
;
BonjourData
*
bd
;
PurpleXfer
*
xfer
;
const
gchar
*
name
=
NULL
;
g_return_if_fail
(
pc
!=
NULL
);
g_return_if_fail
(
packet
!=
NULL
);
g_return_if_fail
(
pb
!=
NULL
);
bd
=
purple_connection_get_protocol_data
(
pc
);
if
(
bd
==
NULL
)
return
;
purple_debug_info
(
"bonjour"
,
"xep-si-parse.
\n
"
);
name
=
purple_buddy_get_name
(
pb
);
type
=
purple_xmlnode_get_attrib
(
packet
,
"type"
);
id
=
purple_xmlnode_get_attrib
(
packet
,
"id"
);
if
(
!
type
)
return
;
if
(
purple_strequal
(
type
,
"set"
))
{
PurpleXmlNode
*
si
;
gboolean
parsed_receive
=
FALSE
;
si
=
purple_xmlnode_get_child
(
packet
,
"si"
);
purple_debug_info
(
"bonjour"
,
"si offer Message type - SET.
\n
"
);
if
(
si
)
{
const
char
*
profile
;
profile
=
purple_xmlnode_get_attrib
(
si
,
"profile"
);
if
(
purple_strequal
(
profile
,
"http://jabber.org/protocol/si/profile/file-transfer"
))
{
const
char
*
filename
=
NULL
,
*
filesize_str
=
NULL
;
goffset
filesize
=
0
;
PurpleXmlNode
*
file
;
const
char
*
sid
=
purple_xmlnode_get_attrib
(
si
,
"id"
);
if
((
file
=
purple_xmlnode_get_child
(
si
,
"file"
)))
{
filename
=
purple_xmlnode_get_attrib
(
file
,
"name"
);
if
((
filesize_str
=
purple_xmlnode_get_attrib
(
file
,
"size"
)))
filesize
=
g_ascii_strtoll
(
filesize_str
,
NULL
,
10
);
}
/* TODO: Make sure that it is advertising a bytestreams transfer */
if
(
filename
)
{
bonjour_xfer_receive
(
pc
,
id
,
sid
,
name
,
filesize
,
filename
,
XEP_BYTESTREAMS
);
parsed_receive
=
TRUE
;
}
}
}
if
(
!
parsed_receive
)
{
BonjourData
*
bd
=
purple_connection_get_protocol_data
(
pc
);
purple_debug_info
(
"bonjour"
,
"rejecting unrecognized si SET offer.
\n
"
);
xep_ft_si_reject
(
bd
,
id
,
name
,
"403"
,
"cancel"
);
/*TODO: Send Cancel (501) */
}
}
else
if
(
purple_strequal
(
type
,
"result"
))
{
purple_debug_info
(
"bonjour"
,
"si offer Message type - RESULT.
\n
"
);
xfer
=
bonjour_si_xfer_find
(
bd
,
id
,
name
);
if
(
xfer
==
NULL
)
{
BonjourData
*
bd
=
purple_connection_get_protocol_data
(
pc
);
purple_debug_info
(
"bonjour"
,
"xfer find fail.
\n
"
);
xep_ft_si_reject
(
bd
,
id
,
name
,
"403"
,
"cancel"
);
}
else
bonjour_bytestreams_init
(
xfer
);
}
else
if
(
purple_strequal
(
type
,
"error"
))
{
purple_debug_info
(
"bonjour"
,
"si offer Message type - ERROR.
\n
"
);
xfer
=
bonjour_si_xfer_find
(
bd
,
id
,
name
);
if
(
xfer
==
NULL
)
purple_debug_info
(
"bonjour"
,
"xfer find fail.
\n
"
);
else
purple_xfer_cancel_remote
(
xfer
);
}
else
purple_debug_info
(
"bonjour"
,
"si offer Message type - Unknown-%s.
\n
"
,
type
);
}
/**
* Will compare a host with a buddy_ip.
*
* Additionally to a common 'purple_strequal(host, buddy_ip)', it will also return TRUE
* if 'host' is a link local IPv6 address without an appended interface
* identifier and 'buddy_ip' string is "host" + "%iface".
*
* Note: This may theoretically result in the attempt to connect to the wrong
* host, because we do not know for sure which interface the according link
* local IPv6 address might relate to and RFC4862 for instance only ensures the
* uniqueness of this address on a given link. So we could possibly have two
* distinct buddies with the same ipv6 link local address on two distinct
* interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with
* link local ip addresses properly...
* However, in practice the possibility for such a conflict is relatively low
* (2011 - might be different in the future though?).
*
* @param host ipv4 or ipv6 address string
* @param buddy_ip ipv4 or ipv6 address string
* @return TRUE if they match, FALSE otherwise
*/
static
gboolean
xep_cmp_addr
(
const
char
*
host
,
const
char
*
buddy_ip
)
{
GInetAddress
*
addr
=
NULL
;
addr
=
g_inet_address_new_from_string
(
host
);
if
(
addr
!=
NULL
&&
g_inet_address_get_family
(
addr
)
==
G_SOCKET_FAMILY_IPV6
&&
g_inet_address_get_is_link_local
(
addr
))
{
g_clear_object
(
&
addr
);
if
(
strlen
(
buddy_ip
)
<=
strlen
(
host
)
||
buddy_ip
[
strlen
(
host
)]
!=
'%'
)
{
return
FALSE
;
}
return
!
strncmp
(
host
,
buddy_ip
,
strlen
(
host
));
}
else
{
g_clear_object
(
&
addr
);
return
purple_strequal
(
host
,
buddy_ip
);
}
}
static
inline
gint
xep_addr_differ
(
const
char
*
buddy_ip
,
const
char
*
host
)
{
return
!
xep_cmp_addr
(
host
,
buddy_ip
);
}
/**
* Create and insert an identical twin
*
* Creates a copy of the specified node and inserts it right after
* this original node.
*
* @param node The node to clone
* @return A pointer to the new, cloned twin if successful
* or NULL otherwise.
*/
static
PurpleXmlNode
*
purple_xmlnode_insert_twin_copy
(
PurpleXmlNode
*
node
)
{
PurpleXmlNode
*
copy
;
g_return_val_if_fail
(
node
!=
NULL
,
NULL
);
copy
=
purple_xmlnode_copy
(
node
);
g_return_val_if_fail
(
copy
!=
NULL
,
NULL
);
copy
->
next
=
node
->
next
;
node
->
next
=
copy
;
return
copy
;
}
/**
* Tries to append an interface scope to an IPv6 link local address.
*
* If the given address is a link local IPv6 address (with no
* interface scope) then we try to determine all fitting interfaces
* from our Bonjour IP address list.
*
* For any such found matches we insert a copy of our current xml
* streamhost entry right after this streamhost entry and append
* the determined interface to the host address of this copy.
*
* @param cur_streamhost The XML streamhost node we examine
* @param host The host address to examine in text form
* @param pb Buddy to get the list of link local IPv6 addresses
* and their interface from
* @return Returns TRUE if the specified 'host' address is a
* link local IPv6 address with no interface scope.
* Otherwise returns FALSE.
*/
static
gboolean
add_ipv6_link_local_ifaces
(
PurpleXmlNode
*
cur_streamhost
,
const
char
*
host
,
PurpleBuddy
*
pb
)
{
PurpleXmlNode
*
new_streamhost
=
NULL
;
GInetAddress
*
addr
;
BonjourBuddy
*
bb
;
GSList
*
ip_elem
;
addr
=
g_inet_address_new_from_string
(
host
);
if
(
addr
==
NULL
||
g_inet_address_get_family
(
addr
)
!=
G_SOCKET_FAMILY_IPV6
||
!
g_inet_address_get_is_link_local
(
addr
)
||
strchr
(
host
,
'%'
))
{
g_clear_object
(
&
addr
);
return
FALSE
;
}
g_clear_object
(
&
addr
);
bb
=
purple_buddy_get_protocol_data
(
pb
);
for
(
ip_elem
=
bb
->
ips
;
(
ip_elem
=
g_slist_find_custom
(
ip_elem
,
host
,
(
GCompareFunc
)
&
xep_addr_differ
));
ip_elem
=
ip_elem
->
next
)
{
purple_debug_info
(
"bonjour"
,
"Inserting an PurpleXmlNode twin copy for %s with new host address %s
\n
"
,
host
,
(
char
*
)
ip_elem
->
data
);
new_streamhost
=
purple_xmlnode_insert_twin_copy
(
cur_streamhost
);
purple_xmlnode_set_attrib
(
new_streamhost
,
"host"
,
ip_elem
->
data
);
}
if
(
!
new_streamhost
)
purple_debug_info
(
"bonjour"
,
"No interface for this IPv6 link local address found: %s
\n
"
,
host
);
return
TRUE
;
}
static
gboolean
__xep_bytestreams_parse
(
PurpleBuddy
*
pb
,
PurpleXfer
*
xfer
,
PurpleXmlNode
*
streamhost
,
const
char
*
iq_id
)
{
char
*
tmp_iq_id
;
const
char
*
jid
,
*
host
,
*
port
;
int
portnum
;
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
for
(;
streamhost
;
streamhost
=
purple_xmlnode_get_next_twin
(
streamhost
))
{
if
(
!
(
jid
=
purple_xmlnode_get_attrib
(
streamhost
,
"jid"
))
||
!
(
host
=
purple_xmlnode_get_attrib
(
streamhost
,
"host"
))
||
!
(
port
=
purple_xmlnode_get_attrib
(
streamhost
,
"port"
))
||
!
(
portnum
=
atoi
(
port
)))
{
purple_debug_info
(
"bonjour"
,
"bytestream offer Message parse error.
\n
"
);
continue
;
}
/* skip IPv6 link local addresses with no interface scope
* (but try to add a new one with an interface scope then) */
if
(
add_ipv6_link_local_ifaces
(
streamhost
,
host
,
pb
))
continue
;
tmp_iq_id
=
g_strdup
(
iq_id
);
g_free
(
xf
->
iq_id
);
g_free
(
xf
->
jid
);
g_free
(
xf
->
proxy_host
);
xf
->
iq_id
=
tmp_iq_id
;
xf
->
jid
=
g_strdup
(
jid
);
xf
->
proxy_host
=
g_strdup
(
host
);
xf
->
proxy_port
=
portnum
;
xf
->
streamhost
=
streamhost
;
xf
->
pb
=
pb
;
purple_debug_info
(
"bonjour"
,
"bytestream offer parse"
"jid=%s host=%s port=%d.
\n
"
,
jid
,
host
,
portnum
);
bonjour_bytestreams_connect
(
xfer
);
return
TRUE
;
}
return
FALSE
;
}
void
xep_bytestreams_parse
(
PurpleConnection
*
pc
,
PurpleXmlNode
*
packet
,
PurpleBuddy
*
pb
)
{
const
char
*
type
,
*
from
,
*
iq_id
,
*
sid
;
PurpleXmlNode
*
query
,
*
streamhost
;
BonjourData
*
bd
;
PurpleXfer
*
xfer
;
g_return_if_fail
(
pc
!=
NULL
);
g_return_if_fail
(
packet
!=
NULL
);
g_return_if_fail
(
pb
!=
NULL
);
bd
=
purple_connection_get_protocol_data
(
pc
);
if
(
bd
==
NULL
)
return
;
purple_debug_info
(
"bonjour"
,
"xep-bytestreams-parse.
\n
"
);
type
=
purple_xmlnode_get_attrib
(
packet
,
"type"
);
from
=
purple_buddy_get_name
(
pb
);
query
=
purple_xmlnode_get_child
(
packet
,
"query"
);
if
(
!
type
)
return
;
query
=
purple_xmlnode_copy
(
query
);
if
(
!
query
)
return
;
if
(
!
purple_strequal
(
type
,
"set"
))
{
purple_debug_info
(
"bonjour"
,
"bytestream offer Message type - Unknown-%s.
\n
"
,
type
);
return
;
}
purple_debug_info
(
"bonjour"
,
"bytestream offer Message type - SET.
\n
"
);
iq_id
=
purple_xmlnode_get_attrib
(
packet
,
"id"
);
sid
=
purple_xmlnode_get_attrib
(
query
,
"sid"
);
xfer
=
bonjour_si_xfer_find
(
bd
,
sid
,
from
);
streamhost
=
purple_xmlnode_get_child
(
query
,
"streamhost"
);
if
(
xfer
&&
streamhost
&&
__xep_bytestreams_parse
(
pb
,
xfer
,
streamhost
,
iq_id
))
return
;
/* success */
purple_debug_error
(
"bonjour"
,
"Didn't find an acceptable streamhost.
\n
"
);
if
(
iq_id
&&
xfer
!=
NULL
)
xep_ft_si_reject
(
bd
,
iq_id
,
purple_xfer_get_remote_user
(
xfer
),
"404"
,
"cancel"
);
}
static
void
bonjour_xfer_receive
(
PurpleConnection
*
pc
,
const
char
*
id
,
const
char
*
sid
,
const
char
*
from
,
goffset
filesize
,
const
char
*
filename
,
int
option
)
{
PurpleXfer
*
xfer
;
XepXfer
*
xf
;
BonjourData
*
bd
;
if
(
pc
==
NULL
||
id
==
NULL
||
from
==
NULL
)
return
;
bd
=
purple_connection_get_protocol_data
(
pc
);
if
(
bd
==
NULL
)
return
;
purple_debug_info
(
"bonjour"
,
"bonjour-xfer-receive.
\n
"
);
/* Build the file transfer handle */
xf
=
g_object_new
(
XEP_TYPE_XFER
,
"account"
,
purple_connection_get_account
(
pc
),
"type"
,
PURPLE_XFER_TYPE_RECEIVE
,
"remote-user"
,
from
,
NULL
);
xfer
=
PURPLE_XFER
(
xf
);
xf
->
data
=
bd
;
purple_xfer_set_filename
(
xfer
,
filename
);
xf
->
iq_id
=
g_strdup
(
id
);
xf
->
sid
=
g_strdup
(
sid
);
if
(
filesize
>
0
)
purple_xfer_set_size
(
xfer
,
filesize
);
bd
->
xfer_lists
=
g_slist_append
(
bd
->
xfer_lists
,
xfer
);
purple_xfer_request
(
xfer
);
}
static
void
bonjour_sock5_request_cb
(
GObject
*
source
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
user_data
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
gsize
bytes_written
=
0
;
GError
*
error
=
NULL
;
GSocket
*
sock
=
NULL
;
gint
fd
=
-1
;
purple_debug_info
(
"bonjour"
,
"bonjour_sock5_request_cb"
);
if
(
!
g_output_stream_write_all_finish
(
G_OUTPUT_STREAM
(
source
),
result
,
&
bytes_written
,
&
error
))
{
if
(
error
->
code
!=
G_IO_ERROR_CANCELLED
)
{
purple_xfer_cancel_remote
(
xfer
);
}
g_clear_error
(
&
error
);
return
;
}
sock
=
g_socket_connection_get_socket
(
xf
->
conn
);
fd
=
g_socket_get_fd
(
sock
);
purple_debug_info
(
"bonjour"
,
"Accepted SOCKS5 ft connection - fd=%d"
,
fd
);
_purple_network_set_common_socket_flags
(
fd
);
purple_xfer_start
(
xfer
,
fd
,
NULL
,
-1
);
}
static
void
bonjour_sock5_read_connect_cb
(
GObject
*
source
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
user_data
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
GOutputStream
*
output
=
NULL
;
gsize
bytes_read
=
0
;
GError
*
error
=
NULL
;
purple_debug_info
(
"bonjour"
,
"bonjour_sock5_request_state4_cb"
);
if
(
!
g_input_stream_read_all_finish
(
G_INPUT_STREAM
(
source
),
result
,
&
bytes_read
,
&
error
))
{
if
(
error
->
code
!=
G_IO_ERROR_CANCELLED
)
{
purple_xfer_cancel_remote
(
xfer
);
}
g_clear_error
(
&
error
);
return
;
}
xf
->
tx_buf
[
0
]
=
0x05
;
xf
->
tx_buf
[
1
]
=
0x00
;
xf
->
tx_buf
[
2
]
=
0x00
;
xf
->
tx_buf
[
3
]
=
0x03
;
xf
->
tx_buf
[
4
]
=
strlen
(
xf
->
buddy_ip
);
memcpy
(
xf
->
tx_buf
+
5
,
xf
->
buddy_ip
,
strlen
(
xf
->
buddy_ip
));
xf
->
tx_buf
[
5
+
strlen
(
xf
->
buddy_ip
)]
=
0x00
;
xf
->
tx_buf
[
6
+
strlen
(
xf
->
buddy_ip
)]
=
0x00
;
output
=
g_io_stream_get_output_stream
(
G_IO_STREAM
(
xf
->
conn
));
g_output_stream_write_all_async
(
output
,
xf
->
tx_buf
,
7
+
strlen
(
xf
->
buddy_ip
),
G_PRIORITY_DEFAULT
,
xf
->
cancellable
,
bonjour_sock5_request_cb
,
xfer
);
}
static
void
bonjour_sock5_write_server_method_cb
(
GObject
*
source
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
user_data
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
GInputStream
*
input
=
NULL
;
gsize
bytes_written
=
0
;
GError
*
error
=
NULL
;
purple_debug_info
(
"bonjour"
,
"bonjour_sock5_request_state3_cb"
);
if
(
!
g_output_stream_write_all_finish
(
G_OUTPUT_STREAM
(
source
),
result
,
&
bytes_written
,
&
error
))
{
if
(
error
->
code
!=
G_IO_ERROR_CANCELLED
)
{
purple_xfer_cancel_remote
(
xfer
);
}
g_clear_error
(
&
error
);
return
;
}
input
=
g_io_stream_get_input_stream
(
G_IO_STREAM
(
xf
->
conn
));
g_input_stream_read_all_async
(
input
,
xf
->
rx_buf
,
20
,
G_PRIORITY_DEFAULT
,
xf
->
cancellable
,
bonjour_sock5_read_connect_cb
,
xfer
);
}
static
void
bonjour_sock5_read_client_version_cb
(
GObject
*
source
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
user_data
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
GOutputStream
*
output
=
NULL
;
gsize
bytes_read
=
0
;
GError
*
error
=
NULL
;
purple_debug_info
(
"bonjour"
,
"bonjour_sock5_read_client_version_cb"
);
if
(
!
g_input_stream_read_all_finish
(
G_INPUT_STREAM
(
source
),
result
,
&
bytes_read
,
&
error
))
{
if
(
error
->
code
!=
G_IO_ERROR_CANCELLED
)
{
purple_xfer_cancel_remote
(
xfer
);
}
g_clear_error
(
&
error
);
return
;
}
xf
->
tx_buf
[
0
]
=
0x05
;
xf
->
tx_buf
[
1
]
=
0x00
;
output
=
g_io_stream_get_output_stream
(
G_IO_STREAM
(
xf
->
conn
));
g_output_stream_write_all_async
(
output
,
xf
->
tx_buf
,
2
,
G_PRIORITY_DEFAULT
,
xf
->
cancellable
,
bonjour_sock5_write_server_method_cb
,
xfer
);
}
static
void
bonjour_sock5_incoming_cb
(
GSocketService
*
service
,
GSocketConnection
*
connection
,
GObject
*
source_object
,
G_GNUC_UNUSED
gpointer
data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
source_object
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
GInputStream
*
input
=
NULL
;
if
(
xf
==
NULL
)
{
return
;
}
purple_debug_info
(
"bonjour"
,
"bonjour_sock5_incoming_cb"
);
xf
->
conn
=
g_object_ref
(
connection
);
g_socket_service_stop
(
xf
->
service
);
g_clear_object
(
&
xf
->
service
);
purple_debug_info
(
"bonjour"
,
"Accepted SOCKS5 ft connection"
);
input
=
g_io_stream_get_input_stream
(
G_IO_STREAM
(
xf
->
conn
));
g_input_stream_read_all_async
(
input
,
xf
->
rx_buf
,
3
,
G_PRIORITY_DEFAULT
,
xf
->
cancellable
,
bonjour_sock5_read_client_version_cb
,
xfer
);
}
static
void
bonjour_bytestreams_init
(
PurpleXfer
*
xfer
)
{
XepXfer
*
xf
;
XepIq
*
iq
;
PurpleXmlNode
*
query
,
*
streamhost
;
guint16
port
;
gchar
*
port_str
;
GList
*
local_ips
;
BonjourData
*
bd
;
GError
*
error
=
NULL
;
if
(
xfer
==
NULL
)
{
return
;
}
purple_debug_info
(
"bonjour"
,
"Bonjour-bytestreams-init.
\n
"
);
xf
=
XEP_XFER
(
xfer
);
xf
->
service
=
g_socket_service_new
();
port
=
purple_socket_listener_add_any_inet_port
(
G_SOCKET_LISTENER
(
xf
->
service
),
G_OBJECT
(
xfer
),
&
error
);
if
(
port
==
0
)
{
purple_debug_error
(
"bonjour"
,
"Unable to open port for file transfer: %s"
,
error
->
message
);
purple_xfer_cancel_local
(
xfer
);
g_error_free
(
error
);
return
;
}
g_signal_connect
(
xf
->
service
,
"incoming"
,
G_CALLBACK
(
bonjour_sock5_incoming_cb
),
NULL
);
bd
=
xf
->
data
;
iq
=
xep_iq_new
(
bd
,
XEP_IQ_SET
,
purple_xfer_get_remote_user
(
xfer
),
bonjour_get_jid
(
bd
->
xmpp_data
->
account
),
xf
->
sid
);
query
=
purple_xmlnode_new_child
(
iq
->
node
,
"query"
);
purple_xmlnode_set_namespace
(
query
,
"http://jabber.org/protocol/bytestreams"
);
purple_xmlnode_set_attrib
(
query
,
"sid"
,
xf
->
sid
);
purple_xmlnode_set_attrib
(
query
,
"mode"
,
"tcp"
);
purple_xfer_set_local_port
(
xfer
,
port
);
local_ips
=
nice_interfaces_get_local_ips
(
FALSE
);
port_str
=
g_strdup_printf
(
"%hu"
,
port
);
while
(
local_ips
)
{
streamhost
=
purple_xmlnode_new_child
(
query
,
"streamhost"
);
purple_xmlnode_set_attrib
(
streamhost
,
"jid"
,
xf
->
sid
);
purple_xmlnode_set_attrib
(
streamhost
,
"host"
,
local_ips
->
data
);
purple_xmlnode_set_attrib
(
streamhost
,
"port"
,
port_str
);
g_free
(
local_ips
->
data
);
local_ips
=
g_list_delete_link
(
local_ips
,
local_ips
);
}
g_free
(
port_str
);
xep_iq_send_and_free
(
iq
);
}
static
void
bonjour_bytestreams_handle_failure
(
PurpleXfer
*
xfer
,
const
gchar
*
error_message
)
{
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
PurpleXmlNode
*
tmp_node
;
gboolean
ret
;
purple_debug_error
(
"bonjour"
,
"Error connecting via SOCKS5 to %s - %s"
,
xf
->
proxy_host
,
error_message
);
tmp_node
=
purple_xmlnode_get_next_twin
(
xf
->
streamhost
);
ret
=
__xep_bytestreams_parse
(
xf
->
pb
,
xfer
,
tmp_node
,
xf
->
iq_id
);
if
(
!
ret
)
{
xep_ft_si_reject
(
xf
->
data
,
xf
->
iq_id
,
purple_xfer_get_remote_user
(
xfer
),
"404"
,
"cancel"
);
/* Cancel the connection */
purple_xfer_cancel_local
(
xfer
);
}
}
static
void
bonjour_bytestreams_connect_cb
(
GObject
*
source
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
user_data
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
GIOStream
*
stream
;
GError
*
error
=
NULL
;
GSocket
*
socket
;
XepIq
*
iq
;
PurpleXmlNode
*
q_node
,
*
tmp_node
;
BonjourData
*
bd
;
stream
=
g_proxy_connect_finish
(
G_PROXY
(
source
),
result
,
&
error
);
if
(
stream
==
NULL
)
{
if
(
!
g_error_matches
(
error
,
G_IO_ERROR
,
G_IO_ERROR_CANCELLED
))
{
purple_debug_error
(
"bonjour"
,
"Unable to connect to destination host: %s"
,
error
->
message
);
bonjour_bytestreams_handle_failure
(
xfer
,
"Unable to connect to destination host."
);
}
g_clear_error
(
&
error
);
return
;
}
if
(
!
G_IS_SOCKET_CONNECTION
(
stream
))
{
purple_debug_error
(
"bonjour"
,
"GProxy didn't return a GSocketConnection."
);
bonjour_bytestreams_handle_failure
(
xfer
,
"GProxy didn't return a GSocketConnection."
);
g_object_unref
(
stream
);
return
;
}
purple_debug_info
(
"bonjour"
,
"Connected successfully via SOCKS5, starting transfer.
\n
"
);
bd
=
xf
->
data
;
/* Here, start the file transfer.*/
/* Notify Initiator of Connection */
iq
=
xep_iq_new
(
bd
,
XEP_IQ_RESULT
,
purple_xfer_get_remote_user
(
xfer
),
bonjour_get_jid
(
bd
->
xmpp_data
->
account
),
xf
->
iq_id
);
q_node
=
purple_xmlnode_new_child
(
iq
->
node
,
"query"
);
purple_xmlnode_set_namespace
(
q_node
,
"http://jabber.org/protocol/bytestreams"
);
tmp_node
=
purple_xmlnode_new_child
(
q_node
,
"streamhost-used"
);
purple_xmlnode_set_attrib
(
tmp_node
,
"jid"
,
xf
->
jid
);
xep_iq_send_and_free
(
iq
);
xf
->
conn
=
G_SOCKET_CONNECTION
(
stream
);
socket
=
g_socket_connection_get_socket
(
xf
->
conn
);
purple_xfer_start
(
xfer
,
g_socket_get_fd
(
socket
),
NULL
,
-1
);
}
/* This is called when we connect to the SOCKS5 proxy server (through any
* relevant account proxy)
*/
static
void
bonjour_bytestreams_socks5_connect_to_host_cb
(
GObject
*
source
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
PurpleXfer
*
xfer
=
PURPLE_XFER
(
user_data
);
XepXfer
*
xf
=
XEP_XFER
(
xfer
);
GSocketConnection
*
conn
;
GProxy
*
proxy
;
GSocketAddress
*
addr
;
GInetSocketAddress
*
inet_addr
;
GSocketAddress
*
proxy_addr
;
PurpleBuddy
*
pb
;
gchar
*
hash_input
;
gchar
*
dstaddr
;
GError
*
error
=
NULL
;
conn
=
g_socket_client_connect_to_host_finish
(
G_SOCKET_CLIENT
(
source
),
result
,
&
error
);
if
(
conn
==
NULL
)
{
if
(
!
g_error_matches
(
error
,
G_IO_ERROR
,
G_IO_ERROR_CANCELLED
))
{
purple_debug_error
(
"bonjour"
,
"Unable to connect to SOCKS5 host: %s"
,
error
->
message
);
bonjour_bytestreams_handle_failure
(
xfer
,
"Unable to connect to SOCKS5 host."
);
}
g_clear_error
(
&
error
);
return
;
}
proxy
=
g_proxy_get_default_for_protocol
(
"socks5"
);
if
(
proxy
==
NULL
)
{
purple_debug_error
(
"bonjour"
,
"SOCKS5 proxy backend missing."
);
bonjour_bytestreams_handle_failure
(
xfer
,
"SOCKS5 proxy backend missing."
);
g_object_unref
(
conn
);
return
;
}
addr
=
g_socket_connection_get_remote_address
(
conn
,
&
error
);
if
(
addr
==
NULL
)
{
purple_debug_error
(
"bonjour"
,
"Unable to retrieve SOCKS5 host address from connection: %s"
,
error
->
message
);
bonjour_bytestreams_handle_failure
(
xfer
,
"Unable to retrieve SOCKS5 host address from connection"
);
g_object_unref
(
conn
);
g_object_unref
(
proxy
);
g_clear_error
(
&
error
);
return
;
}
pb
=
xf
->
pb
;
hash_input
=
g_strdup_printf
(
"%s%s%s"
,
xf
->
sid
,
purple_buddy_get_name
(
pb
),
bonjour_get_jid
(
purple_buddy_get_account
(
pb
)));
dstaddr
=
g_compute_checksum_for_string
(
G_CHECKSUM_SHA1
,
hash_input
,
-1
);
g_free
(
hash_input
);
purple_debug_info
(
"bonjour"
,
"Connecting to %s using SOCKS5 proxy %s:%d"
,
dstaddr
,
xf
->
proxy_host
,
xf
->
proxy_port
);
inet_addr
=
G_INET_SOCKET_ADDRESS
(
addr
);
proxy_addr
=
g_proxy_address_new
(
g_inet_socket_address_get_address
(
inet_addr
),
g_inet_socket_address_get_port
(
inet_addr
),
"socks5"
,
dstaddr
,
0
,
NULL
,
NULL
);
g_object_unref
(
inet_addr
);
g_proxy_connect_async
(
proxy
,
G_IO_STREAM
(
conn
),
G_PROXY_ADDRESS
(
proxy_addr
),
xf
->
cancellable
,
bonjour_bytestreams_connect_cb
,
xfer
);
g_object_unref
(
proxy_addr
);
g_object_unref
(
conn
);
g_object_unref
(
proxy
);
g_free
(
dstaddr
);
}
static
void
bonjour_bytestreams_connect
(
PurpleXfer
*
xfer
)
{
PurpleAccount
*
account
=
NULL
;
XepXfer
*
xf
;
GError
*
error
=
NULL
;
if
(
xfer
==
NULL
)
{
return
;
}
purple_debug_info
(
"bonjour"
,
"bonjour-bytestreams-connect."
);
xf
=
XEP_XFER
(
xfer
);
account
=
purple_buddy_get_account
(
xf
->
pb
);
xf
->
client
=
purple_gio_socket_client_new
(
account
,
&
error
);
if
(
xf
->
client
==
NULL
)
{
/* Cancel the connection */
purple_debug_error
(
"bonjour"
,
"Failed to connect to SOCKS5 streamhost proxy: %s"
,
error
->
message
);
g_clear_error
(
&
error
);
xep_ft_si_reject
(
xf
->
data
,
xf
->
iq_id
,
purple_xfer_get_remote_user
(
xfer
),
"404"
,
"cancel"
);
purple_xfer_cancel_local
(
xfer
);
return
;
}
purple_debug_info
(
"bonjour"
,
"Connecting to SOCKS5 proxy %s:%d"
,
xf
->
proxy_host
,
xf
->
proxy_port
);
g_socket_client_connect_to_host_async
(
xf
->
client
,
xf
->
proxy_host
,
xf
->
proxy_port
,
xf
->
cancellable
,
bonjour_bytestreams_socks5_connect_to_host_cb
,
xfer
);
}
static
void
xep_xfer_init
(
XepXfer
*
xf
)
{
xf
->
cancellable
=
g_cancellable_new
();
}
static
void
xep_xfer_finalize
(
GObject
*
obj
)
{
XepXfer
*
xf
=
XEP_XFER
(
obj
);
BonjourData
*
bd
=
(
BonjourData
*
)
xf
->
data
;
if
(
bd
!=
NULL
)
{
bd
->
xfer_lists
=
g_slist_remove
(
bd
->
xfer_lists
,
PURPLE_XFER
(
xf
));
purple_debug_misc
(
"bonjour"
,
"B free xfer from lists(%p).
\n
"
,
bd
->
xfer_lists
);
}
g_cancellable_cancel
(
xf
->
cancellable
);
g_clear_object
(
&
xf
->
cancellable
);
g_clear_object
(
&
xf
->
client
);
if
(
xf
->
service
)
{
g_socket_service_stop
(
xf
->
service
);
}
g_clear_object
(
&
xf
->
service
);
g_clear_object
(
&
xf
->
conn
);
g_free
(
xf
->
iq_id
);
g_free
(
xf
->
jid
);
g_free
(
xf
->
proxy_host
);
g_free
(
xf
->
buddy_ip
);
g_free
(
xf
->
sid
);
g_clear_pointer
(
&
xf
->
streamhost
,
purple_xmlnode_free_tree
);
G_OBJECT_CLASS
(
xep_xfer_parent_class
)
->
finalize
(
obj
);
}
static
void
xep_xfer_class_finalize
(
XepXferClass
*
klass
)
{
}
static
void
xep_xfer_class_init
(
XepXferClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
PurpleXferClass
*
xfer_class
=
PURPLE_XFER_CLASS
(
klass
);
obj_class
->
finalize
=
xep_xfer_finalize
;
xfer_class
->
init
=
bonjour_xfer_init
;
xfer_class
->
request_denied
=
bonjour_xfer_request_denied
;
xfer_class
->
cancel_recv
=
bonjour_xfer_cancel_recv
;
xfer_class
->
cancel_send
=
bonjour_xfer_cancel_send
;
xfer_class
->
end
=
bonjour_xfer_end
;
}
void
xep_xfer_register
(
GTypeModule
*
module
)
{
xep_xfer_register_type
(
module
);
}