pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Remove all if 0'd code from gtkconv.c
2021-04-21, Gary Kramlich
1f54363a79f2
Remove all if 0'd code from gtkconv.c
This also help expose the bug that caused the pidgin user's nick to be black
in XMPP MUCs.
Testing Done:
Joined a MUC made sure it worked.
Reviewed at https://reviews.imfreedom.org/r/623/
/*
* 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 possiblity 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
);
}