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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "bonjour_ft.h"
34 bonjour_bytestreams_init(PurpleXfer
*xfer
);
36 bonjour_bytestreams_connect(PurpleXfer
*xfer
, PurpleBuddy
*pb
);
38 bonjour_xfer_init(PurpleXfer
*xfer
);
40 bonjour_xfer_receive(PurpleConnection
*pc
, const char *id
, const char *sid
, const char *from
,
41 const int filesize
, const char *filename
, int option
);
42 static void bonjour_free_xfer(PurpleXfer
*xfer
);
44 /* Look for specific xfer handle */
45 static unsigned int next_id
= 0;
48 xep_ft_si_reject(BonjourData
*bd
, const char *id
, const char *to
, const char *error_code
, const char *error_type
)
53 g_return_if_fail(error_code
!= NULL
);
54 g_return_if_fail(error_type
!= NULL
);
59 purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
60 iq
= xep_iq_new(bd
, XEP_IQ_ERROR
, to
, purple_account_get_username(bd
->jabber_data
->account
), id
);
64 error_node
= xmlnode_new_child(iq
->node
, "error");
65 xmlnode_set_attrib(error_node
, "code", error_code
);
66 xmlnode_set_attrib(error_node
, "type", error_type
);
68 /* TODO: Make this better */
69 if (!strcmp(error_code
, "403")) {
70 xmlnode
*tmp_node
= xmlnode_new_child(error_node
, "forbidden");
71 xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
73 tmp_node
= xmlnode_new_child(error_node
, "text");
74 xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
75 xmlnode_insert_data(tmp_node
, "Offer Declined", -1);
76 } else if (!strcmp(error_code
, "404")) {
77 xmlnode
*tmp_node
= xmlnode_new_child(error_node
, "item-not-found");
78 xmlnode_set_namespace(tmp_node
, "urn:ietf:params:xml:ns:xmpp-stanzas");
81 xep_iq_send_and_free(iq
);
84 static void bonjour_xfer_cancel_send(PurpleXfer
*xfer
)
86 purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
87 bonjour_free_xfer(xfer
);
90 static void bonjour_xfer_request_denied(PurpleXfer
*xfer
)
92 XepXfer
*xf
= xfer
->data
;
94 purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
97 xep_ft_si_reject(xf
->data
, xf
->sid
, xfer
->who
, "403", "cancel");
99 bonjour_free_xfer(xfer
);
102 static void bonjour_xfer_cancel_recv(PurpleXfer
*xfer
)
104 purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
105 bonjour_free_xfer(xfer
);
108 struct socket_cleanup
{
114 _wait_for_socket_close(gpointer data
, gint source
, PurpleInputCondition cond
)
116 struct socket_cleanup
*sc
= data
;
120 ret
= recv(source
, buf
, 1, 0);
122 if (ret
== 0 || (ret
== -1 && !(errno
== EAGAIN
|| errno
== EWOULDBLOCK
))) {
123 purple_debug_info("bonjour", "Client completed recieving; closing server socket.\n");
124 purple_input_remove(sc
->handle
);
130 static void bonjour_xfer_end(PurpleXfer
*xfer
)
132 purple_debug_info("bonjour", "Bonjour-xfer-end.\n");
134 /* We can't allow the server side to close the connection until the client is complete,
135 * otherwise there is a RST resulting in an error on the client side */
136 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
&& purple_xfer_is_completed(xfer
)) {
137 struct socket_cleanup
*sc
= g_new0(struct socket_cleanup
, 1);
140 sc
->handle
= purple_input_add(sc
->fd
, PURPLE_INPUT_READ
,
141 _wait_for_socket_close
, sc
);
144 bonjour_free_xfer(xfer
);
148 bonjour_si_xfer_find(BonjourData
*bd
, const char *sid
, const char *from
)
154 if(!sid
|| !from
|| !bd
)
157 purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
160 for(xfers
= bd
->xfer_lists
; xfers
; xfers
= xfers
->next
) {
167 if(xf
->sid
&& xfer
->who
&& !strcmp(xf
->sid
, sid
) &&
168 !strcmp(xfer
->who
, from
))
172 purple_debug_info("bonjour", "Look for xfer list fail\n");
178 xep_ft_si_offer(PurpleXfer
*xfer
, const gchar
*to
)
180 xmlnode
*si_node
, *feature
, *field
, *file
, *x
;
182 XepXfer
*xf
= xfer
->data
;
183 BonjourData
*bd
= NULL
;
193 purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id
);
195 /* Assign stream id. */
197 xf
->iq_id
= g_strdup_printf("%u", next_id
++);
198 iq
= xep_iq_new(xf
->data
, XEP_IQ_SET
, to
, purple_account_get_username(bd
->jabber_data
->account
), xf
->iq_id
);
202 /*Construct Stream initialization offer message.*/
203 si_node
= xmlnode_new_child(iq
->node
, "si");
204 xmlnode_set_namespace(si_node
, "http://jabber.org/protocol/si");
205 xmlnode_set_attrib(si_node
, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
207 xf
->sid
= g_strdup(xf
->iq_id
);
208 xmlnode_set_attrib(si_node
, "id", xf
->sid
);
210 file
= xmlnode_new_child(si_node
, "file");
211 xmlnode_set_namespace(file
, "http://jabber.org/protocol/si/profile/file-transfer");
212 xmlnode_set_attrib(file
, "name", xfer
->filename
);
213 g_snprintf(buf
, sizeof(buf
), "%" G_GSIZE_FORMAT
, xfer
->size
);
214 xmlnode_set_attrib(file
, "size", buf
);
216 feature
= xmlnode_new_child(si_node
, "feature");
217 xmlnode_set_namespace(feature
, "http://jabber.org/protocol/feature-neg");
219 x
= xmlnode_new_child(feature
, "x");
220 xmlnode_set_namespace(x
, "jabber:x:data");
221 xmlnode_set_attrib(x
, "type", "form");
223 field
= xmlnode_new_child(x
, "field");
224 xmlnode_set_attrib(field
, "var", "stream-method");
225 xmlnode_set_attrib(field
, "type", "list-single");
227 if (xf
->mode
& XEP_BYTESTREAMS
) {
228 xmlnode
*option
= xmlnode_new_child(field
, "option");
229 xmlnode
*value
= xmlnode_new_child(option
, "value");
230 xmlnode_insert_data(value
, "http://jabber.org/protocol/bytestreams", -1);
232 if (xf
->mode
& XEP_IBB
) {
233 xmlnode
*option
= xmlnode_new_child(field
, "option");
234 xmlnode
*value
= xmlnode_new_child(option
, "value");
235 xmlnode_insert_data(value
, "http://jabber.org/protocol/ibb", -1);
238 xep_iq_send_and_free(iq
);
242 xep_ft_si_result(PurpleXfer
*xfer
, char *to
)
244 xmlnode
*si_node
, *feature
, *field
, *value
, *x
;
257 purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
258 iq
= xep_iq_new(bd
, XEP_IQ_RESULT
, to
, purple_account_get_username(bd
->jabber_data
->account
), xf
->iq_id
);
262 si_node
= xmlnode_new_child(iq
->node
, "si");
263 xmlnode_set_namespace(si_node
, "http://jabber.org/protocol/si");
264 /*xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
266 feature
= xmlnode_new_child(si_node
, "feature");
267 xmlnode_set_namespace(feature
, "http://jabber.org/protocol/feature-neg");
269 x
= xmlnode_new_child(feature
, "x");
270 xmlnode_set_namespace(x
, "jabber:x:data");
271 xmlnode_set_attrib(x
, "type", "submit");
273 field
= xmlnode_new_child(x
, "field");
274 xmlnode_set_attrib(field
, "var", "stream-method");
276 value
= xmlnode_new_child(field
, "value");
277 xmlnode_insert_data(value
, "http://jabber.org/protocol/bytestreams", -1);
279 xep_iq_send_and_free(iq
);
283 bonjour_free_xfer(PurpleXfer
*xfer
)
288 purple_debug_info("bonjour", "bonjour-free-xfer-null.\n");
292 purple_debug_info("bonjour", "bonjour-free-xfer-%p.\n", xfer
);
294 xf
= (XepXfer
*)xfer
->data
;
296 BonjourData
*bd
= (BonjourData
*)xf
->data
;
298 bd
->xfer_lists
= g_slist_remove(bd
->xfer_lists
, xfer
);
299 purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd
->xfer_lists
);
301 if (xf
->proxy_connection
!= NULL
)
302 purple_proxy_connect_cancel(xf
->proxy_connection
);
303 if (xf
->proxy_info
!= NULL
)
304 purple_proxy_info_destroy(xf
->proxy_info
);
305 if (xf
->listen_data
!= NULL
)
306 purple_network_listen_cancel(xf
->listen_data
);
309 g_free(xf
->proxy_host
);
310 g_free(xf
->buddy_ip
);
316 purple_debug_info("bonjour", "Need close socket=%d.\n", xfer
->fd
);
320 bonjour_new_xfer(PurpleConnection
*gc
, const char *who
)
326 if(who
== NULL
|| gc
== NULL
)
329 purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who
);
330 bd
= (BonjourData
*) gc
->proto_data
;
334 /* Build the file transfer handle */
335 xfer
= purple_xfer_new(gc
->account
, PURPLE_XFER_SEND
, who
);
336 xfer
->data
= xep_xfer
= g_new0(XepXfer
, 1);
339 purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd
, xep_xfer
->data
);
341 /* We don't support IBB yet */
342 /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
343 xep_xfer
->mode
= XEP_BYTESTREAMS
;
344 xep_xfer
->sid
= NULL
;
346 purple_xfer_set_init_fnc(xfer
, bonjour_xfer_init
);
347 purple_xfer_set_cancel_send_fnc(xfer
, bonjour_xfer_cancel_send
);
348 purple_xfer_set_end_fnc(xfer
, bonjour_xfer_end
);
350 bd
->xfer_lists
= g_slist_append(bd
->xfer_lists
, xfer
);
356 bonjour_send_file(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(gc
, who
);
368 purple_xfer_request_accepted(xfer
, file
);
370 purple_xfer_request(xfer
);
375 bonjour_xfer_init(PurpleXfer
*xfer
)
381 xf
= (XepXfer
*)xfer
->data
;
385 purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
387 buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
388 /* this buddy is offline. */
389 if (buddy
== NULL
|| buddy
->proto_data
== NULL
)
392 bb
= (BonjourBuddy
*)buddy
->proto_data
;
393 /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
395 xf
->buddy_ip
= g_strdup(bb
->ips
->data
);
396 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
) {
397 /* initiate file transfer, send SI offer. */
398 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
399 xep_ft_si_offer(xfer
, xfer
->who
);
401 /* accept file transfer request, send SI result. */
402 xep_ft_si_result(xfer
, xfer
->who
);
403 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
408 xep_si_parse(PurpleConnection
*pc
, xmlnode
*packet
, PurpleBuddy
*pb
)
410 const char *type
, *id
;
414 g_return_if_fail(pc
!= NULL
);
415 g_return_if_fail(packet
!= NULL
);
416 g_return_if_fail(pb
!= NULL
);
418 bd
= (BonjourData
*) pc
->proto_data
;
422 purple_debug_info("bonjour", "xep-si-parse.\n");
424 type
= xmlnode_get_attrib(packet
, "type");
425 id
= xmlnode_get_attrib(packet
, "id");
427 if(!strcmp(type
, "set")) {
430 gboolean parsed_receive
= FALSE
;
432 si
= xmlnode_get_child(packet
, "si");
434 purple_debug_info("bonjour", "si offer Message type - SET.\n");
435 if (si
&& (profile
= xmlnode_get_attrib(si
, "profile"))
436 && !strcmp(profile
, "http://jabber.org/protocol/si/profile/file-transfer")) {
437 const char *filename
= NULL
, *filesize_str
= NULL
;
441 const char *sid
= xmlnode_get_attrib(si
, "id");
443 if ((file
= xmlnode_get_child(si
, "file"))) {
444 filename
= xmlnode_get_attrib(file
, "name");
445 if((filesize_str
= xmlnode_get_attrib(file
, "size")))
446 filesize
= atoi(filesize_str
);
449 /* TODO: Make sure that it is advertising a bytestreams transfer */
452 bonjour_xfer_receive(pc
, id
, sid
, pb
->name
, filesize
, filename
, XEP_BYTESTREAMS
);
454 parsed_receive
= TRUE
;
458 if (!parsed_receive
) {
459 purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
460 xep_ft_si_reject((BonjourData
*)pc
->proto_data
, id
, pb
->name
, "403", "cancel");
461 /*TODO: Send Cancel (501) */
463 } else if(!strcmp(type
, "result")) {
464 purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
466 xfer
= bonjour_si_xfer_find(bd
, id
, pb
->name
);
469 purple_debug_info("bonjour", "xfer find fail.\n");
470 xep_ft_si_reject((BonjourData
*)pc
->proto_data
, id
, pb
->name
, "403", "cancel");
472 bonjour_bytestreams_init(xfer
);
474 } else if(!strcmp(type
, "error")) {
475 purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
477 xfer
= bonjour_si_xfer_find(bd
, id
, pb
->name
);
480 purple_debug_info("bonjour", "xfer find fail.\n");
482 purple_xfer_cancel_remote(xfer
);
484 purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type
);
489 xep_bytestreams_parse(PurpleConnection
*pc
, xmlnode
*packet
, PurpleBuddy
*pb
)
491 const char *type
, *from
;
495 g_return_if_fail(pc
!= NULL
);
496 g_return_if_fail(packet
!= NULL
);
497 g_return_if_fail(pb
!= NULL
);
499 bd
= (BonjourData
*) pc
->proto_data
;
503 purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
505 type
= xmlnode_get_attrib(packet
, "type");
507 query
= xmlnode_get_child(packet
,"query");
509 if(!strcmp(type
, "set")) {
510 const char *iq_id
, *sid
;
511 gboolean found
= FALSE
;
514 purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
516 iq_id
= xmlnode_get_attrib(packet
, "id");
518 sid
= xmlnode_get_attrib(query
, "sid");
519 xfer
= bonjour_si_xfer_find(bd
, sid
, from
);
522 const char *jid
, *host
, *port
;
527 xf
= (XepXfer
*)xfer
->data
;
528 for(streamhost
= xmlnode_get_child(query
, "streamhost");
530 streamhost
= xmlnode_get_next_twin(streamhost
)) {
532 if((jid
= xmlnode_get_attrib(streamhost
, "jid")) &&
533 (host
= xmlnode_get_attrib(streamhost
, "host")) &&
534 (port
= xmlnode_get_attrib(streamhost
, "port")) &&
535 (portnum
= atoi(port
))) {
537 if(!strcmp(host
, xf
->buddy_ip
)) {
539 xf
->iq_id
= g_strdup(iq_id
);
540 xf
->jid
= g_strdup(jid
);
541 xf
->proxy_host
= g_strdup(host
);
542 xf
->proxy_port
= portnum
;
543 purple_debug_info("bonjour", "bytestream offer parse"
544 "jid=%s host=%s port=%d.\n", jid
, host
, portnum
);
545 bonjour_bytestreams_connect(xfer
, pb
);
550 purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
558 purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
560 if (iq_id
&& xfer
!= NULL
)
561 xep_ft_si_reject(bd
, iq_id
, xfer
->who
, "404", "cancel");
565 purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type
);
571 bonjour_xfer_receive(PurpleConnection
*pc
, const char *id
, const char *sid
, const char *from
,
572 const int filesize
, const char *filename
, int option
)
578 if(pc
== NULL
|| id
== NULL
|| from
== NULL
)
581 bd
= (BonjourData
*) pc
->proto_data
;
585 purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
587 /* Build the file transfer handle */
588 xfer
= purple_xfer_new(pc
->account
, PURPLE_XFER_RECEIVE
, from
);
589 xfer
->data
= xf
= g_new0(XepXfer
, 1);
591 purple_xfer_set_filename(xfer
, filename
);
592 xf
->iq_id
= g_strdup(id
);
593 xf
->sid
= g_strdup(sid
);
596 purple_xfer_set_size(xfer
, filesize
);
597 purple_xfer_set_init_fnc(xfer
, bonjour_xfer_init
);
598 purple_xfer_set_request_denied_fnc(xfer
, bonjour_xfer_request_denied
);
599 purple_xfer_set_cancel_recv_fnc(xfer
, bonjour_xfer_cancel_recv
);
600 purple_xfer_set_end_fnc(xfer
, bonjour_xfer_end
);
602 bd
->xfer_lists
= g_slist_append(bd
->xfer_lists
, xfer
);
604 purple_xfer_request(xfer
);
608 bonjour_sock5_request_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
610 PurpleXfer
*xfer
= data
;
611 XepXfer
*xf
= xfer
->data
;
618 purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf
->sock5_req_state
);
620 switch(xf
->sock5_req_state
){
622 acceptfd
= accept(source
, NULL
, 0);
623 if(acceptfd
== -1 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)) {
625 } else if(acceptfd
== -1) {
626 /* This should cancel the ft */
627 purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno
);
629 purple_input_remove(xfer
->watcher
);
632 purple_xfer_cancel_remote(xfer
);
637 purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd
);
639 flags
= fcntl(acceptfd
, F_GETFL
);
640 fcntl(acceptfd
, F_SETFL
, flags
| O_NONBLOCK
);
642 fcntl(acceptfd
, F_SETFD
, FD_CLOEXEC
);
645 purple_input_remove(xfer
->watcher
);
647 xfer
->watcher
= purple_input_add(acceptfd
, PURPLE_INPUT_READ
,
648 bonjour_sock5_request_cb
, xfer
);
649 xf
->sock5_req_state
++;
655 len
= read(source
, xf
->rx_buf
+ xf
->rxlen
, 3);
656 if(len
< 0 && errno
== EAGAIN
)
659 purple_input_remove(xfer
->watcher
);
662 purple_xfer_cancel_remote(xfer
);
665 purple_input_remove(xfer
->watcher
);
666 xfer
->watcher
= purple_input_add(source
, PURPLE_INPUT_WRITE
,
667 bonjour_sock5_request_cb
, xfer
);
668 xf
->sock5_req_state
++;
670 bonjour_sock5_request_cb(xfer
, source
, PURPLE_INPUT_WRITE
);
674 xf
->tx_buf
[0] = 0x05;
675 xf
->tx_buf
[1] = 0x00;
676 len
= write(source
, xf
->tx_buf
, 2);
677 if (len
< 0 && errno
== EAGAIN
)
680 purple_input_remove(xfer
->watcher
);
683 purple_xfer_cancel_remote(xfer
);
686 purple_input_remove(xfer
->watcher
);
687 xfer
->watcher
= purple_input_add(source
, PURPLE_INPUT_READ
,
688 bonjour_sock5_request_cb
, xfer
);
689 xf
->sock5_req_state
++;
694 len
= read(source
, xf
->rx_buf
+ xf
->rxlen
, 20);
697 purple_input_remove(xfer
->watcher
);
698 xfer
->watcher
= purple_input_add(source
, PURPLE_INPUT_WRITE
,
699 bonjour_sock5_request_cb
, xfer
);
700 xf
->sock5_req_state
++;
702 bonjour_sock5_request_cb(xfer
, source
, PURPLE_INPUT_WRITE
);
706 xf
->tx_buf
[0] = 0x05;
707 xf
->tx_buf
[1] = 0x00;
708 xf
->tx_buf
[2] = 0x00;
709 xf
->tx_buf
[3] = 0x03;
710 xf
->tx_buf
[4] = strlen(xf
->buddy_ip
);
711 memcpy(xf
->tx_buf
+ 5, xf
->buddy_ip
, strlen(xf
->buddy_ip
));
712 xf
->tx_buf
[5+strlen(xf
->buddy_ip
)] = 0x00;
713 xf
->tx_buf
[6+strlen(xf
->buddy_ip
)] = 0x00;
714 len
= write(source
, xf
->tx_buf
, 7 + strlen(xf
->buddy_ip
));
715 if (len
< 0 && errno
== EAGAIN
) {
717 } else if (len
< 0) {
718 purple_input_remove(xfer
->watcher
);
721 purple_xfer_cancel_remote(xfer
);
724 purple_input_remove(xfer
->watcher
);
728 purple_xfer_start(xfer
, source
, NULL
, -1);
738 bonjour_bytestreams_listen(int sock
, gpointer data
)
740 PurpleXfer
*xfer
= data
;
743 xmlnode
*query
, *streamhost
;
745 const char *next_ip
, *local_ip
;
746 const char token
[] = ";";
749 purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock
);
750 if (sock
< 0 || xfer
== NULL
) {
751 /*purple_xfer_cancel_local(xfer);*/
755 xfer
->watcher
= purple_input_add(sock
, PURPLE_INPUT_READ
,
756 bonjour_sock5_request_cb
, xfer
);
757 xf
= (XepXfer
*)xfer
->data
;
758 xf
->listen_data
= NULL
;
762 iq
= xep_iq_new(bd
, XEP_IQ_SET
, xfer
->who
, purple_account_get_username(bd
->jabber_data
->account
), xf
->sid
);
764 query
= xmlnode_new_child(iq
->node
, "query");
765 xmlnode_set_namespace(query
, "http://jabber.org/protocol/bytestreams");
766 xmlnode_set_attrib(query
, "sid", xf
->sid
);
767 xmlnode_set_attrib(query
, "mode", "tcp");
769 xfer
->local_port
= purple_network_get_port_from_fd(sock
);
771 local_ip
= purple_network_get_my_ip_ext2(sock
);
772 /* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
773 next_ip
= strtok((char *)local_ip
, token
);
775 port
= g_strdup_printf("%hu", xfer
->local_port
);
776 while(next_ip
!= NULL
) {
777 streamhost
= xmlnode_new_child(query
, "streamhost");
778 xmlnode_set_attrib(streamhost
, "jid", xf
->sid
);
779 xmlnode_set_attrib(streamhost
, "host", next_ip
);
780 xmlnode_set_attrib(streamhost
, "port", port
);
781 next_ip
= strtok(NULL
, token
);
785 xep_iq_send_and_free(iq
);
789 bonjour_bytestreams_init(PurpleXfer
*xfer
)
794 purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
796 purple_network_listen_map_external(FALSE
);
797 xf
->listen_data
= purple_network_listen_range(0, 0, SOCK_STREAM
,
798 bonjour_bytestreams_listen
, xfer
);
799 purple_network_listen_map_external(TRUE
);
800 if (xf
->listen_data
== NULL
) {
801 purple_xfer_cancel_local(xfer
);
807 bonjour_bytestreams_connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
809 PurpleXfer
*xfer
= data
;
810 XepXfer
*xf
= xfer
->data
;
812 xmlnode
*q_node
, *tmp_node
;
815 xf
->proxy_connection
= NULL
;
818 purple_debug_error("bonjour", "Error connecting via SOCKS5 - %s\n",
819 error_message
? error_message
: "(null)");
820 xep_ft_si_reject(xf
->data
, xf
->iq_id
, xfer
->who
, "404", "cancel");
821 /* Cancel the connection */
822 purple_xfer_cancel_local(xfer
);
826 purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
830 /* Here, start the file transfer.*/
832 /* Notify Initiator of Connection */
833 iq
= xep_iq_new(bd
, XEP_IQ_RESULT
, xfer
->who
, purple_account_get_username(bd
->jabber_data
->account
), xf
->iq_id
);
834 q_node
= xmlnode_new_child(iq
->node
, "query");
835 xmlnode_set_namespace(q_node
, "http://jabber.org/protocol/bytestreams");
836 tmp_node
= xmlnode_new_child(q_node
, "streamhost-used");
837 xmlnode_set_attrib(tmp_node
, "jid", xf
->jid
);
838 xep_iq_send_and_free(iq
);
840 purple_xfer_start(xfer
, source
, NULL
, -1);
844 bonjour_bytestreams_connect(PurpleXfer
*xfer
, PurpleBuddy
*pb
)
848 unsigned char hashval
[20];
855 purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
857 xf
= (XepXfer
*)xfer
->data
;
861 p
= g_strdup_printf("%s%s%s", xf
->sid
, pb
->name
, purple_account_get_username(pb
->account
));
862 purple_cipher_digest_region("sha1", (guchar
*)p
, strlen(p
),
863 sizeof(hashval
), hashval
, NULL
);
866 memset(dstaddr
, 0, 41);
868 for(i
= 0; i
< 20; i
++, p
+= 2)
869 snprintf(p
, 3, "%02x", hashval
[i
]);
871 xf
->proxy_info
= purple_proxy_info_new();
872 purple_proxy_info_set_type(xf
->proxy_info
, PURPLE_PROXY_SOCKS5
);
873 purple_proxy_info_set_host(xf
->proxy_info
, xf
->proxy_host
);
874 purple_proxy_info_set_port(xf
->proxy_info
, xf
->proxy_port
);
875 xf
->proxy_connection
= purple_proxy_connect_socks5(NULL
, xf
->proxy_info
,
877 bonjour_bytestreams_connect_cb
, xfer
);
879 if(xf
->proxy_connection
== NULL
) {
880 xep_ft_si_reject(xf
->data
, xf
->iq_id
, xfer
->who
, "404", "cancel");
881 /* Cancel the connection */
882 purple_xfer_cancel_local(xfer
);