mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / bonjour / bonjour_ft.c
blobe8c77a4f3f2452d106ee08eff77b81fec93b5914
1 /*
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
6 * source distribution.
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
22 #include "internal.h"
23 #include "util.h"
24 #include "debug.h"
25 #include "notify.h"
26 #include "proxy.h"
27 #include "xfer.h"
28 #include "buddy.h"
29 #include "bonjour.h"
30 #include "bonjour_ft.h"
32 static void
33 bonjour_bytestreams_init(PurpleXfer *xfer);
34 static void
35 bonjour_bytestreams_connect(PurpleXfer *xfer);
36 static void
37 bonjour_xfer_init(PurpleXfer *xfer);
38 static void
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;
45 struct _XepXfer
47 PurpleXfer parent;
49 void *data;
50 char *filename;
51 int filesize;
52 char *iq_id;
53 char *sid;
54 char *recv_id;
55 char *buddy_ip;
56 int mode;
57 PurpleNetworkListenData *listen_data;
58 int sock5_req_state;
59 int rxlen;
60 char rx_buf[0x500];
61 char tx_buf[0x500];
62 PurpleProxyInfo *proxy_info;
63 PurpleProxyConnectData *proxy_connection;
64 char *jid;
65 char *proxy_host;
66 int proxy_port;
67 PurpleXmlNode *streamhost;
68 PurpleBuddy *pb;
71 G_DEFINE_DYNAMIC_TYPE(XepXfer, xep_xfer, PURPLE_TYPE_XFER);
73 static void
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;
77 XepIq *iq;
79 g_return_if_fail(error_code != NULL);
80 g_return_if_fail(error_type != NULL);
82 if(!to || !id) {
83 purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
84 return;
87 iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id);
88 if(iq == NULL)
89 return;
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");
122 if(xf) {
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 {
133 int fd;
134 guint handle;
137 static void
138 _wait_for_socket_close(gpointer data, gint source, PurpleInputCondition cond)
140 struct socket_cleanup *sc = data;
141 char buf[1];
142 int ret;
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);
149 close(sc->fd);
150 g_free(sc);
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);
169 static PurpleXfer*
170 bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
172 GSList *xfers;
173 PurpleXfer *xfer;
174 XepXfer *xf;
176 if(!sid || !from || !bd)
177 return NULL;
179 purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
180 sid, from);
182 for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) {
183 xfer = xfers->data;
184 if(xfer == NULL)
185 break;
186 xf = XEP_XFER(xfer);
187 if(xf == NULL)
188 break;
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))
191 return xfer;
194 purple_debug_info("bonjour", "Look for xfer list fail\n");
196 return NULL;
199 static void
200 xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
202 PurpleXmlNode *si_node, *feature, *field, *file, *x;
203 XepIq *iq;
204 XepXfer *xf = XEP_XFER(xfer);
205 BonjourData *bd = NULL;
206 char buf[32];
208 if(!xf)
209 return;
211 bd = xf->data;
212 if(!bd)
213 return;
215 purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id);
217 /* Assign stream id. */
218 g_free(xf->iq_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);
221 if(iq == NULL)
222 return;
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");
228 g_free(xf->sid);
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);
263 static void
264 xep_ft_si_result(PurpleXfer *xfer, const char *to)
266 PurpleXmlNode *si_node, *feature, *field, *value, *x;
267 XepIq *iq;
268 XepXfer *xf;
269 BonjourData *bd;
271 if(!to || !xfer)
272 return;
273 xf = XEP_XFER(xfer);
275 bd = xf->data;
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);
279 if(iq == NULL)
280 return;
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
306 * from there.
308 * @param node The node to free the tree from
310 static void
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);
321 PurpleXfer *
322 bonjour_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
324 PurpleXfer *xfer;
325 XepXfer *xep_xfer;
326 BonjourData *bd;
328 if(who == NULL || gc == NULL)
329 return NULL;
331 purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
332 bd = purple_connection_get_protocol_data(gc);
333 if(bd == NULL)
334 return NULL;
336 /* Build the file transfer handle */
337 xep_xfer = g_object_new(
338 XEP_TYPE_XFER,
339 "account", purple_connection_get_account(gc),
340 "type", PURPLE_XFER_TYPE_SEND,
341 "remote-user", who,
342 NULL
344 xfer = PURPLE_XFER(xep_xfer);
345 xep_xfer->data = bd;
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);
356 return xfer;
359 void
360 bonjour_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file)
362 PurpleXfer *xfer;
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);
371 if (file)
372 purple_xfer_request_accepted(xfer, file);
373 else
374 purple_xfer_request(xfer);
378 static void
379 bonjour_xfer_init(PurpleXfer *xfer)
381 PurpleBuddy *buddy;
382 BonjourBuddy *bb;
383 XepXfer *xf;
385 xf = XEP_XFER(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)
392 return;
394 /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
395 if (bb->ips)
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));
401 } else {
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");
408 void
409 xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb)
411 const char *type, *id;
412 BonjourData *bd;
413 PurpleXfer *xfer;
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);
421 if(bd == NULL)
422 return;
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");
430 if(!type)
431 return;
433 if(purple_strequal(type, "set")) {
434 PurpleXmlNode *si;
435 gboolean parsed_receive = FALSE;
437 si = purple_xmlnode_get_child(packet, "si");
439 purple_debug_info("bonjour", "si offer Message type - SET.\n");
440 if (si) {
441 const char *profile;
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;
448 PurpleXmlNode *file;
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 */
460 if (filename) {
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);
480 if(xfer == NULL) {
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");
484 } else
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);
492 if(xfer == NULL)
493 purple_debug_info("bonjour", "xfer find fail.\n");
494 else
495 purple_xfer_cancel_remote(xfer);
496 } else
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
521 static gboolean
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;
527 int ret;
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);
534 if(ret)
535 goto out;
536 memcpy(&addr, res->ai_addr, sizeof(addr));
538 if (res->ai_family != AF_INET6 ||
539 !IN6_IS_ADDR_LINKLOCAL(&addr.in6.sin6_addr))
541 freeaddrinfo(res);
542 goto out;
544 freeaddrinfo(res);
546 if(strlen(buddy_ip) <= strlen(host) ||
547 buddy_ip[strlen(host)] != '%')
548 return FALSE;
550 return !strncmp(host, buddy_ip, strlen(host));
552 out:
553 #endif
554 return purple_strequal(host, buddy_ip);
557 static inline gint
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
571 * or NULL otherwise.
573 static PurpleXmlNode *
574 purple_xmlnode_insert_twin_copy(PurpleXmlNode *node) {
575 PurpleXmlNode *copy;
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;
583 node->next = copy;
585 return copy;
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.
607 static gboolean
608 add_ipv6_link_local_ifaces(PurpleXmlNode *cur_streamhost, const char *host,
609 PurpleBuddy *pb)
611 PurpleXmlNode *new_streamhost = NULL;
612 struct in6_addr in6_addr;
613 BonjourBuddy *bb;
614 GSList *ip_elem;
616 if (inet_pton(AF_INET6, host, &in6_addr) != 1 ||
617 !IN6_IS_ADDR_LINKLOCAL(&in6_addr) ||
618 strchr(host, '%'))
619 return FALSE;
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);
632 if (!new_streamhost)
633 purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n",
634 host);
636 return TRUE;
639 static gboolean
640 __xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, PurpleXmlNode *streamhost,
641 const char *iq_id)
643 char *tmp_iq_id;
644 const char *jid, *host, *port;
645 int portnum;
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");
654 continue;
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))
660 continue;
662 tmp_iq_id = g_strdup(iq_id);
663 g_free(xf->iq_id);
664 g_free(xf->jid);
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;
672 xf->pb = pb;
673 purple_debug_info("bonjour", "bytestream offer parse"
674 "jid=%s host=%s port=%d.\n", jid, host, portnum);
675 bonjour_bytestreams_connect(xfer);
676 return TRUE;
679 return FALSE;
682 void
683 xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb)
685 const char *type, *from, *iq_id, *sid;
686 PurpleXmlNode *query, *streamhost;
687 BonjourData *bd;
688 PurpleXfer *xfer;
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);
695 if(bd == NULL)
696 return;
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");
703 if(!type)
704 return;
706 query = purple_xmlnode_copy(query);
707 if (!query)
708 return;
710 if(!purple_strequal(type, "set")) {
711 purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
712 return;
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");
732 static void
733 bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
734 const goffset filesize, const char *filename, int option)
736 PurpleXfer *xfer;
737 XepXfer *xf;
738 BonjourData *bd;
740 if(pc == NULL || id == NULL || from == NULL)
741 return;
743 bd = purple_connection_get_protocol_data(pc);
744 if(bd == NULL)
745 return;
747 purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
749 /* Build the file transfer handle */
750 xf = g_object_new(
751 XEP_TYPE_XFER,
752 "account", purple_connection_get_account(pc),
753 "type", PURPLE_XFER_TYPE_RECEIVE,
754 "remote-user", from,
755 NULL
758 xfer = PURPLE_XFER(xf);
760 xf->data = bd;
761 purple_xfer_set_filename(xfer, filename);
762 xf->iq_id = g_strdup(id);
763 xf->sid = g_strdup(sid);
765 if(filesize > 0)
766 purple_xfer_set_size(xfer, filesize);
768 bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
770 purple_xfer_request(xfer);
773 static void
774 bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
776 PurpleXfer *xfer = data;
777 XepXfer *xf = XEP_XFER(xfer);
778 int acceptfd;
779 int len = 0;
781 if(xf == NULL)
782 return;
784 purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf->sock5_req_state);
786 switch(xf->sock5_req_state){
787 case 0x00:
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);
795 close(source);
796 purple_xfer_cancel_remote(xfer);
797 return;
798 } else {
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));
803 close(source);
804 purple_xfer_set_watcher(xfer, purple_input_add(acceptfd, PURPLE_INPUT_READ,
805 bonjour_sock5_request_cb, xfer));
806 xf->sock5_req_state++;
807 xf->rxlen = 0;
809 break;
810 case 0x01:
811 purple_xfer_set_fd(xfer, source);
812 len = read(source, xf->rx_buf + xf->rxlen, 3);
813 if(len < 0 && errno == EAGAIN)
814 return;
815 else if(len <= 0){
816 purple_xfer_cancel_remote(xfer);
817 return;
818 } else {
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++;
823 xf->rxlen = 0;
824 bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
826 break;
827 case 0x02:
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)
832 return;
833 else if (len < 0) {
834 close(source);
835 purple_xfer_cancel_remote(xfer);
836 return;
837 } else {
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++;
842 xf->rxlen = 0;
844 break;
845 case 0x03:
846 len = read(source, xf->rx_buf + xf->rxlen, 20);
847 if(len<=0){
848 } else {
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++;
853 xf->rxlen = 0;
854 bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
856 break;
857 case 0x04:
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) {
868 return;
869 } else if (len < 0) {
870 close(source);
871 purple_xfer_cancel_remote(xfer);
872 return;
873 } else {
874 purple_input_remove(purple_xfer_get_watcher(xfer));
875 purple_xfer_set_watcher(xfer, 0);
876 xf->rxlen = 0;
877 /*close(source);*/
878 purple_xfer_start(xfer, source, NULL, -1);
880 break;
881 default:
882 break;
884 return;
887 static void
888 bonjour_bytestreams_listen(int sock, gpointer data)
890 PurpleXfer *xfer = data;
891 XepXfer *xf;
892 XepIq *iq;
893 PurpleXmlNode *query, *streamhost;
894 gchar *port;
895 GSList *local_ips;
896 BonjourData *bd;
898 purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
899 if (sock < 0 || xfer == NULL) {
900 /*purple_xfer_cancel_local(xfer);*/
901 return;
904 purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
905 bonjour_sock5_request_cb, xfer));
906 xf = XEP_XFER(xfer);
907 xf->listen_data = NULL;
909 bd = xf->data;
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));
923 while(local_ips) {
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);
931 g_free(port);
933 xep_iq_send_and_free(iq);
936 static void
937 bonjour_bytestreams_init(PurpleXfer *xfer)
939 XepXfer *xf;
940 if(xfer == NULL)
941 return;
943 purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
944 xf = XEP_XFER(xfer);
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);
951 return;
954 static void
955 bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
957 PurpleXfer *xfer = data;
958 XepXfer *xf = XEP_XFER(xfer);
959 XepIq *iq;
960 PurpleXmlNode *q_node, *tmp_node;
961 BonjourData *bd;
962 gboolean ret = FALSE;
964 xf->proxy_connection = NULL;
966 if(source < 0) {
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);
973 if (!ret) {
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);
978 return;
981 purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
983 bd = xf->data;
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);
998 static void
999 bonjour_bytestreams_connect(PurpleXfer *xfer)
1001 PurpleBuddy *pb;
1002 PurpleAccount *account = NULL;
1003 GChecksum *hash;
1004 XepXfer *xf;
1005 char dstaddr[41];
1006 const gchar *name = NULL;
1007 unsigned char hashval[20];
1008 gsize digest_len = 20;
1009 char *p;
1010 int i;
1012 if(xfer == NULL)
1013 return;
1015 purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
1017 xf = XEP_XFER(xfer);
1019 pb = xf->pb;
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);
1030 g_free(p);
1032 memset(dstaddr, 0, 41);
1033 p = dstaddr;
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),
1043 account,
1044 xf->proxy_info,
1045 dstaddr, 0,
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);
1055 static void
1056 xep_xfer_init(XepXfer *xfer) {
1060 static void
1061 xep_xfer_finalize(GObject *obj) {
1062 XepXfer *xf = XEP_XFER(obj);
1064 BonjourData *bd = (BonjourData*)xf->data;
1065 if(bd != NULL) {
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);
1079 g_free(xf->iq_id);
1080 g_free(xf->jid);
1081 g_free(xf->proxy_host);
1082 g_free(xf->buddy_ip);
1083 g_free(xf->sid);
1085 purple_xmlnode_free_tree(xf->streamhost);
1087 G_OBJECT_CLASS(xep_xfer_parent_class)->finalize(obj);
1090 static void
1091 xep_xfer_class_finalize(XepXferClass *klass) {
1095 static void
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;
1109 void
1110 xep_xfer_register(GTypeModule *module) {
1111 xep_xfer_register_type(module);