* purple - Bonjour Protocol Plugin * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA bonjour_bytestreams_init(PurpleXfer *xfer); bonjour_bytestreams_connect(PurpleXfer *xfer); bonjour_xfer_init(PurpleXfer *xfer); bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from, const goffset filesize, const char *filename, int option); /* Look for specific xfer handle */ static unsigned int next_id = 0; PurpleNetworkListenData *listen_data; PurpleProxyInfo *proxy_info; PurpleProxyConnectData *proxy_connection; PurpleXmlNode *streamhost; G_DEFINE_DYNAMIC_TYPE(XepXfer, xep_xfer, PURPLE_TYPE_XFER); xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type) PurpleXmlNode *error_node; g_return_if_fail(error_code != NULL); g_return_if_fail(error_type != NULL); purple_debug_info("bonjour", "xep file transfer stream initialization error.\n"); iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->xmpp_data->account), id); error_node = purple_xmlnode_new_child(iq->node, "error"); purple_xmlnode_set_attrib(error_node, "code", error_code); purple_xmlnode_set_attrib(error_node, "type", error_type); /* TODO: Make this better */ if (purple_strequal(error_code, "403")) { PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "forbidden"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); tmp_node = purple_xmlnode_new_child(error_node, "text"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); purple_xmlnode_insert_data(tmp_node, "Offer Declined", -1); } else if (purple_strequal(error_code, "404")) { PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "item-not-found"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); xep_iq_send_and_free(iq); static void bonjour_xfer_cancel_send(PurpleXfer *xfer) purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n"); static void bonjour_xfer_request_denied(PurpleXfer *xfer) XepXfer *xf = XEP_XFER(xfer); purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n"); xep_ft_si_reject(xf->data, xf->sid, purple_xfer_get_remote_user(xfer), "403", "cancel"); static void bonjour_xfer_cancel_recv(PurpleXfer *xfer) purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n"); _wait_for_socket_close(gpointer data, gint source, PurpleInputCondition cond) struct socket_cleanup *sc = data; ret = recv(source, buf, 1, 0); if (ret == 0 || (ret == -1 && !(errno == EAGAIN || errno == EWOULDBLOCK))) { purple_debug_info("bonjour", "Client completed recieving; closing server socket.\n"); purple_input_remove(sc->handle); static void bonjour_xfer_end(PurpleXfer *xfer) purple_debug_info("bonjour", "Bonjour-xfer-end.\n"); /* We can't allow the server side to close the connection until the client is complete, * otherwise there is a RST resulting in an error on the client side */ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND && purple_xfer_is_completed(xfer)) { struct socket_cleanup *sc = g_new0(struct socket_cleanup, 1); sc->fd = purple_xfer_get_fd(xfer); purple_xfer_set_fd(xfer, -1); sc->handle = purple_input_add(sc->fd, PURPLE_INPUT_READ, _wait_for_socket_close, sc); bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from) purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n", for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) { if(xf->sid && purple_xfer_get_remote_user(xfer) && purple_strequal(xf->sid, sid) && purple_strequal(purple_xfer_get_remote_user(xfer), from)) purple_debug_info("bonjour", "Look for xfer list fail\n"); xep_ft_si_offer(PurpleXfer *xfer, const gchar *to) PurpleXmlNode *si_node, *feature, *field, *file, *x; XepXfer *xf = XEP_XFER(xfer); purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id); xf->iq_id = g_strdup_printf("%u", next_id++); iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->xmpp_data->account), xf->iq_id); /*Construct Stream initialization offer message.*/ si_node = purple_xmlnode_new_child(iq->node, "si"); purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer"); xf->sid = g_strdup(xf->iq_id); purple_xmlnode_set_attrib(si_node, "id", xf->sid); file = purple_xmlnode_new_child(si_node, "file"); purple_xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer"); purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer)); g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer)); purple_xmlnode_set_attrib(file, "size", buf); feature = purple_xmlnode_new_child(si_node, "feature"); purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = purple_xmlnode_new_child(feature, "x"); purple_xmlnode_set_namespace(x, "jabber:x:data"); purple_xmlnode_set_attrib(x, "type", "form"); field = purple_xmlnode_new_child(x, "field"); purple_xmlnode_set_attrib(field, "var", "stream-method"); purple_xmlnode_set_attrib(field, "type", "list-single"); if (xf->mode & XEP_BYTESTREAMS) { PurpleXmlNode *option = purple_xmlnode_new_child(field, "option"); PurpleXmlNode *value = purple_xmlnode_new_child(option, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); if (xf->mode & XEP_IBB) { PurpleXmlNode *option = purple_xmlnode_new_child(field, "option"); PurpleXmlNode *value = purple_xmlnode_new_child(option, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); xep_iq_send_and_free(iq); xep_ft_si_result(PurpleXfer *xfer, const char *to) PurpleXmlNode *si_node, *feature, *field, *value, *x; purple_debug_info("bonjour", "xep file transfer stream initialization result.\n"); iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->xmpp_data->account), xf->iq_id); si_node = purple_xmlnode_new_child(iq->node, "si"); purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/ feature = purple_xmlnode_new_child(si_node, "feature"); purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = purple_xmlnode_new_child(feature, "x"); purple_xmlnode_set_namespace(x, "jabber:x:data"); purple_xmlnode_set_attrib(x, "type", "submit"); field = purple_xmlnode_new_child(x, "field"); purple_xmlnode_set_attrib(field, "var", "stream-method"); value = purple_xmlnode_new_child(field, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); xep_iq_send_and_free(iq); * Frees the whole tree of an xml node * First determines the root of the xml tree and then frees the whole tree * @param node The node to free the tree from purple_xmlnode_free_tree(PurpleXmlNode *node) g_return_if_fail(node != NULL); while(purple_xmlnode_get_parent(node)) node = purple_xmlnode_get_parent(node); purple_xmlnode_free(node); bonjour_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) if(who == NULL || gc == NULL) purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who); bd = purple_connection_get_protocol_data(gc); /* Build the file transfer handle */ "account", purple_connection_get_account(gc), "type", PURPLE_XFER_TYPE_SEND, xfer = PURPLE_XFER(xep_xfer); purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data); /* We don't support IBB yet */ /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/ xep_xfer->mode = XEP_BYTESTREAMS; bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer); bonjour_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file) g_return_if_fail(gc != NULL); g_return_if_fail(who != NULL); purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who); xfer = bonjour_new_xfer(prplxfer, gc, who); purple_xfer_request_accepted(xfer, file); purple_xfer_request(xfer); bonjour_xfer_init(PurpleXfer *xfer) purple_debug_info("bonjour", "Bonjour-xfer-init.\n"); buddy = purple_blist_find_buddy(purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer)); /* this buddy is offline. */ if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL) /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */ xf->buddy_ip = g_strdup(bb->ips->data); if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) { /* initiate file transfer, send SI offer. */ purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_SEND.\n"); xep_ft_si_offer(xfer, purple_xfer_get_remote_user(xfer)); /* accept file transfer request, send SI result. */ xep_ft_si_result(xfer, purple_xfer_get_remote_user(xfer)); purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_RECEIVE.\n"); xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb) const gchar *name = NULL; g_return_if_fail(pc != NULL); g_return_if_fail(packet != NULL); g_return_if_fail(pb != NULL); bd = purple_connection_get_protocol_data(pc); purple_debug_info("bonjour", "xep-si-parse.\n"); name = purple_buddy_get_name(pb); type = purple_xmlnode_get_attrib(packet, "type"); id = purple_xmlnode_get_attrib(packet, "id"); if(purple_strequal(type, "set")) { gboolean parsed_receive = FALSE; si = purple_xmlnode_get_child(packet, "si"); purple_debug_info("bonjour", "si offer Message type - SET.\n"); profile = purple_xmlnode_get_attrib(si, "profile"); if (purple_strequal(profile, "http://jabber.org/protocol/si/profile/file-transfer")) { const char *filename = NULL, *filesize_str = NULL; const char *sid = purple_xmlnode_get_attrib(si, "id"); if ((file = purple_xmlnode_get_child(si, "file"))) { filename = purple_xmlnode_get_attrib(file, "name"); if((filesize_str = purple_xmlnode_get_attrib(file, "size"))) filesize = g_ascii_strtoll(filesize_str, NULL, 10); /* TODO: Make sure that it is advertising a bytestreams transfer */ bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS); BonjourData *bd = purple_connection_get_protocol_data(pc); purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n"); xep_ft_si_reject(bd, id, name, "403", "cancel"); /*TODO: Send Cancel (501) */ } else if(purple_strequal(type, "result")) { purple_debug_info("bonjour", "si offer Message type - RESULT.\n"); xfer = bonjour_si_xfer_find(bd, id, name); BonjourData *bd = purple_connection_get_protocol_data(pc); purple_debug_info("bonjour", "xfer find fail.\n"); xep_ft_si_reject(bd, id, name, "403", "cancel"); bonjour_bytestreams_init(xfer); } else if(purple_strequal(type, "error")) { purple_debug_info("bonjour", "si offer Message type - ERROR.\n"); xfer = bonjour_si_xfer_find(bd, id, name); purple_debug_info("bonjour", "xfer find fail.\n"); purple_xfer_cancel_remote(xfer); purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type); * Will compare a host with a buddy_ip. * Additionally to a common 'purple_strequal(host, buddy_ip)', it will also return TRUE * if 'host' is a link local IPv6 address without an appended interface * identifier and 'buddy_ip' string is "host" + "%iface". * Note: This may theoretically result in the attempt to connect to the wrong * host, because we do not know for sure which interface the according link * local IPv6 address might relate to and RFC4862 for instance only ensures the * uniqueness of this address on a given link. So we could possibly have two * distinct buddies with the same ipv6 link local address on two distinct * interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with * link local ip addresses properly... * However, in practice the possiblity for such a conflict is relatively low * (2011 - might be different in the future though?). * @param host ipv4 or ipv6 address string * @param buddy_ip ipv4 or ipv6 address string * @return TRUE if they match, FALSE otherwise xep_cmp_addr(const char *host, const char *buddy_ip) #if defined(AF_INET6) && defined(HAVE_GETADDRINFO) struct addrinfo hint, *res = NULL; memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_UNSPEC; hint.ai_flags = AI_NUMERICHOST; ret = getaddrinfo(host, NULL, &hint, &res); memcpy(&addr, res->ai_addr, sizeof(addr)); if (res->ai_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr.in6.sin6_addr)) if(strlen(buddy_ip) <= strlen(host) || buddy_ip[strlen(host)] != '%') return !strncmp(host, buddy_ip, strlen(host)); return purple_strequal(host, buddy_ip); xep_addr_differ(const char *buddy_ip, const char *host) return !xep_cmp_addr(host, buddy_ip); * Create and insert an identical twin * Creates a copy of the specified node and inserts it right after * @param node The node to clone * @return A pointer to the new, cloned twin if successful purple_xmlnode_insert_twin_copy(PurpleXmlNode *node) { g_return_val_if_fail(node != NULL, NULL); copy = purple_xmlnode_copy(node); g_return_val_if_fail(copy != NULL, NULL); * Tries to append an interface scope to an IPv6 link local address. * If the given address is a link local IPv6 address (with no * interface scope) then we try to determine all fitting interfaces * from our Bonjour IP address list. * For any such found matches we insert a copy of our current xml * streamhost entry right after this streamhost entry and append * the determined interface to the host address of this copy. * @param cur_streamhost The XML streamhost node we examine * @param host The host address to examine in text form * @param pb Buddy to get the list of link local IPv6 addresses * and their interface from * @return Returns TRUE if the specified 'host' address is a * link local IPv6 address with no interface scope. * Otherwise returns FALSE. add_ipv6_link_local_ifaces(PurpleXmlNode *cur_streamhost, const char *host, PurpleXmlNode *new_streamhost = NULL; struct in6_addr in6_addr; if (inet_pton(AF_INET6, host, &in6_addr) != 1 || !IN6_IS_ADDR_LINKLOCAL(&in6_addr) || bb = purple_buddy_get_protocol_data(pb); (ip_elem = g_slist_find_custom(ip_elem, host, (GCompareFunc)&xep_addr_differ)); ip_elem = ip_elem->next) { purple_debug_info("bonjour", "Inserting an PurpleXmlNode twin copy for %s with new host address %s\n", host, (char*)ip_elem->data); new_streamhost = purple_xmlnode_insert_twin_copy(cur_streamhost); purple_xmlnode_set_attrib(new_streamhost, "host", ip_elem->data); purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n", __xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, PurpleXmlNode *streamhost, const char *jid, *host, *port; XepXfer *xf = XEP_XFER(xfer); for(; streamhost; streamhost = purple_xmlnode_get_next_twin(streamhost)) { if(!(jid = purple_xmlnode_get_attrib(streamhost, "jid")) || !(host = purple_xmlnode_get_attrib(streamhost, "host")) || !(port = purple_xmlnode_get_attrib(streamhost, "port")) || !(portnum = atoi(port))) { purple_debug_info("bonjour", "bytestream offer Message parse error.\n"); /* skip IPv6 link local addresses with no interface scope * (but try to add a new one with an interface scope then) */ if(add_ipv6_link_local_ifaces(streamhost, host, pb)) tmp_iq_id = g_strdup(iq_id); xf->proxy_host = g_strdup(host); xf->proxy_port = portnum; xf->streamhost = streamhost; purple_debug_info("bonjour", "bytestream offer parse" "jid=%s host=%s port=%d.\n", jid, host, portnum); bonjour_bytestreams_connect(xfer); xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb) const char *type, *from, *iq_id, *sid; PurpleXmlNode *query, *streamhost; g_return_if_fail(pc != NULL); g_return_if_fail(packet != NULL); g_return_if_fail(pb != NULL); bd = purple_connection_get_protocol_data(pc); purple_debug_info("bonjour", "xep-bytestreams-parse.\n"); type = purple_xmlnode_get_attrib(packet, "type"); from = purple_buddy_get_name(pb); query = purple_xmlnode_get_child(packet,"query"); query = purple_xmlnode_copy(query); if(!purple_strequal(type, "set")) { purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type); purple_debug_info("bonjour", "bytestream offer Message type - SET.\n"); iq_id = purple_xmlnode_get_attrib(packet, "id"); sid = purple_xmlnode_get_attrib(query, "sid"); xfer = bonjour_si_xfer_find(bd, sid, from); streamhost = purple_xmlnode_get_child(query, "streamhost"); if(xfer && streamhost && __xep_bytestreams_parse(pb, xfer, streamhost, iq_id)) purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n"); if (iq_id && xfer != NULL) xep_ft_si_reject(bd, iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from, const goffset filesize, const char *filename, int option) if(pc == NULL || id == NULL || from == NULL) bd = purple_connection_get_protocol_data(pc); purple_debug_info("bonjour", "bonjour-xfer-receive.\n"); /* Build the file transfer handle */ "account", purple_connection_get_account(pc), "type", PURPLE_XFER_TYPE_RECEIVE, purple_xfer_set_filename(xfer, filename); xf->iq_id = g_strdup(id); purple_xfer_set_size(xfer, filesize); bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer); purple_xfer_request(xfer); bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond) XepXfer *xf = XEP_XFER(xfer); purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf->sock5_req_state); switch(xf->sock5_req_state){ acceptfd = accept(source, NULL, 0); if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { } else if(acceptfd == -1) { /* This should cancel the ft */ purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno); purple_xfer_cancel_remote(xfer); purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd); _purple_network_set_common_socket_flags(acceptfd); purple_input_remove(purple_xfer_get_watcher(xfer)); purple_xfer_set_watcher(xfer, purple_input_add(acceptfd, PURPLE_INPUT_READ, bonjour_sock5_request_cb, xfer)); purple_xfer_set_fd(xfer, source); len = read(source, xf->rx_buf + xf->rxlen, 3); if(len < 0 && errno == EAGAIN) purple_xfer_cancel_remote(xfer); purple_input_remove(purple_xfer_get_watcher(xfer)); purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE, bonjour_sock5_request_cb, xfer)); bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE); len = write(source, xf->tx_buf, 2); if (len < 0 && errno == EAGAIN) purple_xfer_cancel_remote(xfer); purple_input_remove(purple_xfer_get_watcher(xfer)); purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_READ, bonjour_sock5_request_cb, xfer)); len = read(source, xf->rx_buf + xf->rxlen, 20); purple_input_remove(purple_xfer_get_watcher(xfer)); purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE, bonjour_sock5_request_cb, xfer)); bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE); xf->tx_buf[4] = strlen(xf->buddy_ip); memcpy(xf->tx_buf + 5, xf->buddy_ip, strlen(xf->buddy_ip)); xf->tx_buf[5+strlen(xf->buddy_ip)] = 0x00; xf->tx_buf[6+strlen(xf->buddy_ip)] = 0x00; len = write(source, xf->tx_buf, 7 + strlen(xf->buddy_ip)); if (len < 0 && errno == EAGAIN) { purple_xfer_cancel_remote(xfer); purple_input_remove(purple_xfer_get_watcher(xfer)); purple_xfer_set_watcher(xfer, 0); purple_xfer_start(xfer, source, NULL, -1); bonjour_bytestreams_listen(int sock, gpointer data) PurpleXmlNode *query, *streamhost; purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock); if (sock < 0 || xfer == NULL) { /*purple_xfer_cancel_local(xfer);*/ purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ, bonjour_sock5_request_cb, xfer)); iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->xmpp_data->account), xf->sid); query = purple_xmlnode_new_child(iq->node, "query"); purple_xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams"); purple_xmlnode_set_attrib(query, "sid", xf->sid); purple_xmlnode_set_attrib(query, "mode", "tcp"); purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock)); local_ips = bonjour_xmpp_get_local_ips(sock); port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer)); streamhost = purple_xmlnode_new_child(query, "streamhost"); purple_xmlnode_set_attrib(streamhost, "jid", xf->sid); purple_xmlnode_set_attrib(streamhost, "host", local_ips->data); purple_xmlnode_set_attrib(streamhost, "port", port); local_ips = g_slist_delete_link(local_ips, local_ips); xep_iq_send_and_free(iq); bonjour_bytestreams_init(PurpleXfer *xfer) purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n"); xf->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, FALSE, bonjour_bytestreams_listen, xfer); if (xf->listen_data == NULL) purple_xfer_cancel_local(xfer); bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message) XepXfer *xf = XEP_XFER(xfer); PurpleXmlNode *q_node, *tmp_node; xf->proxy_connection = NULL; purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n", xf->proxy_host, error_message ? error_message : "(null)"); tmp_node = purple_xmlnode_get_next_twin(xf->streamhost); ret = __xep_bytestreams_parse(xf->pb, xfer, tmp_node, xf->iq_id); xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); /* Cancel the connection */ purple_xfer_cancel_local(xfer); purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n"); /* Here, start the file transfer.*/ /* Notify Initiator of Connection */ iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->xmpp_data->account), xf->iq_id); q_node = purple_xmlnode_new_child(iq->node, "query"); purple_xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams"); tmp_node = purple_xmlnode_new_child(q_node, "streamhost-used"); purple_xmlnode_set_attrib(tmp_node, "jid", xf->jid); xep_iq_send_and_free(iq); purple_xfer_start(xfer, source, NULL, -1); bonjour_bytestreams_connect(PurpleXfer *xfer) PurpleAccount *account = NULL; const gchar *name = NULL; unsigned char hashval[20]; purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n"); name = purple_buddy_get_name(pb); account = purple_buddy_get_account(pb); p = g_strdup_printf("%s%s%s", xf->sid, name, bonjour_get_jid(account)); hash = g_checksum_new(G_CHECKSUM_SHA1); g_checksum_update(hash, (guchar *)p, -1); g_checksum_get_digest(hash, hashval, &digest_len); for (i = 0; i < 20; i++, p += 2) { g_snprintf(p, 3, "%02x", hashval[i]); xf->proxy_info = purple_proxy_info_new(); purple_proxy_info_set_proxy_type(xf->proxy_info, PURPLE_PROXY_SOCKS5); purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host); purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port); xf->proxy_connection = purple_proxy_connect_socks5_account( purple_account_get_connection(account), bonjour_bytestreams_connect_cb, xfer); if(xf->proxy_connection == NULL) { xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); /* Cancel the connection */ purple_xfer_cancel_local(xfer); xep_xfer_init(XepXfer *xfer) { xep_xfer_finalize(GObject *obj) { XepXfer *xf = XEP_XFER(obj); BonjourData *bd = (BonjourData*)xf->data; bd->xfer_lists = g_slist_remove(bd->xfer_lists, PURPLE_XFER(xf)); purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists); if (xf->proxy_connection != NULL) { purple_proxy_connect_cancel(xf->proxy_connection); if (xf->proxy_info != NULL) { purple_proxy_info_destroy(xf->proxy_info); if (xf->listen_data != NULL) { purple_network_listen_cancel(xf->listen_data); g_clear_pointer(&xf->streamhost, purple_xmlnode_free_tree); G_OBJECT_CLASS(xep_xfer_parent_class)->finalize(obj); xep_xfer_class_finalize(XepXferClass *klass) { xep_xfer_class_init(XepXferClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass); obj_class->finalize = xep_xfer_finalize; xfer_class->init = bonjour_xfer_init; xfer_class->request_denied = bonjour_xfer_request_denied; xfer_class->cancel_recv = bonjour_xfer_cancel_recv; xfer_class->cancel_send = bonjour_xfer_cancel_send; xfer_class->end = bonjour_xfer_end; xep_xfer_register(GTypeModule *module) { xep_xfer_register_type(module);