# HG changeset patch # User Daniel Atallah # Date 1195138433 0 # Node ID dc703f13449a423b2867dd0dc572fd44cdf555cb # Parent 287ee978db2e6d12cf368c407e63216efab7716c Fix a couple bugs in the Bonjour XEP-0065 implementation, mainly related to error handling, but also send a result. Also fix a XEP-0096 bug where the SI profile wasn't being specified. These bring ft with gajim closer to working, but we aren't there yet. diff -r 287ee978db2e -r dc703f13449a libpurple/protocols/bonjour/bonjour.c --- a/libpurple/protocols/bonjour/bonjour.c Thu Nov 15 14:22:06 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Thu Nov 15 14:53:53 2007 +0000 @@ -193,6 +193,11 @@ if (bonjour_group != NULL) purple_blist_remove_group(bonjour_group); + /* Cancel any file transfers */ + while (bd != NULL && bd->xfer_lists) { + purple_xfer_cancel_local(bd->xfer_lists->data); + } + g_free(bd); connection->proto_data = NULL; } diff -r 287ee978db2e -r dc703f13449a libpurple/protocols/bonjour/bonjour_ft.c --- a/libpurple/protocols/bonjour/bonjour_ft.c Thu Nov 15 14:22:06 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Thu Nov 15 14:53:53 2007 +0000 @@ -45,34 +45,39 @@ static unsigned int next_id = 0; static void -xep_ft_si_reject(PurpleXfer *xfer, char *to) +xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type) { xmlnode *error_node = NULL; xmlnode *tmp_node = NULL; XepIq *iq = NULL; - XepXfer *xf = NULL; - if(!to || !xfer) - return; - xf = xfer->data; - if(!xf) + g_return_if_fail(error_code != NULL); + g_return_if_fail(error_type != NULL); + + if(!to || !id) return; purple_debug_info("bonjour", "xep file transfer stream initialization error.\n"); - iq = xep_iq_new(xf->data, XEP_IQ_ERROR, to, xf->sid); + iq = xep_iq_new(bd, XEP_IQ_ERROR, to, purple_account_get_username(bd->jabber_data->account), id); if(iq == NULL) return; error_node = xmlnode_new_child(iq->node, "error"); - xmlnode_set_attrib(error_node, "code", "403"); - xmlnode_set_attrib(error_node, "type", "cancel"); + xmlnode_set_attrib(error_node, "code", error_code); + xmlnode_set_attrib(error_node, "type", error_type); + + /* TODO: Make this better */ + if (!strcmp(error_code, "403")) { + tmp_node = xmlnode_new_child(error_node, "forbidden"); + xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); - tmp_node = xmlnode_new_child(error_node, "forbidden"); - xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); - - tmp_node = xmlnode_new_child(error_node, "text"); - xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); - xmlnode_insert_data(tmp_node, "Offer Declined", -1); + tmp_node = xmlnode_new_child(error_node, "text"); + xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); + xmlnode_insert_data(tmp_node, "Offer Declined", -1); + } else if (!strcmp(error_code, "404")) { + tmp_node = xmlnode_new_child(error_node, "item-not-found"); + xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); + } xep_iq_send_and_free(iq); } @@ -85,8 +90,14 @@ static void bonjour_xfer_request_denied(PurpleXfer *xfer) { + XepXfer *xf = NULL; + purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n"); - xep_ft_si_reject(xfer, xfer->who); + + xf = xfer->data; + if(xf) + xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel"); + bonjour_free_xfer(xfer); } @@ -193,7 +204,7 @@ /* Assign stream id. */ memset(buf, 0, 32); g_snprintf(buf, sizeof(buf), "%u", next_id++); - iq = xep_iq_new(xf->data, XEP_IQ_SET, to, buf); + iq = xep_iq_new(xf->data, XEP_IQ_SET, to, purple_account_get_username(bd->jabber_data->account), buf); if(iq == NULL) return; @@ -202,6 +213,7 @@ /*Construct Stream initialization offer message.*/ si_node = xmlnode_new_child(iq->node, "si"); xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); + xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer"); file = xmlnode_new_child(si_node, "file"); xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer"); @@ -236,37 +248,6 @@ } static void -xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid) -{ - xmlnode *error_node = NULL; - xmlnode *tmp_node = NULL; - XepIq *iq = NULL; - - g_return_if_fail(bd != NULL); - - if(!to || !sid) - return; - - purple_debug_info("bonjour", "xep file transfer stream initialization error.\n"); - iq = xep_iq_new(bd, XEP_IQ_ERROR, to, sid); - if(iq == NULL) - return; - - error_node = xmlnode_new_child(iq->node, "error"); - xmlnode_set_attrib(error_node, "code", "403"); - xmlnode_set_attrib(error_node, "type", "cancel"); - - tmp_node = xmlnode_new_child(error_node, "forbidden"); - xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); - - tmp_node = xmlnode_new_child(error_node, "text"); - xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); - xmlnode_insert_data(tmp_node, "Offer Declined", -1); - - xep_iq_send_and_free(iq); -} - -static void xep_ft_si_result(PurpleXfer *xfer, char *to) { xmlnode *si_node = NULL; @@ -276,6 +257,7 @@ xmlnode *x = NULL; XepIq *iq = NULL; XepXfer *xf = NULL; + BonjourData *bd; if(!to || !xfer) return; @@ -283,13 +265,16 @@ if(!xf) return; + bd = xf->data; + purple_debug_info("bonjour", "xep file transfer stream initialization result.\n"); - iq = xep_iq_new(xf->data, XEP_IQ_RESULT, to, xf->sid); + iq = xep_iq_new(bd, XEP_IQ_RESULT, to, purple_account_get_username(bd->jabber_data->account), xf->sid); if(iq == NULL) return; si_node = xmlnode_new_child(iq->node, "si"); xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); + /*xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/ feature = xmlnode_new_child(si_node, "feature"); xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); @@ -331,6 +316,7 @@ purple_proxy_connect_cancel(xf->proxy_connection); if (xf->listen_data != NULL) purple_network_listen_cancel(xf->listen_data); + g_free(xf->iq_id); g_free(xf->jid); g_free(xf->proxy_host); g_free(xf->buddy_ip); @@ -364,7 +350,9 @@ purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data); - xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB; + /* We don't support IBB yet */ + /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/ + xep_xfer->mode = XEP_BYTESTREAMS; xep_xfer->sid = NULL; purple_xfer_set_init_fnc(xfer, bonjour_xfer_init); @@ -379,14 +367,15 @@ void bonjour_send_file(PurpleConnection *gc, const char *who, const char *file) { + PurpleXfer *xfer = NULL; - PurpleXfer *xfer = NULL; - if(gc == NULL || who == NULL) - return; + 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(gc, who); - if(xfer == NULL) - return; + if (file) purple_xfer_request_accepted(xfer, file); else @@ -401,11 +390,10 @@ BonjourBuddy *bd = NULL; XepXfer *xf = NULL; - if(xfer == NULL) - return; xf = (XepXfer*)xfer->data; if(xf == NULL) return; + purple_debug_info("bonjour", "Bonjour-xfer-init.\n"); buddy = purple_find_buddy(xfer->account, xfer->who); @@ -419,25 +407,19 @@ /* initiate file transfer, send SI offer. */ purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n"); xep_ft_si_offer(xfer, xfer->who); - } else { /* accept file transfer request, send SI result. */ xep_ft_si_result(xfer, xfer->who); purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n"); } - return; } - void xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb) { - const char *type = NULL, *from = NULL, *id = NULL; - xmlnode *si = NULL, *file = NULL; + const char *type, *id; BonjourData *bd = NULL; PurpleXfer *xfer = NULL; - const char *filename = NULL, *filesize_str = NULL; - int filesize = 0, option = XEP_BYTESTREAMS; if(pc == NULL || packet == NULL || pb == NULL) return; @@ -448,51 +430,73 @@ purple_debug_info("bonjour", "xep-si-parse.\n"); type = xmlnode_get_attrib(packet, "type"); - from = pb->name; id = xmlnode_get_attrib(packet, "id"); if(type) { - if(!strcmp(type, "set")){ - si = xmlnode_get_child(packet,"si"); + if(!strcmp(type, "set")) { + const char *profile; + xmlnode *si; + gboolean parsed_receive = FALSE; + + si = xmlnode_get_child(packet, "si"); + purple_debug_info("bonjour", "si offer Message type - SET.\n"); - file = xmlnode_get_child(si, "file"); - /**/ - filename = xmlnode_get_attrib(file, "name"); - if((filesize_str = xmlnode_get_attrib(file, "size"))) - filesize = atoi(filesize_str); - bonjour_xfer_receive(pc, id, from, filesize, filename, option); - } else if(!strcmp(type, "result")){ - si = xmlnode_get_child(packet,"si"); + if (si && (profile = xmlnode_get_attrib(si, "profile")) + && !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) { + const char *filename = NULL, *filesize_str = NULL; + int filesize = 0; + xmlnode *file; + + if ((file = xmlnode_get_child(si, "file"))) { + filename = xmlnode_get_attrib(file, "name"); + if((filesize_str = xmlnode_get_attrib(file, "size"))) + filesize = atoi(filesize_str); + } + + /* TODO: Make sure that it is advertising a bytestreams transfer */ + + bonjour_xfer_receive(pc, id, pb->name, filesize, filename, XEP_BYTESTREAMS); + + parsed_receive = TRUE; + } + + if (!parsed_receive) { + purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n"); + xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel"); + /*TODO: Send Cancel (501) */ + } + } else if(!strcmp(type, "result")) { purple_debug_info("bonjour", "si offer Message type - RESULT.\n"); - xfer = bonjour_si_xfer_find(bd, id, from); - if(xfer == NULL){ + + xfer = bonjour_si_xfer_find(bd, id, pb->name); + + if(xfer == NULL) { purple_debug_info("bonjour", "xfer find fail.\n"); - xep_ft_si_reject2((BonjourData *)pc->proto_data, from, id); - } else { + xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel"); + } else bonjour_bytestreams_init(xfer); - } - } else if(!strcmp(type, "error")){ + + } else if(!strcmp(type, "error")) { purple_debug_info("bonjour", "si offer Message type - ERROR.\n"); - xfer = bonjour_si_xfer_find(bd, id, from); - if(xfer == NULL){ + + xfer = bonjour_si_xfer_find(bd, id, pb->name); + + if(xfer == NULL) purple_debug_info("bonjour", "xfer find fail.\n"); - } else { + else purple_xfer_cancel_remote(xfer); - } - } else { + } else purple_debug_info("bonjour", "si offer Message type - Unknown-%d.\n", type); - } } } void xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb) { - const char *type = NULL, *from = NULL, *id = NULL; + const char *type = NULL, *from = NULL; xmlnode *query = NULL, *streamhost = NULL; BonjourData *bd = NULL; PurpleXfer *xfer = NULL; XepXfer *xf = NULL; - const char *jid=NULL, *host=NULL, *port=NULL; int portnum; if(pc == NULL || packet == NULL || pb == NULL) @@ -508,13 +512,20 @@ from = pb->name; query = xmlnode_get_child(packet,"query"); if(type) { - if(!strcmp(type, "set")){ + if(!strcmp(type, "set")) { + const char *iq_id, *sid; + gboolean found = FALSE; + purple_debug_info("bonjour", "bytestream offer Message type - SET.\n"); - id = xmlnode_get_attrib(query, "sid"); - xfer = bonjour_si_xfer_find(bd, id, from); + iq_id = xmlnode_get_attrib(packet, "id"); - if(xfer){ + sid = xmlnode_get_attrib(query, "sid"); + xfer = bonjour_si_xfer_find(bd, sid, from); + + if(xfer) { + const char *jid, *host, *port; + xf = (XepXfer*)xfer->data; for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost; @@ -526,15 +537,17 @@ (portnum = atoi(port))) { if(!strcmp(host, xf->buddy_ip)) { + g_free(xf->iq_id); + xf->iq_id = g_strdup(iq_id); xf->jid = g_strdup(jid); xf->proxy_host = g_strdup(host); xf->proxy_port = portnum; purple_debug_info("bonjour", "bytestream offer parse" "jid=%s host=%s port=%d.\n", jid, host, portnum); bonjour_bytestreams_connect(xfer); + found = TRUE; break; } - } else { purple_debug_info("bonjour", "bytestream offer Message parse error.\n"); } @@ -543,8 +556,15 @@ } + if (!found) { + purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n"); + + if (iq_id) + xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel"); + } + } else { - purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%d.\n", type); + purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type); } } } @@ -557,7 +577,7 @@ XepXfer *xf = NULL; BonjourData *bd = NULL; - if(pc == NULL || id == NULL || from == NULL || filename == NULL) + if(pc == NULL || id == NULL || from == NULL) return; bd = (BonjourData*) pc->proto_data; @@ -593,9 +613,6 @@ int acceptfd; int len = 0; - if(xfer == NULL) - return; - xf = xfer->data; if(xf == NULL) return; @@ -608,7 +625,7 @@ if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { } else if(acceptfd == -1) { - + /* TODO: This should cancel the ft */ } else { int flags; @@ -720,6 +737,7 @@ const char *next_ip; const char *local_ip = NULL; char token [] = ";"; + BonjourData *bd; purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock); if (sock < 0 || xfer == NULL) { @@ -732,7 +750,9 @@ xf = (XepXfer*)xfer->data; xf->listen_data = NULL; - iq = xep_iq_new(xf->data, XEP_IQ_SET, xfer->who, xf->sid); + bd = xf->data; + + iq = xep_iq_new(bd, XEP_IQ_SET, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->sid); query = xmlnode_new_child(iq->node, "query"); xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams"); @@ -745,15 +765,15 @@ /* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */ next_ip = strtok((char *)local_ip, token); + port = g_strdup_printf("%hu", xfer->local_port); while(next_ip != NULL) { streamhost = xmlnode_new_child(query, "streamhost"); xmlnode_set_attrib(streamhost, "jid", xf->sid); xmlnode_set_attrib(streamhost, "host", next_ip); - port = g_strdup_printf("%hu", xfer->local_port); xmlnode_set_attrib(streamhost, "port", port); - g_free(port); next_ip = strtok(NULL, token); } + g_free(port); xep_iq_send_and_free(iq); } @@ -781,14 +801,32 @@ { PurpleXfer *xfer = data; XepXfer *xf = xfer->data; + XepIq *iq = NULL; + xmlnode *q_node, *tmp_node; + BonjourData *bd; - if(data == NULL || source < 0) + if(data == NULL || source < 0) { + xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel"); + /* Cancel the connection */ + purple_xfer_cancel_local(xfer); return; + } + + bd = xf->data; purple_proxy_info_destroy(xf->proxy_info); xf->proxy_connection = NULL; xf->proxy_info = NULL; /* Here, start the file transfer.*/ + + /* Notify Initiator of Connection */ + iq = xep_iq_new(bd, XEP_IQ_RESULT, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->iq_id); + q_node = xmlnode_new_child(iq->node, "query"); + xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams"); + tmp_node = xmlnode_new_child(q_node, "streamhost-used"); + xmlnode_set_attrib(tmp_node, "jid", xf->jid); + xep_iq_send_and_free(iq); + purple_xfer_start(xfer, source, NULL, -1); } @@ -829,8 +867,11 @@ bonjour_bytestreams_connect_cb, xfer); if(xf->proxy_connection == NULL) { - purple_proxy_info_destroy(xf->proxy_info); - xf->proxy_info = NULL; + xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel"); + /* Cancel the connection */ + purple_xfer_cancel_local(xfer); + /*purple_proxy_info_destroy(xf->proxy_info); + xf->proxy_info = NULL;*/ } } diff -r 287ee978db2e -r dc703f13449a libpurple/protocols/bonjour/bonjour_ft.h --- a/libpurple/protocols/bonjour/bonjour_ft.h Thu Nov 15 14:22:06 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.h Thu Nov 15 14:53:53 2007 +0000 @@ -35,7 +35,7 @@ void *data; char *filename; int filesize; - int id; + char *iq_id; char *sid; char *recv_id; char *buddy_ip; diff -r 287ee978db2e -r dc703f13449a libpurple/protocols/bonjour/jabber.c --- a/libpurple/protocols/bonjour/jabber.c Thu Nov 15 14:22:06 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Thu Nov 15 14:53:53 2007 +0000 @@ -89,13 +89,14 @@ #endif static BonjourJabberConversation * -bonjour_jabber_conv_new() { +bonjour_jabber_conv_new(PurpleBuddy *pb) { BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1); bconv->socket = -1; bconv->tx_buf = purple_circ_buffer_new(512); bconv->tx_handler = 0; bconv->rx_handler = 0; + bconv->pb = pb; return bconv; } @@ -401,7 +402,6 @@ bonjour_jabber_close_conversation(bb->conversation); bb->conversation = NULL; } - } void bonjour_jabber_stream_started(PurpleBuddy *pb) { @@ -573,7 +573,7 @@ /* Check if the conversation has been previously started */ if (bb->conversation == NULL) { - bb->conversation = bonjour_jabber_conv_new(); + bb->conversation = bonjour_jabber_conv_new(pb); if (!bonjour_jabber_stream_init(pb, client_socket)) { close(client_socket); @@ -752,7 +752,7 @@ return NULL; } - bb->conversation = bonjour_jabber_conv_new(); + bb->conversation = bonjour_jabber_conv_new(pb); bb->conversation->connect_data = connect_data; /* We don't want _send_data() to register the tx_handler; * that neeeds to wait until we're actually connected. */ @@ -816,8 +816,25 @@ void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) { - if (bconv != NULL) - { + if (bconv != NULL) { + GList *xfers, *tmp_next; + BonjourData *bd = bconv->pb->account->gc->proto_data; + + /* Cancel any file transfers that are waiting to begin */ + xfers = bd->xfer_lists; + while(xfers != NULL) { + PurpleXfer *xfer = xfers->data; + tmp_next = xfers->next; + /* We only need to cancel this if it hasn't actually started transferring. */ + /* This will change if we ever support IBB transfers. */ + if (strcmp(xfer->who, bconv->pb->name) == 0 + && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED + || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) { + purple_xfer_cancel_remote(xfer); + } + xfers = tmp_next; + } + /* Close the socket and remove the watcher */ if (bconv->socket >= 0) { /* Send the end of the stream to the other end of the conversation */ @@ -874,7 +891,7 @@ } XepIq * -xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id) +xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id) { xmlnode *iq_node = NULL; XepIq *iq = NULL; @@ -886,6 +903,7 @@ iq_node = xmlnode_new("iq"); xmlnode_set_attrib(iq_node, "to", to); + xmlnode_set_attrib(iq_node, "from", from); xmlnode_set_attrib(iq_node, "id", id); switch (type) { case XEP_IQ_SET: @@ -911,6 +929,7 @@ iq->type = type; iq->data = ((BonjourData*)data)->jabber_data; iq->to = (char*)to; + return iq; } diff -r 287ee978db2e -r dc703f13449a libpurple/protocols/bonjour/jabber.h --- a/libpurple/protocols/bonjour/jabber.h Thu Nov 15 14:22:06 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Thu Nov 15 14:53:53 2007 +0000 @@ -53,6 +53,7 @@ gpointer stream_data; xmlParserCtxt *context; xmlnode *current; + PurpleBuddy *pb; } BonjourJabberConversation; /** @@ -63,7 +64,7 @@ */ gint bonjour_jabber_start(BonjourJabber *data); -int bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body); +int bonjour_jabber_send_message(BonjourJabber *data, const char *to, const char *body); void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv); @@ -91,7 +92,7 @@ void *data; } XepIq; -XepIq *xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id); +XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id); int xep_iq_send_and_free(XepIq *iq); const char *purple_network_get_my_ip_ext2(int fd);