2 * purple - Bonjour Protocol Plugin
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "bonjour_ft.h"
33 bonjour_bytestreams_init(PurpleXfer
*xfer
);
35 bonjour_bytestreams_connect(PurpleXfer
*xfer
);
37 bonjour_xfer_init(PurpleXfer
*xfer
);
39 bonjour_xfer_receive(PurpleConnection
*pc
, const char *id
, const char *sid
, const char *from
,
40 const goffset filesize
, const char *filename
, int option
);
42 /* Look for specific xfer handle */
43 static unsigned int next_id
= 0;
57 PurpleNetworkListenData
*listen_data
;
62 PurpleProxyInfo
*proxy_info
;
63 PurpleProxyConnectData
*proxy_connection
;
67 PurpleXmlNode
*streamhost
;
71 G_DEFINE_DYNAMIC_TYPE(XepXfer
, xep_xfer
, PURPLE_TYPE_XFER
);
74 xep_ft_si_reject(BonjourData
*bd
, const char *id
, const char *to
, const char *error_code
, const char *error_type
)
76 PurpleXmlNode
*error_node
;
79 g_return_if_fail(error_code
!= NULL
);
80 g_return_if_fail(error_type
!= NULL
);
83 purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
87 iq
= xep_iq_new(bd
, XEP_IQ_ERROR
, to
, bonjour_get_jid(bd
->jabber_data
->account
), id
);
91 error_node
= purple_xmlnode_new_child(iq
->node
, "error");
92 purple_xmlnode_set_attrib(error_node
, "code", error_code
);
93 purple_xmlnode_set_attrib(error_node
, "type", error_type
);
95 /* TODO: Make this better */
96 if (purple_strequal(error_code
, "403")) {
97 PurpleXmlNode
*tmp_node
= purple_xmlnode_new_child(error_node
, "forbidden");
98 purple_xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
100 tmp_node
= purple_xmlnode_new_child(error_node
, "text");
101 purple_xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
102 purple_xmlnode_insert_data(tmp_node
, "Offer Declined", -1);
103 } else if (purple_strequal(error_code
, "404")) {
104 PurpleXmlNode
*tmp_node
= purple_xmlnode_new_child(error_node
, "item-not-found");
105 purple_xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
108 xep_iq_send_and_free(iq
);
111 static void bonjour_xfer_cancel_send(PurpleXfer
*xfer
)
113 purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
116 static void bonjour_xfer_request_denied(PurpleXfer
*xfer
)
118 XepXfer
*xf
= XEP_XFER(xfer
);
120 purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
123 xep_ft_si_reject(xf
->data
, xf
->sid
, purple_xfer_get_remote_user(xfer
), "403", "cancel");
127 static void bonjour_xfer_cancel_recv(PurpleXfer
*xfer
)
129 purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
132 struct socket_cleanup
{
138 _wait_for_socket_close(gpointer data
, gint source
, PurpleInputCondition cond
)
140 struct socket_cleanup
*sc
= data
;
144 ret
= recv(source
, buf
, 1, 0);
146 if (ret
== 0 || (ret
== -1 && !(errno
== EAGAIN
|| errno
== EWOULDBLOCK
))) {
147 purple_debug_info("bonjour", "Client completed recieving; closing server socket.\n");
148 purple_input_remove(sc
->handle
);
154 static void bonjour_xfer_end(PurpleXfer
*xfer
)
156 purple_debug_info("bonjour", "Bonjour-xfer-end.\n");
158 /* We can't allow the server side to close the connection until the client is complete,
159 * otherwise there is a RST resulting in an error on the client side */
160 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_SEND
&& purple_xfer_is_completed(xfer
)) {
161 struct socket_cleanup
*sc
= g_new0(struct socket_cleanup
, 1);
162 sc
->fd
= purple_xfer_get_fd(xfer
);
163 purple_xfer_set_fd(xfer
, -1);
164 sc
->handle
= purple_input_add(sc
->fd
, PURPLE_INPUT_READ
,
165 _wait_for_socket_close
, sc
);
170 bonjour_si_xfer_find(BonjourData
*bd
, const char *sid
, const char *from
)
176 if(!sid
|| !from
|| !bd
)
179 purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
182 for(xfers
= bd
->xfer_lists
; xfers
; xfers
= xfers
->next
) {
189 if(xf
->sid
&& purple_xfer_get_remote_user(xfer
) && purple_strequal(xf
->sid
, sid
) &&
190 purple_strequal(purple_xfer_get_remote_user(xfer
), from
))
194 purple_debug_info("bonjour", "Look for xfer list fail\n");
200 xep_ft_si_offer(PurpleXfer
*xfer
, const gchar
*to
)
202 PurpleXmlNode
*si_node
, *feature
, *field
, *file
, *x
;
204 XepXfer
*xf
= XEP_XFER(xfer
);
205 BonjourData
*bd
= NULL
;
215 purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id
);
217 /* Assign stream id. */
219 xf
->iq_id
= g_strdup_printf("%u", next_id
++);
220 iq
= xep_iq_new(xf
->data
, XEP_IQ_SET
, to
, bonjour_get_jid(bd
->jabber_data
->account
), xf
->iq_id
);
224 /*Construct Stream initialization offer message.*/
225 si_node
= purple_xmlnode_new_child(iq
->node
, "si");
226 purple_xmlnode_set_namespace(si_node
, "http://jabber.org/protocol/si");
227 purple_xmlnode_set_attrib(si_node
, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
229 xf
->sid
= g_strdup(xf
->iq_id
);
230 purple_xmlnode_set_attrib(si_node
, "id", xf
->sid
);
232 file
= purple_xmlnode_new_child(si_node
, "file");
233 purple_xmlnode_set_namespace(file
, "http://jabber.org/protocol/si/profile/file-transfer");
234 purple_xmlnode_set_attrib(file
, "name", purple_xfer_get_filename(xfer
));
235 g_snprintf(buf
, sizeof(buf
), "%" G_GOFFSET_FORMAT
, purple_xfer_get_size(xfer
));
236 purple_xmlnode_set_attrib(file
, "size", buf
);
238 feature
= purple_xmlnode_new_child(si_node
, "feature");
239 purple_xmlnode_set_namespace(feature
, "http://jabber.org/protocol/feature-neg");
241 x
= purple_xmlnode_new_child(feature
, "x");
242 purple_xmlnode_set_namespace(x
, "jabber:x:data");
243 purple_xmlnode_set_attrib(x
, "type", "form");
245 field
= purple_xmlnode_new_child(x
, "field");
246 purple_xmlnode_set_attrib(field
, "var", "stream-method");
247 purple_xmlnode_set_attrib(field
, "type", "list-single");
249 if (xf
->mode
& XEP_BYTESTREAMS
) {
250 PurpleXmlNode
*option
= purple_xmlnode_new_child(field
, "option");
251 PurpleXmlNode
*value
= purple_xmlnode_new_child(option
, "value");
252 purple_xmlnode_insert_data(value
, "http://jabber.org/protocol/bytestreams", -1);
254 if (xf
->mode
& XEP_IBB
) {
255 PurpleXmlNode
*option
= purple_xmlnode_new_child(field
, "option");
256 PurpleXmlNode
*value
= purple_xmlnode_new_child(option
, "value");
257 purple_xmlnode_insert_data(value
, "http://jabber.org/protocol/ibb", -1);
260 xep_iq_send_and_free(iq
);
264 xep_ft_si_result(PurpleXfer
*xfer
, const char *to
)
266 PurpleXmlNode
*si_node
, *feature
, *field
, *value
, *x
;
277 purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
278 iq
= xep_iq_new(bd
, XEP_IQ_RESULT
, to
, bonjour_get_jid(bd
->jabber_data
->account
), xf
->iq_id
);
282 si_node
= purple_xmlnode_new_child(iq
->node
, "si");
283 purple_xmlnode_set_namespace(si_node
, "http://jabber.org/protocol/si");
284 /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
286 feature
= purple_xmlnode_new_child(si_node
, "feature");
287 purple_xmlnode_set_namespace(feature
, "http://jabber.org/protocol/feature-neg");
289 x
= purple_xmlnode_new_child(feature
, "x");
290 purple_xmlnode_set_namespace(x
, "jabber:x:data");
291 purple_xmlnode_set_attrib(x
, "type", "submit");
293 field
= purple_xmlnode_new_child(x
, "field");
294 purple_xmlnode_set_attrib(field
, "var", "stream-method");
296 value
= purple_xmlnode_new_child(field
, "value");
297 purple_xmlnode_insert_data(value
, "http://jabber.org/protocol/bytestreams", -1);
299 xep_iq_send_and_free(iq
);
303 * Frees the whole tree of an xml node
305 * First determines the root of the xml tree and then frees the whole tree
308 * @param node The node to free the tree from
311 purple_xmlnode_free_tree(PurpleXmlNode
*node
)
313 g_return_if_fail(node
!= NULL
);
315 while(purple_xmlnode_get_parent(node
))
316 node
= purple_xmlnode_get_parent(node
);
318 purple_xmlnode_free(node
);
322 bonjour_new_xfer(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
)
328 if(who
== NULL
|| gc
== NULL
)
331 purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who
);
332 bd
= purple_connection_get_protocol_data(gc
);
336 /* Build the file transfer handle */
337 xep_xfer
= g_object_new(
339 "account", purple_connection_get_account(gc
),
340 "type", PURPLE_XFER_TYPE_SEND
,
344 xfer
= PURPLE_XFER(xep_xfer
);
347 purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd
, xep_xfer
->data
);
349 /* We don't support IBB yet */
350 /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
351 xep_xfer
->mode
= XEP_BYTESTREAMS
;
352 xep_xfer
->sid
= NULL
;
354 bd
->xfer_lists
= g_slist_append(bd
->xfer_lists
, xfer
);
360 bonjour_send_file(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
, const char *file
)
364 g_return_if_fail(gc
!= NULL
);
365 g_return_if_fail(who
!= NULL
);
367 purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who
);
369 xfer
= bonjour_new_xfer(prplxfer
, gc
, who
);
372 purple_xfer_request_accepted(xfer
, file
);
374 purple_xfer_request(xfer
);
379 bonjour_xfer_init(PurpleXfer
*xfer
)
387 purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
389 buddy
= purple_blist_find_buddy(purple_xfer_get_account(xfer
), purple_xfer_get_remote_user(xfer
));
390 /* this buddy is offline. */
391 if (buddy
== NULL
|| (bb
= purple_buddy_get_protocol_data(buddy
)) == NULL
)
394 /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
396 xf
->buddy_ip
= g_strdup(bb
->ips
->data
);
397 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_SEND
) {
398 /* initiate file transfer, send SI offer. */
399 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_SEND.\n");
400 xep_ft_si_offer(xfer
, purple_xfer_get_remote_user(xfer
));
402 /* accept file transfer request, send SI result. */
403 xep_ft_si_result(xfer
, purple_xfer_get_remote_user(xfer
));
404 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_RECEIVE.\n");
409 xep_si_parse(PurpleConnection
*pc
, PurpleXmlNode
*packet
, PurpleBuddy
*pb
)
411 const char *type
, *id
;
414 const gchar
*name
= NULL
;
416 g_return_if_fail(pc
!= NULL
);
417 g_return_if_fail(packet
!= NULL
);
418 g_return_if_fail(pb
!= NULL
);
420 bd
= purple_connection_get_protocol_data(pc
);
424 purple_debug_info("bonjour", "xep-si-parse.\n");
426 name
= purple_buddy_get_name(pb
);
428 type
= purple_xmlnode_get_attrib(packet
, "type");
429 id
= purple_xmlnode_get_attrib(packet
, "id");
433 if(purple_strequal(type
, "set")) {
435 gboolean parsed_receive
= FALSE
;
437 si
= purple_xmlnode_get_child(packet
, "si");
439 purple_debug_info("bonjour", "si offer Message type - SET.\n");
443 profile
= purple_xmlnode_get_attrib(si
, "profile");
445 if (purple_strequal(profile
, "http://jabber.org/protocol/si/profile/file-transfer")) {
446 const char *filename
= NULL
, *filesize_str
= NULL
;
447 goffset filesize
= 0;
450 const char *sid
= purple_xmlnode_get_attrib(si
, "id");
452 if ((file
= purple_xmlnode_get_child(si
, "file"))) {
453 filename
= purple_xmlnode_get_attrib(file
, "name");
454 if((filesize_str
= purple_xmlnode_get_attrib(file
, "size")))
455 filesize
= g_ascii_strtoll(filesize_str
, NULL
, 10);
458 /* TODO: Make sure that it is advertising a bytestreams transfer */
461 bonjour_xfer_receive(pc
, id
, sid
, name
, filesize
, filename
, XEP_BYTESTREAMS
);
463 parsed_receive
= TRUE
;
468 if (!parsed_receive
) {
469 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
471 purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
472 xep_ft_si_reject(bd
, id
, name
, "403", "cancel");
473 /*TODO: Send Cancel (501) */
475 } else if(purple_strequal(type
, "result")) {
476 purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
478 xfer
= bonjour_si_xfer_find(bd
, id
, name
);
481 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
482 purple_debug_info("bonjour", "xfer find fail.\n");
483 xep_ft_si_reject(bd
, id
, name
, "403", "cancel");
485 bonjour_bytestreams_init(xfer
);
487 } else if(purple_strequal(type
, "error")) {
488 purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
490 xfer
= bonjour_si_xfer_find(bd
, id
, name
);
493 purple_debug_info("bonjour", "xfer find fail.\n");
495 purple_xfer_cancel_remote(xfer
);
497 purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type
);
501 * Will compare a host with a buddy_ip.
503 * Additionally to a common 'purple_strequal(host, buddy_ip)', it will also return TRUE
504 * if 'host' is a link local IPv6 address without an appended interface
505 * identifier and 'buddy_ip' string is "host" + "%iface".
507 * Note: This may theoretically result in the attempt to connect to the wrong
508 * host, because we do not know for sure which interface the according link
509 * local IPv6 address might relate to and RFC4862 for instance only ensures the
510 * uniqueness of this address on a given link. So we could possibly have two
511 * distinct buddies with the same ipv6 link local address on two distinct
512 * interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with
513 * link local ip addresses properly...
514 * However, in practice the possiblity for such a conflict is relatively low
515 * (2011 - might be different in the future though?).
517 * @param host ipv4 or ipv6 address string
518 * @param buddy_ip ipv4 or ipv6 address string
519 * @return TRUE if they match, FALSE otherwise
522 xep_cmp_addr(const char *host
, const char *buddy_ip
)
524 #if defined(AF_INET6) && defined(HAVE_GETADDRINFO)
525 struct addrinfo hint
, *res
= NULL
;
526 common_sockaddr_t addr
;
529 memset(&hint
, 0, sizeof(hint
));
530 hint
.ai_family
= AF_UNSPEC
;
531 hint
.ai_flags
= AI_NUMERICHOST
;
533 ret
= getaddrinfo(host
, NULL
, &hint
, &res
);
536 memcpy(&addr
, res
->ai_addr
, sizeof(addr
));
538 if (res
->ai_family
!= AF_INET6
||
539 !IN6_IS_ADDR_LINKLOCAL(&addr
.in6
.sin6_addr
))
546 if(strlen(buddy_ip
) <= strlen(host
) ||
547 buddy_ip
[strlen(host
)] != '%')
550 return !strncmp(host
, buddy_ip
, strlen(host
));
554 return purple_strequal(host
, buddy_ip
);
558 xep_addr_differ(const char *buddy_ip
, const char *host
)
560 return !xep_cmp_addr(host
, buddy_ip
);
564 * Create and insert an identical twin
566 * Creates a copy of the specified node and inserts it right after
567 * this original node.
569 * @param node The node to clone
570 * @return A pointer to the new, cloned twin if successful
573 static PurpleXmlNode
*
574 purple_xmlnode_insert_twin_copy(PurpleXmlNode
*node
) {
577 g_return_val_if_fail(node
!= NULL
, NULL
);
579 copy
= purple_xmlnode_copy(node
);
580 g_return_val_if_fail(copy
!= NULL
, NULL
);
582 copy
->next
= node
->next
;
589 * Tries to append an interface scope to an IPv6 link local address.
591 * If the given address is a link local IPv6 address (with no
592 * interface scope) then we try to determine all fitting interfaces
593 * from our Bonjour IP address list.
595 * For any such found matches we insert a copy of our current xml
596 * streamhost entry right after this streamhost entry and append
597 * the determined interface to the host address of this copy.
599 * @param cur_streamhost The XML streamhost node we examine
600 * @param host The host address to examine in text form
601 * @param pb Buddy to get the list of link local IPv6 addresses
602 * and their interface from
603 * @return Returns TRUE if the specified 'host' address is a
604 * link local IPv6 address with no interface scope.
605 * Otherwise returns FALSE.
608 add_ipv6_link_local_ifaces(PurpleXmlNode
*cur_streamhost
, const char *host
,
611 PurpleXmlNode
*new_streamhost
= NULL
;
612 struct in6_addr in6_addr
;
616 if (inet_pton(AF_INET6
, host
, &in6_addr
) != 1 ||
617 !IN6_IS_ADDR_LINKLOCAL(&in6_addr
) ||
621 bb
= purple_buddy_get_protocol_data(pb
);
623 for (ip_elem
= bb
->ips
;
624 (ip_elem
= g_slist_find_custom(ip_elem
, host
, (GCompareFunc
)&xep_addr_differ
));
625 ip_elem
= ip_elem
->next
) {
626 purple_debug_info("bonjour", "Inserting an PurpleXmlNode twin copy for %s with new host address %s\n",
627 host
, (char*)ip_elem
->data
);
628 new_streamhost
= purple_xmlnode_insert_twin_copy(cur_streamhost
);
629 purple_xmlnode_set_attrib(new_streamhost
, "host", ip_elem
->data
);
633 purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n",
640 __xep_bytestreams_parse(PurpleBuddy
*pb
, PurpleXfer
*xfer
, PurpleXmlNode
*streamhost
,
644 const char *jid
, *host
, *port
;
646 XepXfer
*xf
= XEP_XFER(xfer
);
648 for(; streamhost
; streamhost
= purple_xmlnode_get_next_twin(streamhost
)) {
649 if(!(jid
= purple_xmlnode_get_attrib(streamhost
, "jid")) ||
650 !(host
= purple_xmlnode_get_attrib(streamhost
, "host")) ||
651 !(port
= purple_xmlnode_get_attrib(streamhost
, "port")) ||
652 !(portnum
= atoi(port
))) {
653 purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
657 /* skip IPv6 link local addresses with no interface scope
658 * (but try to add a new one with an interface scope then) */
659 if(add_ipv6_link_local_ifaces(streamhost
, host
, pb
))
662 tmp_iq_id
= g_strdup(iq_id
);
665 g_free(xf
->proxy_host
);
667 xf
->iq_id
= tmp_iq_id
;
668 xf
->jid
= g_strdup(jid
);
669 xf
->proxy_host
= g_strdup(host
);
670 xf
->proxy_port
= portnum
;
671 xf
->streamhost
= streamhost
;
673 purple_debug_info("bonjour", "bytestream offer parse"
674 "jid=%s host=%s port=%d.\n", jid
, host
, portnum
);
675 bonjour_bytestreams_connect(xfer
);
683 xep_bytestreams_parse(PurpleConnection
*pc
, PurpleXmlNode
*packet
, PurpleBuddy
*pb
)
685 const char *type
, *from
, *iq_id
, *sid
;
686 PurpleXmlNode
*query
, *streamhost
;
690 g_return_if_fail(pc
!= NULL
);
691 g_return_if_fail(packet
!= NULL
);
692 g_return_if_fail(pb
!= NULL
);
694 bd
= purple_connection_get_protocol_data(pc
);
698 purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
700 type
= purple_xmlnode_get_attrib(packet
, "type");
701 from
= purple_buddy_get_name(pb
);
702 query
= purple_xmlnode_get_child(packet
,"query");
706 query
= purple_xmlnode_copy(query
);
710 if(!purple_strequal(type
, "set")) {
711 purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type
);
715 purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
717 iq_id
= purple_xmlnode_get_attrib(packet
, "id");
719 sid
= purple_xmlnode_get_attrib(query
, "sid");
720 xfer
= bonjour_si_xfer_find(bd
, sid
, from
);
721 streamhost
= purple_xmlnode_get_child(query
, "streamhost");
723 if(xfer
&& streamhost
&& __xep_bytestreams_parse(pb
, xfer
, streamhost
, iq_id
))
724 return; /* success */
726 purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
728 if (iq_id
&& xfer
!= NULL
)
729 xep_ft_si_reject(bd
, iq_id
, purple_xfer_get_remote_user(xfer
), "404", "cancel");
733 bonjour_xfer_receive(PurpleConnection
*pc
, const char *id
, const char *sid
, const char *from
,
734 const goffset filesize
, const char *filename
, int option
)
740 if(pc
== NULL
|| id
== NULL
|| from
== NULL
)
743 bd
= purple_connection_get_protocol_data(pc
);
747 purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
749 /* Build the file transfer handle */
752 "account", purple_connection_get_account(pc
),
753 "type", PURPLE_XFER_TYPE_RECEIVE
,
758 xfer
= PURPLE_XFER(xf
);
761 purple_xfer_set_filename(xfer
, filename
);
762 xf
->iq_id
= g_strdup(id
);
763 xf
->sid
= g_strdup(sid
);
766 purple_xfer_set_size(xfer
, filesize
);
768 bd
->xfer_lists
= g_slist_append(bd
->xfer_lists
, xfer
);
770 purple_xfer_request(xfer
);
774 bonjour_sock5_request_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
776 PurpleXfer
*xfer
= data
;
777 XepXfer
*xf
= XEP_XFER(xfer
);
784 purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf
->sock5_req_state
);
786 switch(xf
->sock5_req_state
){
788 acceptfd
= accept(source
, NULL
, 0);
789 if(acceptfd
== -1 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)) {
791 } else if(acceptfd
== -1) {
792 /* This should cancel the ft */
793 purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno
);
796 purple_xfer_cancel_remote(xfer
);
799 purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd
);
801 _purple_network_set_common_socket_flags(acceptfd
);
802 purple_input_remove(purple_xfer_get_watcher(xfer
));
804 purple_xfer_set_watcher(xfer
, purple_input_add(acceptfd
, PURPLE_INPUT_READ
,
805 bonjour_sock5_request_cb
, xfer
));
806 xf
->sock5_req_state
++;
811 purple_xfer_set_fd(xfer
, source
);
812 len
= read(source
, xf
->rx_buf
+ xf
->rxlen
, 3);
813 if(len
< 0 && errno
== EAGAIN
)
816 purple_xfer_cancel_remote(xfer
);
819 purple_input_remove(purple_xfer_get_watcher(xfer
));
820 purple_xfer_set_watcher(xfer
, purple_input_add(source
, PURPLE_INPUT_WRITE
,
821 bonjour_sock5_request_cb
, xfer
));
822 xf
->sock5_req_state
++;
824 bonjour_sock5_request_cb(xfer
, source
, PURPLE_INPUT_WRITE
);
828 xf
->tx_buf
[0] = 0x05;
829 xf
->tx_buf
[1] = 0x00;
830 len
= write(source
, xf
->tx_buf
, 2);
831 if (len
< 0 && errno
== EAGAIN
)
835 purple_xfer_cancel_remote(xfer
);
838 purple_input_remove(purple_xfer_get_watcher(xfer
));
839 purple_xfer_set_watcher(xfer
, purple_input_add(source
, PURPLE_INPUT_READ
,
840 bonjour_sock5_request_cb
, xfer
));
841 xf
->sock5_req_state
++;
846 len
= read(source
, xf
->rx_buf
+ xf
->rxlen
, 20);
849 purple_input_remove(purple_xfer_get_watcher(xfer
));
850 purple_xfer_set_watcher(xfer
, purple_input_add(source
, PURPLE_INPUT_WRITE
,
851 bonjour_sock5_request_cb
, xfer
));
852 xf
->sock5_req_state
++;
854 bonjour_sock5_request_cb(xfer
, source
, PURPLE_INPUT_WRITE
);
858 xf
->tx_buf
[0] = 0x05;
859 xf
->tx_buf
[1] = 0x00;
860 xf
->tx_buf
[2] = 0x00;
861 xf
->tx_buf
[3] = 0x03;
862 xf
->tx_buf
[4] = strlen(xf
->buddy_ip
);
863 memcpy(xf
->tx_buf
+ 5, xf
->buddy_ip
, strlen(xf
->buddy_ip
));
864 xf
->tx_buf
[5+strlen(xf
->buddy_ip
)] = 0x00;
865 xf
->tx_buf
[6+strlen(xf
->buddy_ip
)] = 0x00;
866 len
= write(source
, xf
->tx_buf
, 7 + strlen(xf
->buddy_ip
));
867 if (len
< 0 && errno
== EAGAIN
) {
869 } else if (len
< 0) {
871 purple_xfer_cancel_remote(xfer
);
874 purple_input_remove(purple_xfer_get_watcher(xfer
));
875 purple_xfer_set_watcher(xfer
, 0);
878 purple_xfer_start(xfer
, source
, NULL
, -1);
888 bonjour_bytestreams_listen(int sock
, gpointer data
)
890 PurpleXfer
*xfer
= data
;
893 PurpleXmlNode
*query
, *streamhost
;
898 purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock
);
899 if (sock
< 0 || xfer
== NULL
) {
900 /*purple_xfer_cancel_local(xfer);*/
904 purple_xfer_set_watcher(xfer
, purple_input_add(sock
, PURPLE_INPUT_READ
,
905 bonjour_sock5_request_cb
, xfer
));
907 xf
->listen_data
= NULL
;
911 iq
= xep_iq_new(bd
, XEP_IQ_SET
, purple_xfer_get_remote_user(xfer
), bonjour_get_jid(bd
->jabber_data
->account
), xf
->sid
);
913 query
= purple_xmlnode_new_child(iq
->node
, "query");
914 purple_xmlnode_set_namespace(query
, "http://jabber.org/protocol/bytestreams");
915 purple_xmlnode_set_attrib(query
, "sid", xf
->sid
);
916 purple_xmlnode_set_attrib(query
, "mode", "tcp");
918 purple_xfer_set_local_port(xfer
, purple_network_get_port_from_fd(sock
));
920 local_ips
= bonjour_jabber_get_local_ips(sock
);
922 port
= g_strdup_printf("%hu", purple_xfer_get_local_port(xfer
));
924 streamhost
= purple_xmlnode_new_child(query
, "streamhost");
925 purple_xmlnode_set_attrib(streamhost
, "jid", xf
->sid
);
926 purple_xmlnode_set_attrib(streamhost
, "host", local_ips
->data
);
927 purple_xmlnode_set_attrib(streamhost
, "port", port
);
928 g_free(local_ips
->data
);
929 local_ips
= g_slist_delete_link(local_ips
, local_ips
);
933 xep_iq_send_and_free(iq
);
937 bonjour_bytestreams_init(PurpleXfer
*xfer
)
943 purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
946 xf
->listen_data
= purple_network_listen_range(0, 0, AF_UNSPEC
, SOCK_STREAM
, FALSE
,
947 bonjour_bytestreams_listen
, xfer
);
948 if (xf
->listen_data
== NULL
)
949 purple_xfer_cancel_local(xfer
);
955 bonjour_bytestreams_connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
957 PurpleXfer
*xfer
= data
;
958 XepXfer
*xf
= XEP_XFER(xfer
);
960 PurpleXmlNode
*q_node
, *tmp_node
;
962 gboolean ret
= FALSE
;
964 xf
->proxy_connection
= NULL
;
967 purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n",
968 xf
->proxy_host
, error_message
? error_message
: "(null)");
970 tmp_node
= purple_xmlnode_get_next_twin(xf
->streamhost
);
971 ret
= __xep_bytestreams_parse(xf
->pb
, xfer
, tmp_node
, xf
->iq_id
);
974 xep_ft_si_reject(xf
->data
, xf
->iq_id
, purple_xfer_get_remote_user(xfer
), "404", "cancel");
975 /* Cancel the connection */
976 purple_xfer_cancel_local(xfer
);
981 purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
985 /* Here, start the file transfer.*/
987 /* Notify Initiator of Connection */
988 iq
= xep_iq_new(bd
, XEP_IQ_RESULT
, purple_xfer_get_remote_user(xfer
), bonjour_get_jid(bd
->jabber_data
->account
), xf
->iq_id
);
989 q_node
= purple_xmlnode_new_child(iq
->node
, "query");
990 purple_xmlnode_set_namespace(q_node
, "http://jabber.org/protocol/bytestreams");
991 tmp_node
= purple_xmlnode_new_child(q_node
, "streamhost-used");
992 purple_xmlnode_set_attrib(tmp_node
, "jid", xf
->jid
);
993 xep_iq_send_and_free(iq
);
995 purple_xfer_start(xfer
, source
, NULL
, -1);
999 bonjour_bytestreams_connect(PurpleXfer
*xfer
)
1002 PurpleAccount
*account
= NULL
;
1006 const gchar
*name
= NULL
;
1007 unsigned char hashval
[20];
1008 gsize digest_len
= 20;
1015 purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
1017 xf
= XEP_XFER(xfer
);
1020 name
= purple_buddy_get_name(pb
);
1021 account
= purple_buddy_get_account(pb
);
1023 p
= g_strdup_printf("%s%s%s", xf
->sid
, name
, bonjour_get_jid(account
));
1025 hash
= g_checksum_new(G_CHECKSUM_SHA1
);
1026 g_checksum_update(hash
, (guchar
*)p
, -1);
1027 g_checksum_get_digest(hash
, hashval
, &digest_len
);
1028 g_checksum_free(hash
);
1032 memset(dstaddr
, 0, 41);
1034 for(i
= 0; i
< 20; i
++, p
+= 2)
1035 snprintf(p
, 3, "%02x", hashval
[i
]);
1037 xf
->proxy_info
= purple_proxy_info_new();
1038 purple_proxy_info_set_proxy_type(xf
->proxy_info
, PURPLE_PROXY_SOCKS5
);
1039 purple_proxy_info_set_host(xf
->proxy_info
, xf
->proxy_host
);
1040 purple_proxy_info_set_port(xf
->proxy_info
, xf
->proxy_port
);
1041 xf
->proxy_connection
= purple_proxy_connect_socks5_account(
1042 purple_account_get_connection(account
),
1046 bonjour_bytestreams_connect_cb
, xfer
);
1048 if(xf
->proxy_connection
== NULL
) {
1049 xep_ft_si_reject(xf
->data
, xf
->iq_id
, purple_xfer_get_remote_user(xfer
), "404", "cancel");
1050 /* Cancel the connection */
1051 purple_xfer_cancel_local(xfer
);
1056 xep_xfer_init(XepXfer
*xfer
) {
1061 xep_xfer_finalize(GObject
*obj
) {
1062 XepXfer
*xf
= XEP_XFER(obj
);
1064 BonjourData
*bd
= (BonjourData
*)xf
->data
;
1066 bd
->xfer_lists
= g_slist_remove(bd
->xfer_lists
, PURPLE_XFER(xf
));
1067 purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd
->xfer_lists
);
1069 if (xf
->proxy_connection
!= NULL
) {
1070 purple_proxy_connect_cancel(xf
->proxy_connection
);
1072 if (xf
->proxy_info
!= NULL
) {
1073 purple_proxy_info_destroy(xf
->proxy_info
);
1075 if (xf
->listen_data
!= NULL
) {
1076 purple_network_listen_cancel(xf
->listen_data
);
1081 g_free(xf
->proxy_host
);
1082 g_free(xf
->buddy_ip
);
1085 purple_xmlnode_free_tree(xf
->streamhost
);
1087 G_OBJECT_CLASS(xep_xfer_parent_class
)->finalize(obj
);
1091 xep_xfer_class_finalize(XepXferClass
*klass
) {
1096 xep_xfer_class_init(XepXferClass
*klass
) {
1097 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
1098 PurpleXferClass
*xfer_class
= PURPLE_XFER_CLASS(klass
);
1100 obj_class
->finalize
= xep_xfer_finalize
;
1102 xfer_class
->init
= bonjour_xfer_init
;
1103 xfer_class
->request_denied
= bonjour_xfer_request_denied
;
1104 xfer_class
->cancel_recv
= bonjour_xfer_cancel_recv
;
1105 xfer_class
->cancel_send
= bonjour_xfer_cancel_send
;
1106 xfer_class
->end
= bonjour_xfer_end
;
1110 xep_xfer_register(GTypeModule
*module
) {
1111 xep_xfer_register_type(module
);