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
26 #include "bonjour_ft.h"
29 bonjour_bytestreams_init(PurpleXfer
*xfer
);
31 bonjour_bytestreams_connect(PurpleXfer
*xfer
);
33 bonjour_xfer_init(PurpleXfer
*xfer
);
35 bonjour_xfer_receive(PurpleConnection
*pc
, const char *id
, const char *sid
, const char *from
,
36 const goffset filesize
, const char *filename
, int option
);
38 /* Look for specific xfer handle */
39 static unsigned int next_id
= 0;
53 PurpleNetworkListenData
*listen_data
;
58 PurpleProxyInfo
*proxy_info
;
59 PurpleProxyConnectData
*proxy_connection
;
63 PurpleXmlNode
*streamhost
;
67 G_DEFINE_DYNAMIC_TYPE(XepXfer
, xep_xfer
, PURPLE_TYPE_XFER
);
70 xep_ft_si_reject(BonjourData
*bd
, const char *id
, const char *to
, const char *error_code
, const char *error_type
)
72 PurpleXmlNode
*error_node
;
75 g_return_if_fail(error_code
!= NULL
);
76 g_return_if_fail(error_type
!= NULL
);
79 purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
83 iq
= xep_iq_new(bd
, XEP_IQ_ERROR
, to
, bonjour_get_jid(bd
->jabber_data
->account
), id
);
87 error_node
= purple_xmlnode_new_child(iq
->node
, "error");
88 purple_xmlnode_set_attrib(error_node
, "code", error_code
);
89 purple_xmlnode_set_attrib(error_node
, "type", error_type
);
91 /* TODO: Make this better */
92 if (purple_strequal(error_code
, "403")) {
93 PurpleXmlNode
*tmp_node
= purple_xmlnode_new_child(error_node
, "forbidden");
94 purple_xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
96 tmp_node
= purple_xmlnode_new_child(error_node
, "text");
97 purple_xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
98 purple_xmlnode_insert_data(tmp_node
, "Offer Declined", -1);
99 } else if (purple_strequal(error_code
, "404")) {
100 PurpleXmlNode
*tmp_node
= purple_xmlnode_new_child(error_node
, "item-not-found");
101 purple_xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
104 xep_iq_send_and_free(iq
);
107 static void bonjour_xfer_cancel_send(PurpleXfer
*xfer
)
109 purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
112 static void bonjour_xfer_request_denied(PurpleXfer
*xfer
)
114 XepXfer
*xf
= XEP_XFER(xfer
);
116 purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
119 xep_ft_si_reject(xf
->data
, xf
->sid
, purple_xfer_get_remote_user(xfer
), "403", "cancel");
123 static void bonjour_xfer_cancel_recv(PurpleXfer
*xfer
)
125 purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
128 struct socket_cleanup
{
134 _wait_for_socket_close(gpointer data
, gint source
, PurpleInputCondition cond
)
136 struct socket_cleanup
*sc
= data
;
140 ret
= recv(source
, buf
, 1, 0);
142 if (ret
== 0 || (ret
== -1 && !(errno
== EAGAIN
|| errno
== EWOULDBLOCK
))) {
143 purple_debug_info("bonjour", "Client completed recieving; closing server socket.\n");
144 purple_input_remove(sc
->handle
);
150 static void bonjour_xfer_end(PurpleXfer
*xfer
)
152 purple_debug_info("bonjour", "Bonjour-xfer-end.\n");
154 /* We can't allow the server side to close the connection until the client is complete,
155 * otherwise there is a RST resulting in an error on the client side */
156 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_SEND
&& purple_xfer_is_completed(xfer
)) {
157 struct socket_cleanup
*sc
= g_new0(struct socket_cleanup
, 1);
158 sc
->fd
= purple_xfer_get_fd(xfer
);
159 purple_xfer_set_fd(xfer
, -1);
160 sc
->handle
= purple_input_add(sc
->fd
, PURPLE_INPUT_READ
,
161 _wait_for_socket_close
, sc
);
166 bonjour_si_xfer_find(BonjourData
*bd
, const char *sid
, const char *from
)
172 if(!sid
|| !from
|| !bd
)
175 purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
178 for(xfers
= bd
->xfer_lists
; xfers
; xfers
= xfers
->next
) {
185 if(xf
->sid
&& purple_xfer_get_remote_user(xfer
) && purple_strequal(xf
->sid
, sid
) &&
186 purple_strequal(purple_xfer_get_remote_user(xfer
), from
))
190 purple_debug_info("bonjour", "Look for xfer list fail\n");
196 xep_ft_si_offer(PurpleXfer
*xfer
, const gchar
*to
)
198 PurpleXmlNode
*si_node
, *feature
, *field
, *file
, *x
;
200 XepXfer
*xf
= XEP_XFER(xfer
);
201 BonjourData
*bd
= NULL
;
211 purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id
);
213 /* Assign stream id. */
215 xf
->iq_id
= g_strdup_printf("%u", next_id
++);
216 iq
= xep_iq_new(xf
->data
, XEP_IQ_SET
, to
, bonjour_get_jid(bd
->jabber_data
->account
), xf
->iq_id
);
220 /*Construct Stream initialization offer message.*/
221 si_node
= purple_xmlnode_new_child(iq
->node
, "si");
222 purple_xmlnode_set_namespace(si_node
, "http://jabber.org/protocol/si");
223 purple_xmlnode_set_attrib(si_node
, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
225 xf
->sid
= g_strdup(xf
->iq_id
);
226 purple_xmlnode_set_attrib(si_node
, "id", xf
->sid
);
228 file
= purple_xmlnode_new_child(si_node
, "file");
229 purple_xmlnode_set_namespace(file
, "http://jabber.org/protocol/si/profile/file-transfer");
230 purple_xmlnode_set_attrib(file
, "name", purple_xfer_get_filename(xfer
));
231 g_snprintf(buf
, sizeof(buf
), "%" G_GOFFSET_FORMAT
, purple_xfer_get_size(xfer
));
232 purple_xmlnode_set_attrib(file
, "size", buf
);
234 feature
= purple_xmlnode_new_child(si_node
, "feature");
235 purple_xmlnode_set_namespace(feature
, "http://jabber.org/protocol/feature-neg");
237 x
= purple_xmlnode_new_child(feature
, "x");
238 purple_xmlnode_set_namespace(x
, "jabber:x:data");
239 purple_xmlnode_set_attrib(x
, "type", "form");
241 field
= purple_xmlnode_new_child(x
, "field");
242 purple_xmlnode_set_attrib(field
, "var", "stream-method");
243 purple_xmlnode_set_attrib(field
, "type", "list-single");
245 if (xf
->mode
& XEP_BYTESTREAMS
) {
246 PurpleXmlNode
*option
= purple_xmlnode_new_child(field
, "option");
247 PurpleXmlNode
*value
= purple_xmlnode_new_child(option
, "value");
248 purple_xmlnode_insert_data(value
, "http://jabber.org/protocol/bytestreams", -1);
250 if (xf
->mode
& XEP_IBB
) {
251 PurpleXmlNode
*option
= purple_xmlnode_new_child(field
, "option");
252 PurpleXmlNode
*value
= purple_xmlnode_new_child(option
, "value");
253 purple_xmlnode_insert_data(value
, "http://jabber.org/protocol/ibb", -1);
256 xep_iq_send_and_free(iq
);
260 xep_ft_si_result(PurpleXfer
*xfer
, const char *to
)
262 PurpleXmlNode
*si_node
, *feature
, *field
, *value
, *x
;
273 purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
274 iq
= xep_iq_new(bd
, XEP_IQ_RESULT
, to
, bonjour_get_jid(bd
->jabber_data
->account
), xf
->iq_id
);
278 si_node
= purple_xmlnode_new_child(iq
->node
, "si");
279 purple_xmlnode_set_namespace(si_node
, "http://jabber.org/protocol/si");
280 /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
282 feature
= purple_xmlnode_new_child(si_node
, "feature");
283 purple_xmlnode_set_namespace(feature
, "http://jabber.org/protocol/feature-neg");
285 x
= purple_xmlnode_new_child(feature
, "x");
286 purple_xmlnode_set_namespace(x
, "jabber:x:data");
287 purple_xmlnode_set_attrib(x
, "type", "submit");
289 field
= purple_xmlnode_new_child(x
, "field");
290 purple_xmlnode_set_attrib(field
, "var", "stream-method");
292 value
= purple_xmlnode_new_child(field
, "value");
293 purple_xmlnode_insert_data(value
, "http://jabber.org/protocol/bytestreams", -1);
295 xep_iq_send_and_free(iq
);
299 * Frees the whole tree of an xml node
301 * First determines the root of the xml tree and then frees the whole tree
304 * @param node The node to free the tree from
307 purple_xmlnode_free_tree(PurpleXmlNode
*node
)
309 g_return_if_fail(node
!= NULL
);
311 while(purple_xmlnode_get_parent(node
))
312 node
= purple_xmlnode_get_parent(node
);
314 purple_xmlnode_free(node
);
318 bonjour_new_xfer(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
)
324 if(who
== NULL
|| gc
== NULL
)
327 purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who
);
328 bd
= purple_connection_get_protocol_data(gc
);
332 /* Build the file transfer handle */
333 xep_xfer
= g_object_new(
335 "account", purple_connection_get_account(gc
),
336 "type", PURPLE_XFER_TYPE_SEND
,
340 xfer
= PURPLE_XFER(xep_xfer
);
343 purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd
, xep_xfer
->data
);
345 /* We don't support IBB yet */
346 /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
347 xep_xfer
->mode
= XEP_BYTESTREAMS
;
348 xep_xfer
->sid
= NULL
;
350 bd
->xfer_lists
= g_slist_append(bd
->xfer_lists
, xfer
);
356 bonjour_send_file(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
, const char *file
)
360 g_return_if_fail(gc
!= NULL
);
361 g_return_if_fail(who
!= NULL
);
363 purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who
);
365 xfer
= bonjour_new_xfer(prplxfer
, gc
, who
);
368 purple_xfer_request_accepted(xfer
, file
);
370 purple_xfer_request(xfer
);
375 bonjour_xfer_init(PurpleXfer
*xfer
)
383 purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
385 buddy
= purple_blist_find_buddy(purple_xfer_get_account(xfer
), purple_xfer_get_remote_user(xfer
));
386 /* this buddy is offline. */
387 if (buddy
== NULL
|| (bb
= purple_buddy_get_protocol_data(buddy
)) == NULL
)
390 /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
392 xf
->buddy_ip
= g_strdup(bb
->ips
->data
);
393 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_SEND
) {
394 /* initiate file transfer, send SI offer. */
395 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_SEND.\n");
396 xep_ft_si_offer(xfer
, purple_xfer_get_remote_user(xfer
));
398 /* accept file transfer request, send SI result. */
399 xep_ft_si_result(xfer
, purple_xfer_get_remote_user(xfer
));
400 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_RECEIVE.\n");
405 xep_si_parse(PurpleConnection
*pc
, PurpleXmlNode
*packet
, PurpleBuddy
*pb
)
407 const char *type
, *id
;
410 const gchar
*name
= NULL
;
412 g_return_if_fail(pc
!= NULL
);
413 g_return_if_fail(packet
!= NULL
);
414 g_return_if_fail(pb
!= NULL
);
416 bd
= purple_connection_get_protocol_data(pc
);
420 purple_debug_info("bonjour", "xep-si-parse.\n");
422 name
= purple_buddy_get_name(pb
);
424 type
= purple_xmlnode_get_attrib(packet
, "type");
425 id
= purple_xmlnode_get_attrib(packet
, "id");
429 if(purple_strequal(type
, "set")) {
431 gboolean parsed_receive
= FALSE
;
433 si
= purple_xmlnode_get_child(packet
, "si");
435 purple_debug_info("bonjour", "si offer Message type - SET.\n");
439 profile
= purple_xmlnode_get_attrib(si
, "profile");
441 if (purple_strequal(profile
, "http://jabber.org/protocol/si/profile/file-transfer")) {
442 const char *filename
= NULL
, *filesize_str
= NULL
;
443 goffset filesize
= 0;
446 const char *sid
= purple_xmlnode_get_attrib(si
, "id");
448 if ((file
= purple_xmlnode_get_child(si
, "file"))) {
449 filename
= purple_xmlnode_get_attrib(file
, "name");
450 if((filesize_str
= purple_xmlnode_get_attrib(file
, "size")))
451 filesize
= g_ascii_strtoll(filesize_str
, NULL
, 10);
454 /* TODO: Make sure that it is advertising a bytestreams transfer */
457 bonjour_xfer_receive(pc
, id
, sid
, name
, filesize
, filename
, XEP_BYTESTREAMS
);
459 parsed_receive
= TRUE
;
464 if (!parsed_receive
) {
465 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
467 purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
468 xep_ft_si_reject(bd
, id
, name
, "403", "cancel");
469 /*TODO: Send Cancel (501) */
471 } else if(purple_strequal(type
, "result")) {
472 purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
474 xfer
= bonjour_si_xfer_find(bd
, id
, name
);
477 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
478 purple_debug_info("bonjour", "xfer find fail.\n");
479 xep_ft_si_reject(bd
, id
, name
, "403", "cancel");
481 bonjour_bytestreams_init(xfer
);
483 } else if(purple_strequal(type
, "error")) {
484 purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
486 xfer
= bonjour_si_xfer_find(bd
, id
, name
);
489 purple_debug_info("bonjour", "xfer find fail.\n");
491 purple_xfer_cancel_remote(xfer
);
493 purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type
);
497 * Will compare a host with a buddy_ip.
499 * Additionally to a common 'purple_strequal(host, buddy_ip)', it will also return TRUE
500 * if 'host' is a link local IPv6 address without an appended interface
501 * identifier and 'buddy_ip' string is "host" + "%iface".
503 * Note: This may theoretically result in the attempt to connect to the wrong
504 * host, because we do not know for sure which interface the according link
505 * local IPv6 address might relate to and RFC4862 for instance only ensures the
506 * uniqueness of this address on a given link. So we could possibly have two
507 * distinct buddies with the same ipv6 link local address on two distinct
508 * interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with
509 * link local ip addresses properly...
510 * However, in practice the possiblity for such a conflict is relatively low
511 * (2011 - might be different in the future though?).
513 * @param host ipv4 or ipv6 address string
514 * @param buddy_ip ipv4 or ipv6 address string
515 * @return TRUE if they match, FALSE otherwise
518 xep_cmp_addr(const char *host
, const char *buddy_ip
)
520 #if defined(AF_INET6) && defined(HAVE_GETADDRINFO)
521 struct addrinfo hint
, *res
= NULL
;
522 common_sockaddr_t addr
;
525 memset(&hint
, 0, sizeof(hint
));
526 hint
.ai_family
= AF_UNSPEC
;
527 hint
.ai_flags
= AI_NUMERICHOST
;
529 ret
= getaddrinfo(host
, NULL
, &hint
, &res
);
532 memcpy(&addr
, res
->ai_addr
, sizeof(addr
));
534 if (res
->ai_family
!= AF_INET6
||
535 !IN6_IS_ADDR_LINKLOCAL(&addr
.in6
.sin6_addr
))
542 if(strlen(buddy_ip
) <= strlen(host
) ||
543 buddy_ip
[strlen(host
)] != '%')
546 return !strncmp(host
, buddy_ip
, strlen(host
));
550 return purple_strequal(host
, buddy_ip
);
554 xep_addr_differ(const char *buddy_ip
, const char *host
)
556 return !xep_cmp_addr(host
, buddy_ip
);
560 * Create and insert an identical twin
562 * Creates a copy of the specified node and inserts it right after
563 * this original node.
565 * @param node The node to clone
566 * @return A pointer to the new, cloned twin if successful
569 static PurpleXmlNode
*
570 purple_xmlnode_insert_twin_copy(PurpleXmlNode
*node
) {
573 g_return_val_if_fail(node
!= NULL
, NULL
);
575 copy
= purple_xmlnode_copy(node
);
576 g_return_val_if_fail(copy
!= NULL
, NULL
);
578 copy
->next
= node
->next
;
585 * Tries to append an interface scope to an IPv6 link local address.
587 * If the given address is a link local IPv6 address (with no
588 * interface scope) then we try to determine all fitting interfaces
589 * from our Bonjour IP address list.
591 * For any such found matches we insert a copy of our current xml
592 * streamhost entry right after this streamhost entry and append
593 * the determined interface to the host address of this copy.
595 * @param cur_streamhost The XML streamhost node we examine
596 * @param host The host address to examine in text form
597 * @param pb Buddy to get the list of link local IPv6 addresses
598 * and their interface from
599 * @return Returns TRUE if the specified 'host' address is a
600 * link local IPv6 address with no interface scope.
601 * Otherwise returns FALSE.
604 add_ipv6_link_local_ifaces(PurpleXmlNode
*cur_streamhost
, const char *host
,
607 PurpleXmlNode
*new_streamhost
= NULL
;
608 struct in6_addr in6_addr
;
612 if (inet_pton(AF_INET6
, host
, &in6_addr
) != 1 ||
613 !IN6_IS_ADDR_LINKLOCAL(&in6_addr
) ||
617 bb
= purple_buddy_get_protocol_data(pb
);
619 for (ip_elem
= bb
->ips
;
620 (ip_elem
= g_slist_find_custom(ip_elem
, host
, (GCompareFunc
)&xep_addr_differ
));
621 ip_elem
= ip_elem
->next
) {
622 purple_debug_info("bonjour", "Inserting an PurpleXmlNode twin copy for %s with new host address %s\n",
623 host
, (char*)ip_elem
->data
);
624 new_streamhost
= purple_xmlnode_insert_twin_copy(cur_streamhost
);
625 purple_xmlnode_set_attrib(new_streamhost
, "host", ip_elem
->data
);
629 purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n",
636 __xep_bytestreams_parse(PurpleBuddy
*pb
, PurpleXfer
*xfer
, PurpleXmlNode
*streamhost
,
640 const char *jid
, *host
, *port
;
642 XepXfer
*xf
= XEP_XFER(xfer
);
644 for(; streamhost
; streamhost
= purple_xmlnode_get_next_twin(streamhost
)) {
645 if(!(jid
= purple_xmlnode_get_attrib(streamhost
, "jid")) ||
646 !(host
= purple_xmlnode_get_attrib(streamhost
, "host")) ||
647 !(port
= purple_xmlnode_get_attrib(streamhost
, "port")) ||
648 !(portnum
= atoi(port
))) {
649 purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
653 /* skip IPv6 link local addresses with no interface scope
654 * (but try to add a new one with an interface scope then) */
655 if(add_ipv6_link_local_ifaces(streamhost
, host
, pb
))
658 tmp_iq_id
= g_strdup(iq_id
);
661 g_free(xf
->proxy_host
);
663 xf
->iq_id
= tmp_iq_id
;
664 xf
->jid
= g_strdup(jid
);
665 xf
->proxy_host
= g_strdup(host
);
666 xf
->proxy_port
= portnum
;
667 xf
->streamhost
= streamhost
;
669 purple_debug_info("bonjour", "bytestream offer parse"
670 "jid=%s host=%s port=%d.\n", jid
, host
, portnum
);
671 bonjour_bytestreams_connect(xfer
);
679 xep_bytestreams_parse(PurpleConnection
*pc
, PurpleXmlNode
*packet
, PurpleBuddy
*pb
)
681 const char *type
, *from
, *iq_id
, *sid
;
682 PurpleXmlNode
*query
, *streamhost
;
686 g_return_if_fail(pc
!= NULL
);
687 g_return_if_fail(packet
!= NULL
);
688 g_return_if_fail(pb
!= NULL
);
690 bd
= purple_connection_get_protocol_data(pc
);
694 purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
696 type
= purple_xmlnode_get_attrib(packet
, "type");
697 from
= purple_buddy_get_name(pb
);
698 query
= purple_xmlnode_get_child(packet
,"query");
702 query
= purple_xmlnode_copy(query
);
706 if(!purple_strequal(type
, "set")) {
707 purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type
);
711 purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
713 iq_id
= purple_xmlnode_get_attrib(packet
, "id");
715 sid
= purple_xmlnode_get_attrib(query
, "sid");
716 xfer
= bonjour_si_xfer_find(bd
, sid
, from
);
717 streamhost
= purple_xmlnode_get_child(query
, "streamhost");
719 if(xfer
&& streamhost
&& __xep_bytestreams_parse(pb
, xfer
, streamhost
, iq_id
))
720 return; /* success */
722 purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
724 if (iq_id
&& xfer
!= NULL
)
725 xep_ft_si_reject(bd
, iq_id
, purple_xfer_get_remote_user(xfer
), "404", "cancel");
729 bonjour_xfer_receive(PurpleConnection
*pc
, const char *id
, const char *sid
, const char *from
,
730 const goffset filesize
, const char *filename
, int option
)
736 if(pc
== NULL
|| id
== NULL
|| from
== NULL
)
739 bd
= purple_connection_get_protocol_data(pc
);
743 purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
745 /* Build the file transfer handle */
748 "account", purple_connection_get_account(pc
),
749 "type", PURPLE_XFER_TYPE_RECEIVE
,
754 xfer
= PURPLE_XFER(xf
);
757 purple_xfer_set_filename(xfer
, filename
);
758 xf
->iq_id
= g_strdup(id
);
759 xf
->sid
= g_strdup(sid
);
762 purple_xfer_set_size(xfer
, filesize
);
764 bd
->xfer_lists
= g_slist_append(bd
->xfer_lists
, xfer
);
766 purple_xfer_request(xfer
);
770 bonjour_sock5_request_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
772 PurpleXfer
*xfer
= data
;
773 XepXfer
*xf
= XEP_XFER(xfer
);
780 purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf
->sock5_req_state
);
782 switch(xf
->sock5_req_state
){
784 acceptfd
= accept(source
, NULL
, 0);
785 if(acceptfd
== -1 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)) {
787 } else if(acceptfd
== -1) {
788 /* This should cancel the ft */
789 purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno
);
792 purple_xfer_cancel_remote(xfer
);
795 purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd
);
797 _purple_network_set_common_socket_flags(acceptfd
);
798 purple_input_remove(purple_xfer_get_watcher(xfer
));
800 purple_xfer_set_watcher(xfer
, purple_input_add(acceptfd
, PURPLE_INPUT_READ
,
801 bonjour_sock5_request_cb
, xfer
));
802 xf
->sock5_req_state
++;
807 purple_xfer_set_fd(xfer
, source
);
808 len
= read(source
, xf
->rx_buf
+ xf
->rxlen
, 3);
809 if(len
< 0 && errno
== EAGAIN
)
812 purple_xfer_cancel_remote(xfer
);
815 purple_input_remove(purple_xfer_get_watcher(xfer
));
816 purple_xfer_set_watcher(xfer
, purple_input_add(source
, PURPLE_INPUT_WRITE
,
817 bonjour_sock5_request_cb
, xfer
));
818 xf
->sock5_req_state
++;
820 bonjour_sock5_request_cb(xfer
, source
, PURPLE_INPUT_WRITE
);
824 xf
->tx_buf
[0] = 0x05;
825 xf
->tx_buf
[1] = 0x00;
826 len
= write(source
, xf
->tx_buf
, 2);
827 if (len
< 0 && errno
== EAGAIN
)
831 purple_xfer_cancel_remote(xfer
);
834 purple_input_remove(purple_xfer_get_watcher(xfer
));
835 purple_xfer_set_watcher(xfer
, purple_input_add(source
, PURPLE_INPUT_READ
,
836 bonjour_sock5_request_cb
, xfer
));
837 xf
->sock5_req_state
++;
842 len
= read(source
, xf
->rx_buf
+ xf
->rxlen
, 20);
845 purple_input_remove(purple_xfer_get_watcher(xfer
));
846 purple_xfer_set_watcher(xfer
, purple_input_add(source
, PURPLE_INPUT_WRITE
,
847 bonjour_sock5_request_cb
, xfer
));
848 xf
->sock5_req_state
++;
850 bonjour_sock5_request_cb(xfer
, source
, PURPLE_INPUT_WRITE
);
854 xf
->tx_buf
[0] = 0x05;
855 xf
->tx_buf
[1] = 0x00;
856 xf
->tx_buf
[2] = 0x00;
857 xf
->tx_buf
[3] = 0x03;
858 xf
->tx_buf
[4] = strlen(xf
->buddy_ip
);
859 memcpy(xf
->tx_buf
+ 5, xf
->buddy_ip
, strlen(xf
->buddy_ip
));
860 xf
->tx_buf
[5+strlen(xf
->buddy_ip
)] = 0x00;
861 xf
->tx_buf
[6+strlen(xf
->buddy_ip
)] = 0x00;
862 len
= write(source
, xf
->tx_buf
, 7 + strlen(xf
->buddy_ip
));
863 if (len
< 0 && errno
== EAGAIN
) {
865 } else if (len
< 0) {
867 purple_xfer_cancel_remote(xfer
);
870 purple_input_remove(purple_xfer_get_watcher(xfer
));
871 purple_xfer_set_watcher(xfer
, 0);
874 purple_xfer_start(xfer
, source
, NULL
, -1);
884 bonjour_bytestreams_listen(int sock
, gpointer data
)
886 PurpleXfer
*xfer
= data
;
889 PurpleXmlNode
*query
, *streamhost
;
894 purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock
);
895 if (sock
< 0 || xfer
== NULL
) {
896 /*purple_xfer_cancel_local(xfer);*/
900 purple_xfer_set_watcher(xfer
, purple_input_add(sock
, PURPLE_INPUT_READ
,
901 bonjour_sock5_request_cb
, xfer
));
903 xf
->listen_data
= NULL
;
907 iq
= xep_iq_new(bd
, XEP_IQ_SET
, purple_xfer_get_remote_user(xfer
), bonjour_get_jid(bd
->jabber_data
->account
), xf
->sid
);
909 query
= purple_xmlnode_new_child(iq
->node
, "query");
910 purple_xmlnode_set_namespace(query
, "http://jabber.org/protocol/bytestreams");
911 purple_xmlnode_set_attrib(query
, "sid", xf
->sid
);
912 purple_xmlnode_set_attrib(query
, "mode", "tcp");
914 purple_xfer_set_local_port(xfer
, purple_network_get_port_from_fd(sock
));
916 local_ips
= bonjour_jabber_get_local_ips(sock
);
918 port
= g_strdup_printf("%hu", purple_xfer_get_local_port(xfer
));
920 streamhost
= purple_xmlnode_new_child(query
, "streamhost");
921 purple_xmlnode_set_attrib(streamhost
, "jid", xf
->sid
);
922 purple_xmlnode_set_attrib(streamhost
, "host", local_ips
->data
);
923 purple_xmlnode_set_attrib(streamhost
, "port", port
);
924 g_free(local_ips
->data
);
925 local_ips
= g_slist_delete_link(local_ips
, local_ips
);
929 xep_iq_send_and_free(iq
);
933 bonjour_bytestreams_init(PurpleXfer
*xfer
)
939 purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
942 xf
->listen_data
= purple_network_listen_range(0, 0, AF_UNSPEC
, SOCK_STREAM
, FALSE
,
943 bonjour_bytestreams_listen
, xfer
);
944 if (xf
->listen_data
== NULL
)
945 purple_xfer_cancel_local(xfer
);
951 bonjour_bytestreams_connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
953 PurpleXfer
*xfer
= data
;
954 XepXfer
*xf
= XEP_XFER(xfer
);
956 PurpleXmlNode
*q_node
, *tmp_node
;
958 gboolean ret
= FALSE
;
960 xf
->proxy_connection
= NULL
;
963 purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n",
964 xf
->proxy_host
, error_message
? error_message
: "(null)");
966 tmp_node
= purple_xmlnode_get_next_twin(xf
->streamhost
);
967 ret
= __xep_bytestreams_parse(xf
->pb
, xfer
, tmp_node
, xf
->iq_id
);
970 xep_ft_si_reject(xf
->data
, xf
->iq_id
, purple_xfer_get_remote_user(xfer
), "404", "cancel");
971 /* Cancel the connection */
972 purple_xfer_cancel_local(xfer
);
977 purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
981 /* Here, start the file transfer.*/
983 /* Notify Initiator of Connection */
984 iq
= xep_iq_new(bd
, XEP_IQ_RESULT
, purple_xfer_get_remote_user(xfer
), bonjour_get_jid(bd
->jabber_data
->account
), xf
->iq_id
);
985 q_node
= purple_xmlnode_new_child(iq
->node
, "query");
986 purple_xmlnode_set_namespace(q_node
, "http://jabber.org/protocol/bytestreams");
987 tmp_node
= purple_xmlnode_new_child(q_node
, "streamhost-used");
988 purple_xmlnode_set_attrib(tmp_node
, "jid", xf
->jid
);
989 xep_iq_send_and_free(iq
);
991 purple_xfer_start(xfer
, source
, NULL
, -1);
995 bonjour_bytestreams_connect(PurpleXfer
*xfer
)
998 PurpleAccount
*account
= NULL
;
1002 const gchar
*name
= NULL
;
1003 unsigned char hashval
[20];
1004 gsize digest_len
= 20;
1011 purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
1013 xf
= XEP_XFER(xfer
);
1016 name
= purple_buddy_get_name(pb
);
1017 account
= purple_buddy_get_account(pb
);
1019 p
= g_strdup_printf("%s%s%s", xf
->sid
, name
, bonjour_get_jid(account
));
1021 hash
= g_checksum_new(G_CHECKSUM_SHA1
);
1022 g_checksum_update(hash
, (guchar
*)p
, -1);
1023 g_checksum_get_digest(hash
, hashval
, &digest_len
);
1024 g_checksum_free(hash
);
1028 memset(dstaddr
, 0, 41);
1030 for(i
= 0; i
< 20; i
++, p
+= 2)
1031 snprintf(p
, 3, "%02x", hashval
[i
]);
1033 xf
->proxy_info
= purple_proxy_info_new();
1034 purple_proxy_info_set_proxy_type(xf
->proxy_info
, PURPLE_PROXY_SOCKS5
);
1035 purple_proxy_info_set_host(xf
->proxy_info
, xf
->proxy_host
);
1036 purple_proxy_info_set_port(xf
->proxy_info
, xf
->proxy_port
);
1037 xf
->proxy_connection
= purple_proxy_connect_socks5_account(
1038 purple_account_get_connection(account
),
1042 bonjour_bytestreams_connect_cb
, xfer
);
1044 if(xf
->proxy_connection
== NULL
) {
1045 xep_ft_si_reject(xf
->data
, xf
->iq_id
, purple_xfer_get_remote_user(xfer
), "404", "cancel");
1046 /* Cancel the connection */
1047 purple_xfer_cancel_local(xfer
);
1052 xep_xfer_init(XepXfer
*xfer
) {
1057 xep_xfer_finalize(GObject
*obj
) {
1058 XepXfer
*xf
= XEP_XFER(obj
);
1060 BonjourData
*bd
= (BonjourData
*)xf
->data
;
1062 bd
->xfer_lists
= g_slist_remove(bd
->xfer_lists
, PURPLE_XFER(xf
));
1063 purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd
->xfer_lists
);
1065 if (xf
->proxy_connection
!= NULL
) {
1066 purple_proxy_connect_cancel(xf
->proxy_connection
);
1068 if (xf
->proxy_info
!= NULL
) {
1069 purple_proxy_info_destroy(xf
->proxy_info
);
1071 if (xf
->listen_data
!= NULL
) {
1072 purple_network_listen_cancel(xf
->listen_data
);
1077 g_free(xf
->proxy_host
);
1078 g_free(xf
->buddy_ip
);
1081 g_clear_pointer(&xf
->streamhost
, purple_xmlnode_free_tree
);
1083 G_OBJECT_CLASS(xep_xfer_parent_class
)->finalize(obj
);
1087 xep_xfer_class_finalize(XepXferClass
*klass
) {
1092 xep_xfer_class_init(XepXferClass
*klass
) {
1093 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
1094 PurpleXferClass
*xfer_class
= PURPLE_XFER_CLASS(klass
);
1096 obj_class
->finalize
= xep_xfer_finalize
;
1098 xfer_class
->init
= bonjour_xfer_init
;
1099 xfer_class
->request_denied
= bonjour_xfer_request_denied
;
1100 xfer_class
->cancel_recv
= bonjour_xfer_cancel_recv
;
1101 xfer_class
->cancel_send
= bonjour_xfer_cancel_send
;
1102 xfer_class
->end
= bonjour_xfer_end
;
1106 xep_xfer_register(GTypeModule
*module
) {
1107 xep_xfer_register_type(module
);