Use GSList functions instead of manual iterations
[pidgin-git.git] / libpurple / protocols / bonjour / bonjour_ft.c
blob2a21087601ad609d44f6baa08bad6307882254e1
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 <purple.h>
25 #include "bonjour.h"
26 #include "bonjour_ft.h"
28 static void
29 bonjour_bytestreams_init(PurpleXfer *xfer);
30 static void
31 bonjour_bytestreams_connect(PurpleXfer *xfer);
32 static void
33 bonjour_xfer_init(PurpleXfer *xfer);
34 static void
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;
41 struct _XepXfer
43 PurpleXfer parent;
45 void *data;
46 char *filename;
47 int filesize;
48 char *iq_id;
49 char *sid;
50 char *recv_id;
51 char *buddy_ip;
52 int mode;
53 PurpleNetworkListenData *listen_data;
54 int sock5_req_state;
55 int rxlen;
56 char rx_buf[0x500];
57 char tx_buf[0x500];
58 PurpleProxyInfo *proxy_info;
59 PurpleProxyConnectData *proxy_connection;
60 char *jid;
61 char *proxy_host;
62 int proxy_port;
63 PurpleXmlNode *streamhost;
64 PurpleBuddy *pb;
67 G_DEFINE_DYNAMIC_TYPE(XepXfer, xep_xfer, PURPLE_TYPE_XFER);
69 static void
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;
73 XepIq *iq;
75 g_return_if_fail(error_code != NULL);
76 g_return_if_fail(error_type != NULL);
78 if(!to || !id) {
79 purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
80 return;
83 iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id);
84 if(iq == NULL)
85 return;
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");
118 if(xf) {
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 {
129 int fd;
130 guint handle;
133 static void
134 _wait_for_socket_close(gpointer data, gint source, PurpleInputCondition cond)
136 struct socket_cleanup *sc = data;
137 char buf[1];
138 int ret;
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);
145 close(sc->fd);
146 g_free(sc);
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);
165 static PurpleXfer*
166 bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
168 GSList *xfers;
169 PurpleXfer *xfer;
170 XepXfer *xf;
172 if(!sid || !from || !bd)
173 return NULL;
175 purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
176 sid, from);
178 for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) {
179 xfer = xfers->data;
180 if(xfer == NULL)
181 break;
182 xf = XEP_XFER(xfer);
183 if(xf == NULL)
184 break;
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))
187 return xfer;
190 purple_debug_info("bonjour", "Look for xfer list fail\n");
192 return NULL;
195 static void
196 xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
198 PurpleXmlNode *si_node, *feature, *field, *file, *x;
199 XepIq *iq;
200 XepXfer *xf = XEP_XFER(xfer);
201 BonjourData *bd = NULL;
202 char buf[32];
204 if(!xf)
205 return;
207 bd = xf->data;
208 if(!bd)
209 return;
211 purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id);
213 /* Assign stream id. */
214 g_free(xf->iq_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);
217 if(iq == NULL)
218 return;
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");
224 g_free(xf->sid);
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);
259 static void
260 xep_ft_si_result(PurpleXfer *xfer, const char *to)
262 PurpleXmlNode *si_node, *feature, *field, *value, *x;
263 XepIq *iq;
264 XepXfer *xf;
265 BonjourData *bd;
267 if(!to || !xfer)
268 return;
269 xf = XEP_XFER(xfer);
271 bd = xf->data;
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);
275 if(iq == NULL)
276 return;
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
302 * from there.
304 * @param node The node to free the tree from
306 static void
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);
317 PurpleXfer *
318 bonjour_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
320 PurpleXfer *xfer;
321 XepXfer *xep_xfer;
322 BonjourData *bd;
324 if(who == NULL || gc == NULL)
325 return NULL;
327 purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
328 bd = purple_connection_get_protocol_data(gc);
329 if(bd == NULL)
330 return NULL;
332 /* Build the file transfer handle */
333 xep_xfer = g_object_new(
334 XEP_TYPE_XFER,
335 "account", purple_connection_get_account(gc),
336 "type", PURPLE_XFER_TYPE_SEND,
337 "remote-user", who,
338 NULL
340 xfer = PURPLE_XFER(xep_xfer);
341 xep_xfer->data = bd;
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);
352 return xfer;
355 void
356 bonjour_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file)
358 PurpleXfer *xfer;
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);
367 if (file)
368 purple_xfer_request_accepted(xfer, file);
369 else
370 purple_xfer_request(xfer);
374 static void
375 bonjour_xfer_init(PurpleXfer *xfer)
377 PurpleBuddy *buddy;
378 BonjourBuddy *bb;
379 XepXfer *xf;
381 xf = XEP_XFER(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)
388 return;
390 /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
391 if (bb->ips)
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));
397 } else {
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");
404 void
405 xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb)
407 const char *type, *id;
408 BonjourData *bd;
409 PurpleXfer *xfer;
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);
417 if(bd == NULL)
418 return;
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");
426 if(!type)
427 return;
429 if(purple_strequal(type, "set")) {
430 PurpleXmlNode *si;
431 gboolean parsed_receive = FALSE;
433 si = purple_xmlnode_get_child(packet, "si");
435 purple_debug_info("bonjour", "si offer Message type - SET.\n");
436 if (si) {
437 const char *profile;
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;
444 PurpleXmlNode *file;
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 */
456 if (filename) {
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);
476 if(xfer == NULL) {
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");
480 } else
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);
488 if(xfer == NULL)
489 purple_debug_info("bonjour", "xfer find fail.\n");
490 else
491 purple_xfer_cancel_remote(xfer);
492 } else
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
517 static gboolean
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;
523 int ret;
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);
530 if(ret)
531 goto out;
532 memcpy(&addr, res->ai_addr, sizeof(addr));
534 if (res->ai_family != AF_INET6 ||
535 !IN6_IS_ADDR_LINKLOCAL(&addr.in6.sin6_addr))
537 freeaddrinfo(res);
538 goto out;
540 freeaddrinfo(res);
542 if(strlen(buddy_ip) <= strlen(host) ||
543 buddy_ip[strlen(host)] != '%')
544 return FALSE;
546 return !strncmp(host, buddy_ip, strlen(host));
548 out:
549 #endif
550 return purple_strequal(host, buddy_ip);
553 static inline gint
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
567 * or NULL otherwise.
569 static PurpleXmlNode *
570 purple_xmlnode_insert_twin_copy(PurpleXmlNode *node) {
571 PurpleXmlNode *copy;
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;
579 node->next = copy;
581 return copy;
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.
603 static gboolean
604 add_ipv6_link_local_ifaces(PurpleXmlNode *cur_streamhost, const char *host,
605 PurpleBuddy *pb)
607 PurpleXmlNode *new_streamhost = NULL;
608 struct in6_addr in6_addr;
609 BonjourBuddy *bb;
610 GSList *ip_elem;
612 if (inet_pton(AF_INET6, host, &in6_addr) != 1 ||
613 !IN6_IS_ADDR_LINKLOCAL(&in6_addr) ||
614 strchr(host, '%'))
615 return FALSE;
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);
628 if (!new_streamhost)
629 purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n",
630 host);
632 return TRUE;
635 static gboolean
636 __xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, PurpleXmlNode *streamhost,
637 const char *iq_id)
639 char *tmp_iq_id;
640 const char *jid, *host, *port;
641 int portnum;
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");
650 continue;
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))
656 continue;
658 tmp_iq_id = g_strdup(iq_id);
659 g_free(xf->iq_id);
660 g_free(xf->jid);
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;
668 xf->pb = pb;
669 purple_debug_info("bonjour", "bytestream offer parse"
670 "jid=%s host=%s port=%d.\n", jid, host, portnum);
671 bonjour_bytestreams_connect(xfer);
672 return TRUE;
675 return FALSE;
678 void
679 xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb)
681 const char *type, *from, *iq_id, *sid;
682 PurpleXmlNode *query, *streamhost;
683 BonjourData *bd;
684 PurpleXfer *xfer;
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);
691 if(bd == NULL)
692 return;
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");
699 if(!type)
700 return;
702 query = purple_xmlnode_copy(query);
703 if (!query)
704 return;
706 if(!purple_strequal(type, "set")) {
707 purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
708 return;
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");
728 static void
729 bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
730 const goffset filesize, const char *filename, int option)
732 PurpleXfer *xfer;
733 XepXfer *xf;
734 BonjourData *bd;
736 if(pc == NULL || id == NULL || from == NULL)
737 return;
739 bd = purple_connection_get_protocol_data(pc);
740 if(bd == NULL)
741 return;
743 purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
745 /* Build the file transfer handle */
746 xf = g_object_new(
747 XEP_TYPE_XFER,
748 "account", purple_connection_get_account(pc),
749 "type", PURPLE_XFER_TYPE_RECEIVE,
750 "remote-user", from,
751 NULL
754 xfer = PURPLE_XFER(xf);
756 xf->data = bd;
757 purple_xfer_set_filename(xfer, filename);
758 xf->iq_id = g_strdup(id);
759 xf->sid = g_strdup(sid);
761 if(filesize > 0)
762 purple_xfer_set_size(xfer, filesize);
764 bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
766 purple_xfer_request(xfer);
769 static void
770 bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
772 PurpleXfer *xfer = data;
773 XepXfer *xf = XEP_XFER(xfer);
774 int acceptfd;
775 int len = 0;
777 if(xf == NULL)
778 return;
780 purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf->sock5_req_state);
782 switch(xf->sock5_req_state){
783 case 0x00:
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);
791 close(source);
792 purple_xfer_cancel_remote(xfer);
793 return;
794 } else {
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));
799 close(source);
800 purple_xfer_set_watcher(xfer, purple_input_add(acceptfd, PURPLE_INPUT_READ,
801 bonjour_sock5_request_cb, xfer));
802 xf->sock5_req_state++;
803 xf->rxlen = 0;
805 break;
806 case 0x01:
807 purple_xfer_set_fd(xfer, source);
808 len = read(source, xf->rx_buf + xf->rxlen, 3);
809 if(len < 0 && errno == EAGAIN)
810 return;
811 else if(len <= 0){
812 purple_xfer_cancel_remote(xfer);
813 return;
814 } else {
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++;
819 xf->rxlen = 0;
820 bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
822 break;
823 case 0x02:
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)
828 return;
829 else if (len < 0) {
830 close(source);
831 purple_xfer_cancel_remote(xfer);
832 return;
833 } else {
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++;
838 xf->rxlen = 0;
840 break;
841 case 0x03:
842 len = read(source, xf->rx_buf + xf->rxlen, 20);
843 if(len<=0){
844 } else {
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++;
849 xf->rxlen = 0;
850 bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
852 break;
853 case 0x04:
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) {
864 return;
865 } else if (len < 0) {
866 close(source);
867 purple_xfer_cancel_remote(xfer);
868 return;
869 } else {
870 purple_input_remove(purple_xfer_get_watcher(xfer));
871 purple_xfer_set_watcher(xfer, 0);
872 xf->rxlen = 0;
873 /*close(source);*/
874 purple_xfer_start(xfer, source, NULL, -1);
876 break;
877 default:
878 break;
880 return;
883 static void
884 bonjour_bytestreams_listen(int sock, gpointer data)
886 PurpleXfer *xfer = data;
887 XepXfer *xf;
888 XepIq *iq;
889 PurpleXmlNode *query, *streamhost;
890 gchar *port;
891 GSList *local_ips;
892 BonjourData *bd;
894 purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
895 if (sock < 0 || xfer == NULL) {
896 /*purple_xfer_cancel_local(xfer);*/
897 return;
900 purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
901 bonjour_sock5_request_cb, xfer));
902 xf = XEP_XFER(xfer);
903 xf->listen_data = NULL;
905 bd = xf->data;
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));
919 while(local_ips) {
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);
927 g_free(port);
929 xep_iq_send_and_free(iq);
932 static void
933 bonjour_bytestreams_init(PurpleXfer *xfer)
935 XepXfer *xf;
936 if(xfer == NULL)
937 return;
939 purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
940 xf = XEP_XFER(xfer);
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);
947 return;
950 static void
951 bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
953 PurpleXfer *xfer = data;
954 XepXfer *xf = XEP_XFER(xfer);
955 XepIq *iq;
956 PurpleXmlNode *q_node, *tmp_node;
957 BonjourData *bd;
958 gboolean ret = FALSE;
960 xf->proxy_connection = NULL;
962 if(source < 0) {
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);
969 if (!ret) {
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);
974 return;
977 purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
979 bd = xf->data;
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);
994 static void
995 bonjour_bytestreams_connect(PurpleXfer *xfer)
997 PurpleBuddy *pb;
998 PurpleAccount *account = NULL;
999 GChecksum *hash;
1000 XepXfer *xf;
1001 char dstaddr[41];
1002 const gchar *name = NULL;
1003 unsigned char hashval[20];
1004 gsize digest_len = 20;
1005 char *p;
1006 int i;
1008 if(xfer == NULL)
1009 return;
1011 purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
1013 xf = XEP_XFER(xfer);
1015 pb = xf->pb;
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);
1026 g_free(p);
1028 memset(dstaddr, 0, 41);
1029 p = dstaddr;
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),
1039 account,
1040 xf->proxy_info,
1041 dstaddr, 0,
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);
1051 static void
1052 xep_xfer_init(XepXfer *xfer) {
1056 static void
1057 xep_xfer_finalize(GObject *obj) {
1058 XepXfer *xf = XEP_XFER(obj);
1060 BonjourData *bd = (BonjourData*)xf->data;
1061 if(bd != NULL) {
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);
1075 g_free(xf->iq_id);
1076 g_free(xf->jid);
1077 g_free(xf->proxy_host);
1078 g_free(xf->buddy_ip);
1079 g_free(xf->sid);
1081 g_clear_pointer(&xf->streamhost, purple_xmlnode_free_tree);
1083 G_OBJECT_CLASS(xep_xfer_parent_class)->finalize(obj);
1086 static void
1087 xep_xfer_class_finalize(XepXferClass *klass) {
1091 static void
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;
1105 void
1106 xep_xfer_register(GTypeModule *module) {
1107 xep_xfer_register_type(module);