Mercurial > pidgin.yaz
changeset 21462:31fc6a0769ce
merge of 'aa714a29059f790cb003a8fe1bec9c8873d09e33'
and 'd92a21a7dca2f2c4abb6371abea83d5ab4f28b10'
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Mon, 12 Nov 2007 23:10:08 +0000 |
parents | da75dd6c41fb (diff) a34204321321 (current diff) |
children | 2b6553584385 |
files | |
diffstat | 10 files changed, 1103 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Mon Nov 12 20:39:26 2007 +0000 +++ b/ChangeLog Mon Nov 12 23:10:08 2007 +0000 @@ -6,6 +6,11 @@ libpurple: * We now honor a PURPLE_DISABLE_DEPRECATED define to allow plugins to catch deprecated functions earlier rather than later. + * Thanks to a patch from Intel, the Bonjour prpl now supports file + transfers using XEP-0096 and XEP-0065. This should enable file + transfers between libpurple clients and Gaijm clients, but will + not work with iChat or Adium as they use a different file transfer + implementation. Pidgin: * If a plugin says it can't be unloaded, we now display an error and
--- a/Makefile.mingw Mon Nov 12 20:39:26 2007 +0000 +++ b/Makefile.mingw Mon Nov 12 23:10:08 2007 +0000 @@ -62,8 +62,8 @@ saslGSSAPI.dll \ saslLOGIN.dll \ saslPLAIN.dll \ - silc.dll \ - silcclient.dll \ + libsilc-1-1-2.dll \ + libsilcclient-1-1-2.dll \ smime3.dll \ softokn3.dll \ ssl3.dll
--- a/libpurple/protocols/bonjour/Makefile.am Mon Nov 12 20:39:26 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.am Mon Nov 12 23:10:08 2007 +0000 @@ -18,7 +18,9 @@ mdns_interface.h \ mdns_types.h \ parser.c \ - parser.h + parser.h \ + bonjour_ft.c \ + bonjour_ft.h if MDNS_AVAHI BONJOURSOURCES += mdns_avahi.c
--- a/libpurple/protocols/bonjour/Makefile.mingw Mon Nov 12 20:39:26 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.mingw Mon Nov 12 23:10:08 2007 +0000 @@ -43,12 +43,13 @@ ## SOURCES, OBJECTS ## C_SRC = bonjour.c \ + bonjour_ft.c \ buddy.c \ dns_sd_proxy.c \ + jabber.c \ mdns_common.c \ mdns_win32.c \ - parser.c \ - jabber.c + parser.c OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/bonjour/bonjour.c Mon Nov 12 20:39:26 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Mon Nov 12 23:10:08 2007 +0000 @@ -41,6 +41,7 @@ #include "mdns_common.h" #include "jabber.h" #include "buddy.h" +#include "bonjour_ft.h" /* * TODO: Should implement an add_buddy callback that removes the buddy @@ -453,8 +454,8 @@ NULL, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ + bonjour_send_file, /* send_file */ + bonjour_new_xfer, /* new_xfer */ NULL, /* offline_message */ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */
--- a/libpurple/protocols/bonjour/bonjour.h Mon Nov 12 20:39:26 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.h Mon Nov 12 23:10:08 2007 +0000 @@ -44,6 +44,7 @@ { BonjourDnsSd *dns_sd_data; BonjourJabber *jabber_data; + GList *xfer_lists; } BonjourData; #endif /* _BONJOUR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Mon Nov 12 23:10:08 2007 +0000 @@ -0,0 +1,777 @@ +/* + * 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 + * source distribution. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "internal.h" +#include "util.h" +#include "debug.h" +#include "notify.h" +#include "proxy.h" +#include "ft.h" +#include "buddy.h" +#include "bonjour.h" +#include "bonjour_ft.h" +#include "cipher.h" + +static PurpleXfer* +bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from); +static void +xep_ft_si_offer(PurpleXfer *xfer, const gchar *to); +static void +xep_ft_si_result(PurpleXfer *xfer, char *to); +static void +xep_ft_si_reject(PurpleXfer *xfer, char *to); +static void +xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid); +static void +bonjour_bytestreams_init(PurpleXfer *xfer); +static void +bonjour_bytestreams_listen(int sock, gpointer data); +static void +bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond); +static void +bonjour_bytestreams_connect(PurpleXfer *xfer); +static void +bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message); +static void +bonjour_xfer_init(PurpleXfer *xfer); +static void +bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *from, + const int filesize, const char *filename, int option); +static void bonjour_xfer_cancel_send(PurpleXfer *xfer); +static void bonjour_xfer_request_denied(PurpleXfer *xfer); +static void bonjour_xfer_cancel_recv(PurpleXfer *xfer); +static void bonjour_xfer_end(PurpleXfer *xfer); +static void bonjour_free_xfer(PurpleXfer *xfer); + +/* Look for specific xfer handle */ +static unsigned int next_id = 0; + +static void bonjour_xfer_cancel_send(PurpleXfer *xfer) +{ + purple_debug_info("Bonjour", "Bonjour-xfer-cancel-send.\n"); + bonjour_free_xfer(xfer); +} + +static void bonjour_xfer_request_denied(PurpleXfer *xfer) +{ + purple_debug_info("Bonjour", "Bonjour-xfer-request-denied.\n"); + xep_ft_si_reject(xfer, xfer->who); + bonjour_free_xfer(xfer); +} + + +static void bonjour_xfer_cancel_recv(PurpleXfer *xfer) +{ + purple_debug_info("Bonjour", "Bonjour-xfer-cancel-recv.\n"); + bonjour_free_xfer(xfer); +} + + +static void bonjour_xfer_end(PurpleXfer *xfer) +{ + purple_debug_info("Bonjour", "Bonjour-xfer-end.\n"); + bonjour_free_xfer(xfer); +} + +static PurpleXfer* +bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from) +{ + GList *xfers = NULL; + PurpleXfer *xfer = NULL; + XepXfer *xf = NULL; + + if(!sid || !from || !bd) + return NULL; + + purple_debug_info("Bonjour", "Look for sid=%s from=%s xferlists.\n", + sid, from); + + for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) { + xfer = xfers->data; + if(xfer == NULL) + break; + xf = xfer->data; + if(xf == NULL) + break; + if(xf->sid && xfer->who && !strcmp(xf->sid, sid) && + !strcmp(xfer->who, from)) + return xfer; + } + + purple_debug_info("Bonjour", "Look for xfer list fail\n"); + + return NULL; +} + +static void +xep_ft_si_offer(PurpleXfer *xfer, const gchar *to) +{ + xmlnode *si_node = NULL; + xmlnode *feature = NULL; + xmlnode *field = NULL; + xmlnode *option = NULL; + xmlnode *value = NULL; + xmlnode *file = NULL; + xmlnode *x = NULL; + XepIq *iq = NULL; + XepXfer *xf = NULL; + BonjourData *bd = NULL; + char buf[32]; + + if(!xfer || !to) + return; + xf = xfer->data; + if(!xf) + return; + bd = xf->data; + if(!bd) + return; + + purple_debug_info("Bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id); + + /* 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); + if(iq == NULL) + return; + + g_free(xf->sid); + xf->sid = g_strdup(buf); + /*Construct Stream initialization offer message.*/ + si_node = xmlnode_new_child(iq->node, "si"); + xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); + + file = xmlnode_new_child(si_node, "file"); + xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer"); + xmlnode_set_attrib(file, "name", xfer->filename); + memset(buf, 0, 32); + g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size); + xmlnode_set_attrib(file, "size", buf); + + feature = xmlnode_new_child(si_node, "feature"); + xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); + + x = xmlnode_new_child(feature, "x"); + xmlnode_set_namespace(x, "jabber:x:data"); + xmlnode_set_attrib(x, "type", "form"); + + field = xmlnode_new_child(x, "field"); + xmlnode_set_attrib(field, "var", "stream-method"); + xmlnode_set_attrib(field, "type", "list-single"); + + if (xf->mode & XEP_BYTESTREAMS) { + option = xmlnode_new_child(field, "option"); + value = xmlnode_new_child(option, "value"); + xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); + } + if (xf->mode & XEP_IBB) { + option = xmlnode_new_child(field, "option"); + value = xmlnode_new_child(option, "value"); + xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); + } + xep_iq_send(iq); +} + +static void +xep_ft_si_reject(PurpleXfer *xfer, char *to) +{ + xmlnode *error_node = NULL; + xmlnode *forbidden = NULL; + xmlnode *text = NULL; + XepIq *iq = NULL; + XepXfer *xf = NULL; + + if(!to || !xfer) + return; + xf = xfer->data; + if(!xf) + return; + + purple_debug_info("Bonjour", "xep file transfer stream initialization error.\n"); + iq = xep_iq_new(xf->data, XEP_IQ_ERROR, to, xf->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"); + + forbidden = xmlnode_new_child(error_node, "forbidden"); + xmlnode_set_namespace(forbidden, "urn:ietf:params:xml:ns:xmpp-stanzas"); + + text = xmlnode_new_child(error_node, "text"); + xmlnode_set_namespace(text, "urn:ietf:params:xml:ns:xmpp-stanzas"); + xmlnode_insert_data(text, "Offer Declined", -1); + + xep_iq_send(iq); +} +static void +xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid) +{ + xmlnode *error_node = NULL; + xmlnode *forbidden = NULL; + xmlnode *text = NULL; + XepIq *iq = NULL; + if(bd == NULL) + return; + 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"); + + forbidden = xmlnode_new_child(error_node, "forbidden"); + xmlnode_set_namespace(forbidden, "urn:ietf:params:xml:ns:xmpp-stanzas"); + + text = xmlnode_new_child(error_node, "text"); + xmlnode_set_namespace(text, "urn:ietf:params:xml:ns:xmpp-stanzas"); + xmlnode_insert_data(text, "Offer Declined", -1); + + xep_iq_send(iq); +} + +static void +xep_ft_si_result(PurpleXfer *xfer, char *to) +{ + xmlnode *si_node = NULL; + xmlnode *feature = NULL; + xmlnode *field = NULL; + xmlnode *value = NULL; + xmlnode *x = NULL; + XepIq *iq = NULL; + XepXfer *xf = NULL; + + if(!to || !xfer) + return; + xf = xfer->data; + if(!xf) + return; + + purple_debug_info("Bonjour", "xep file transfer stream initialization result.\n"); + iq = xep_iq_new(xf->data, XEP_IQ_RESULT, to, xf->sid); + if(iq == NULL) + return; + + si_node = xmlnode_new_child(iq->node, "si"); + xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); + + feature = xmlnode_new_child(si_node, "feature"); + xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); + + x = xmlnode_new_child(feature, "x"); + xmlnode_set_namespace(x, "jabber:x:data"); + xmlnode_set_attrib(x, "type", "submit"); + + field = xmlnode_new_child(x, "field"); + xmlnode_set_attrib(field, "var", "stream-method"); + + value = xmlnode_new_child(field, "value"); + xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); + + xep_iq_send(iq); +} + +static void +bonjour_free_xfer(PurpleXfer *xfer) +{ + XepXfer *xf = NULL; + BonjourData *bd = NULL; + if(xfer == NULL) { + purple_debug_info("Bonjour", "bonjour-free-xfer-null.\n"); + return; + } + purple_debug_info("Bonjour", "bonjour-free-xfer-%p.\n", xfer); + xf = (XepXfer*)xfer->data; + if(xf != NULL){ + bd = (BonjourData*)xf->data; + if(bd != NULL){ + bd->xfer_lists = g_list_remove(bd->xfer_lists, xfer); + purple_debug_info("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->listen_data != NULL) + purple_network_listen_cancel(xf->listen_data); + g_free(xf->jid); + g_free(xf->proxy_host); + g_free(xf->buddy_ip); + g_free(xf->sid); + g_free(xf); + xfer->data = NULL; + } + purple_debug_info("Bonjour", "Need close socket=%d.\n", xfer->fd); +} + +PurpleXfer * +bonjour_new_xfer(PurpleConnection *gc, const char *who) +{ + PurpleXfer *xfer = NULL; + XepXfer *xep_xfer = NULL; + BonjourData *bd = NULL; + if(who == NULL || gc == NULL) + return xfer; + + purple_debug_info("Bonjour", "Bonjour-new-xfer to %s.\n", who); + bd = (BonjourData*) gc->proto_data; + if(bd == NULL) + return xfer; + /* Build the file transfer handle */ + xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who); + + if (xfer) { + xep_xfer = g_new0(XepXfer, 1); + if(xep_xfer == NULL){ + bonjour_free_xfer(xfer); + return NULL; + } + xep_xfer->data = bd; + purple_debug_info("Bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data); + xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB; + xfer->data = xep_xfer; + xep_xfer->sid = NULL; + purple_xfer_set_init_fnc(xfer, bonjour_xfer_init); + purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send); + purple_xfer_set_end_fnc(xfer, bonjour_xfer_end); + bd->xfer_lists = g_list_append(bd->xfer_lists, xfer); + } + return xfer; +} + +void +bonjour_send_file(PurpleConnection *gc, const char *who, const char *file) +{ + + PurpleXfer *xfer = NULL; + if(gc == NULL || who == NULL) + return; + 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 + purple_xfer_request(xfer); + +} + +static void +bonjour_xfer_init(PurpleXfer *xfer) +{ + PurpleBuddy *buddy = NULL; + BonjourBuddy *bd = NULL; + XepXfer *xf = NULL; + char buffer[100]; + 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); + /* this buddy is offline. */ + if (buddy == NULL) + return; + + bd = (BonjourBuddy *)buddy->proto_data; + memcpy(buffer, bd->ip, 20); + xf->buddy_ip = g_strdup(bd->ip); + if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) { + /* 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; + 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; + bd = (BonjourData*) pc->proto_data; + if(bd == NULL) + return; + 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"); + 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")) != NULL ) + filesize = atoi(filesize_str); + bonjour_xfer_receive(pc, id, from, filesize, filename, option); + } else if(!strcmp(type, "result")){ + si = xmlnode_get_child(packet,"si"); + purple_debug_info("Bonjour", "si offer Message type - RESULT.\n"); + xfer = bonjour_si_xfer_find(bd, id, from); + if(xfer == NULL){ + purple_debug_info("Bonjour", "xfer find fail.\n"); + xep_ft_si_reject2((BonjourData *)pc->proto_data, from, id); + } else { + bonjour_bytestreams_init(xfer); + } + } 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){ + purple_debug_info("Bonjour", "xfer find fail.\n"); + } else { + purple_xfer_cancel_remote(xfer); + } + } 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; + 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) + return; + bd = (BonjourData*) pc->proto_data; + if(bd == NULL) + return; + purple_debug_info("Bonjour", "xep-bytestreams-parse.\n"); + type = xmlnode_get_attrib(packet, "type"); + from = pb->name; + query = xmlnode_get_child(packet,"query"); + if(type) { + if(!strcmp(type, "set")){ + purple_debug_info("Bonjour", "bytestream offer Message type - SET.\n"); + id = xmlnode_get_attrib(query, "sid"); + xfer = bonjour_si_xfer_find(bd, id, from); + if(xfer){ + xf = (XepXfer*)xfer->data; + for(streamhost = xmlnode_get_child(query, "streamhost"); + streamhost; + streamhost = xmlnode_get_next_twin(streamhost)) { + if((jid = xmlnode_get_attrib(streamhost, "jid")) && + (host = xmlnode_get_attrib(streamhost, "host")) && + (port = xmlnode_get_attrib(streamhost, "port")) && + (portnum = atoi(port))) { + if(!strcmp(host, xf->buddy_ip)) { + 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); + } + } else { + purple_debug_info("Bonjour", "bytestream offer Message parse error.\n"); + } + } + } else { + + } + + } else { + purple_debug_info("Bonjour", "bytestream offer Message type - Unknown-%d.\n", type); + } + } +} + +static void +bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *from, + const int filesize, const char *filename, int option) +{ + PurpleXfer *xfer = NULL; + XepXfer *xf = NULL; + BonjourData *bd = NULL; + if(pc == NULL || id == NULL || from == NULL || filename == NULL) + return; + bd = (BonjourData*) pc->proto_data; + if(bd == NULL) + return; + purple_debug_info("Bonjour", "bonjour-xfer-receive.\n"); + /* Build the file transfer handle */ + xfer = purple_xfer_new(pc->account, PURPLE_XFER_RECEIVE, from); + if (xfer) { + xf = g_new0(XepXfer, 1); + xf->data = bd; + xfer->data = xf; + purple_xfer_set_filename(xfer, filename); + xf->sid = g_strdup(id); + if(filesize > 0) + purple_xfer_set_size(xfer, filesize); + purple_xfer_set_init_fnc(xfer, bonjour_xfer_init); + purple_xfer_set_request_denied_fnc(xfer, bonjour_xfer_request_denied); + purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv); + purple_xfer_set_end_fnc(xfer, bonjour_xfer_end); + bd->xfer_lists = g_list_append(bd->xfer_lists, xfer); + purple_xfer_request(xfer); + } +} + +static void +bonjour_bytestreams_init(PurpleXfer *xfer) +{ + XepXfer *xf = NULL; + if(xfer == NULL) + return; + purple_debug_info("Bonjour", "Bonjour-bytestreams-init.\n"); + xf = xfer->data; + xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM, + bonjour_bytestreams_listen, xfer); + if (xf->listen_data == NULL) { + purple_xfer_cancel_local(xfer); + } + return; +} + +static void +bonjour_bytestreams_listen(int sock, gpointer data) +{ + PurpleXfer *xfer = NULL; + XepXfer *xf; + XepIq *iq; + xmlnode *query, *streamhost; + char *port; + char *next_ip = NULL; + char *local_ip = NULL; + char token [] = ";"; + purple_debug_info("Bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock); + if (sock < 0 || data == NULL) { + /*purple_xfer_cancel_local(xfer);*/ + return; + } + xfer = data; + xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ, + bonjour_sock5_request_cb, xfer); + xf = (XepXfer*)xfer->data; + xf->listen_data = NULL; + /*xf->listen_data = NULL;*/ + iq = xep_iq_new(xf->data, XEP_IQ_SET, xfer->who, xf->sid); + query = xmlnode_new_child(iq->node, "query"); + xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams"); + xmlnode_set_attrib(query, "sid", xf->sid); + xmlnode_set_attrib(query, "mode", "tcp"); + local_ip = purple_network_get_my_ip_ext2(sock); + next_ip = strtok(local_ip, token); + xfer->local_port = purple_network_get_port_from_fd(sock); + 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); + } + xep_iq_send(iq); +} + +static void +bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleXfer *xfer = data; + XepXfer *xf = NULL; + int acceptfd; + int len = 0; + if(xfer == NULL ) + return; + xf = xfer->data; + if(xf == NULL) + return; + switch(xf->sock5_req_state){ + case 0x00: + acceptfd = accept (source, NULL, 0); + if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK )) { + + } else if(acceptfd == -1) { + + } else { + purple_debug_info("Bonjour", "Conjour-sock5-request-cb. state= %d, accept=%d\n", xf->sock5_req_state, acceptfd); + purple_input_remove(xfer->watcher); + close(source); + xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ, + bonjour_sock5_request_cb, xfer); + xf->sock5_req_state++; + xf->rxlen = 0; + } + break; + case 0x01: + xfer->fd = source; + len = read(source, xf->rx_buf + xf->rxlen, 3); + if(len < 0 && errno == EAGAIN) + return; + else if(len <= 0){ + purple_input_remove(xfer->watcher); + xfer->watcher = 0; + close(source); + purple_xfer_cancel_remote(xfer); + return; + } else { + purple_input_remove(xfer->watcher); + xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE, + bonjour_sock5_request_cb, xfer); + xf->sock5_req_state++; + xf->rxlen = 0; + bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE); + } + break; + case 0x02: + xf->tx_buf[0] = 0x05; + xf->tx_buf[1] = 0x00; + len = write(source, xf->tx_buf, 2); + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + purple_input_remove(xfer->watcher); + xfer->watcher = 0; + close(source); + purple_xfer_cancel_remote(xfer); + return; + } else { + purple_input_remove(xfer->watcher); + xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ, + bonjour_sock5_request_cb, xfer); + xf->sock5_req_state++; + xf->rxlen = 0; + } + break; + case 0x03: + len = read(source, xf->rx_buf + xf->rxlen, 20); + if(len<=0){ + } else { + purple_input_remove(xfer->watcher); + xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE, + bonjour_sock5_request_cb, xfer); + xf->sock5_req_state++; + xf->rxlen = 0; + bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE); + } + break; + case 0x04: + xf->tx_buf[0] = 0x05; + xf->tx_buf[1] = 0x00; + xf->tx_buf[2] = 0x00; + xf->tx_buf[3] = 0x03; + 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) { + return; + } else if (len < 0) { + purple_input_remove(xfer->watcher); + xfer->watcher = 0; + close(source); + purple_xfer_cancel_remote(xfer); + return; + } else { + purple_input_remove(xfer->watcher); + xfer->watcher = 0; + xf->rxlen = 0; + /*close(source);*/ + purple_xfer_start(xfer, source, NULL, -1); + } + break; + default: + break; + } + return; +} +static void +bonjour_bytestreams_connect(PurpleXfer *xfer) +{ + XepXfer *xf = NULL; + char dstaddr[41]; + unsigned char hashval[20]; + char *p = NULL; + int i; + if(xfer == NULL) + return; + purple_debug_info("Bonjour", "bonjour-bytestreams-connect.\n"); + xf = (XepXfer*)xfer->data; + if(!xf) + return; + xf->proxy_info = purple_proxy_info_new(); + if(xf->proxy_info == NULL) + return; + p = g_strdup_printf("%s@%s", xf->sid, xfer->who); + purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), + sizeof(hashval), hashval, NULL); + g_free(p); + memset(dstaddr, 0, 41); + p = dstaddr; + for(i=0; i<20; i++) { + snprintf(p, 3, "%02x", hashval[i]); + } + purple_proxy_info_set_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(NULL, xf->proxy_info, + dstaddr, 0, + bonjour_bytestreams_connect_cb, xfer); + if(xf->proxy_connection == NULL) { + purple_proxy_info_destroy(xf->proxy_info); + xf->proxy_info = NULL; + } +} + +static void +bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message) +{ + PurpleXfer *xfer = data; + XepXfer *xf = xfer->data; + if(data == NULL || source < 0) + return; + purple_proxy_info_destroy(xf->proxy_info); + xf->proxy_connection = NULL; + xf->proxy_info = NULL; + /* Here, start the file transfer.*/ + purple_xfer_start(xfer, source, NULL, -1); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.h Mon Nov 12 23:10:08 2007 +0000 @@ -0,0 +1,75 @@ +/* + * 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 + * source distribution. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _BONJOUR_FT_H_ +#define _BONJOUR_FT_H_ +#include "network.h" +#include "proxy.h" +typedef struct _XepXfer XepXfer; +typedef enum { + XEP_BYTESTREAMS = 1, + XEP_IBB = 2, + XEP_UNKNOWN = 4, +} XepSiMode; + +struct _XepXfer +{ + void *data; + char *filename; + int filesize; + int id; + char *sid; + char *recv_id; + char *buddy_ip; + int mode; + PurpleNetworkListenData *listen_data; + int sock5_req_state; + int rxlen; + char rx_buf[0x500]; + char tx_buf[0x500]; + PurpleProxyInfo *proxy_info; + PurpleProxyConnectData *proxy_connection; + char *jid; + char *proxy_host; + int proxy_port; +}; + +/** + * Create a new PurpleXfer + * + * @param gc The PurpleConnection handle. + * @param who Who will we be sending it to? + */ +PurpleXfer *bonjour_new_xfer(PurpleConnection *gc, const char *who); + +/** + * Send a file. + * + * @param gc The PurpleConnection handle. + * @param who Who are we sending it to? + * @param file What file? If NULL, user will choose after this call. + */ +void bonjour_send_file(PurpleConnection *gc, const char *who, const char *file); + +void xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb); +void +xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb); +#endif
--- a/libpurple/protocols/bonjour/jabber.c Mon Nov 12 20:39:26 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Mon Nov 12 23:10:08 2007 +0000 @@ -20,6 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #ifndef _WIN32 +#include <net/if.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -45,12 +47,22 @@ #include "parser.h" #include "bonjour.h" #include "buddy.h" +#include "bonjour_ft.h" + +#ifdef _SIZEOF_ADDR_IFREQ +# define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a) +#else +# define HX_SIZE_OF_IFREQ(a) sizeof(a) +#endif #define STREAM_END "</stream:stream>" /* TODO: specify version='1.0' and send stream features */ #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \ "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">" +static void +xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb); + #if 0 /* this isn't used anywhere... */ static const char * _font_size_purple_to_ichat(int size) @@ -316,6 +328,8 @@ void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) { if (!strcmp(packet->name, "message")) _jabber_parse_and_write_message_to_ui(packet, pb); + else if(!strcmp(packet->name, "iq")) + xep_iq_parse(packet, NULL, pb); else purple_debug_warning("bonjour", "Unknown packet: %s\n", packet->name); @@ -697,23 +711,21 @@ } } -int -bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body) +static PurpleBuddy * +_find_or_start_conversation(BonjourJabber *data, const gchar *to) { - xmlnode *message_node, *node, *node2; - gchar *message; - PurpleBuddy *pb; - BonjourBuddy *bb; - int ret; + PurpleBuddy *pb = NULL; + BonjourBuddy *bb = NULL; + + g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(to != NULL, NULL); pb = purple_find_buddy(data->account, to); - if (pb == NULL) { - purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to); + if (pb == NULL || pb->proto_data == NULL) /* You can not send a message to an offline buddy */ - return -10000; - } + return NULL; - bb = pb->proto_data; + bb = (BonjourBuddy *) pb->proto_data; /* Check if there is a previously open conversation */ if (bb->conversation == NULL) @@ -721,6 +733,8 @@ PurpleProxyConnectData *connect_data; PurpleProxyInfo *proxy_info; + purple_debug_info("Bonjour", "Starting conversation with %s\n", to); + /* Make sure that the account always has a proxy of "none". * This is kind of dirty, but proxy_connect_none() isn't exposed. */ proxy_info = purple_account_get_proxy_info(data->account); @@ -730,13 +744,12 @@ } purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE); - connect_data = - purple_proxy_connect(data->account->gc, data->account, bb->ip, - bb->port_p2pj, _connected_to_buddy, pb); + connect_data = purple_proxy_connect(data->account->gc, data->account, + bb->ip, bb->port_p2pj, _connected_to_buddy, pb); if (connect_data == NULL) { purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to); - return -10001; + return NULL; } bb->conversation = bonjour_jabber_conv_new(); @@ -745,6 +758,26 @@ * that neeeds to wait until we're actually connected. */ bb->conversation->tx_handler = 0; } + return pb; +} + +int +bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body) +{ + xmlnode *message_node, *node, *node2; + gchar *message; + PurpleBuddy *pb; + BonjourBuddy *bb; + int ret; + + pb = _find_or_start_conversation(data, to); + if (pb == NULL) { + purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to); + /* You can not send a message to an offline buddy */ + return -10000; + } + + bb = pb->proto_data; message_node = xmlnode_new("message"); xmlnode_set_attrib(message_node, "to", bb->name); @@ -839,3 +872,168 @@ g_slist_free(buddies); } } + +XepIq * +xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id) +{ + xmlnode *iq_node = NULL; + XepIq *iq = NULL; + + if(data == NULL || to == NULL || id == NULL) + return NULL; + iq = g_new0(XepIq, 1); + if(iq == NULL) + return NULL; + + iq_node = xmlnode_new("iq"); + if(iq_node == NULL) { + g_free(iq); + return NULL; + } + + xmlnode_set_attrib(iq_node, "to", to); + xmlnode_set_attrib(iq_node, "id", id); + switch (type) { + case XEP_IQ_SET: + xmlnode_set_attrib(iq_node, "type", "set"); + break; + case XEP_IQ_GET: + xmlnode_set_attrib(iq_node, "type", "get"); + break; + case XEP_IQ_RESULT: + xmlnode_set_attrib(iq_node, "type", "result"); + break; + case XEP_IQ_ERROR: + xmlnode_set_attrib(iq_node, "type", "error"); + break; + case XEP_IQ_NONE: + default: + xmlnode_set_attrib(iq_node, "type", "none"); + break; + } + + iq->node = iq_node; + iq->type = type; + iq->data = ((BonjourData*)data)->jabber_data; + iq->to = (char*)to; + return iq; +} + +static gboolean +check_if_blocked(PurpleBuddy *pb) +{ + gboolean blocked = FALSE; + GSList *l = NULL; + PurpleAccount *acc = NULL; + + if(pb == NULL) + return FALSE; + + acc = pb->account; + + for(l = acc->deny; l != NULL; l = l->next) { + if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) { + purple_debug_info("Bonjour", "%s has been blocked.\n", pb->name, acc->username); + blocked = TRUE; + break; + } + } + return blocked; +} + +static void +xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb) +{ + xmlnode *child = NULL; + + if(packet == NULL || pb == NULL) + return; + + if(connection == NULL) { + if(pb->account != NULL) + connection = (pb->account)->gc; + } + + if(check_if_blocked(pb)) + return; + + if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "si"))) + xep_si_parse(connection, packet, pb); + else + xep_bytestreams_parse(connection, packet, pb); +} + +int +xep_iq_send(XepIq *iq) +{ + int ret = -1; + PurpleBuddy *pb = NULL; + + /* start the talk, reuse the message socket */ + pb = _find_or_start_conversation ((BonjourJabber*)iq->data, iq->to); + /* Send the message */ + if (pb != NULL) { + /* Convert xml node into stream */ + gchar *msg = xmlnode_to_str(iq->node, NULL); + ret = _send_data(pb, msg); + g_free(msg); + } + xmlnode_free(iq->node); + + return (ret >= 0) ? 0 : -1; +} + +/* This returns a ';' delimited string containing all non-localhost IPs */ +char * +purple_network_get_my_ip_ext2(int fd) +{ + char buffer[1024]; + static char ip_ext[17 * 10]; + char *tmp; + char *tip; + struct ifconf ifc; + struct ifreq *ifr; + struct sockaddr_in *sinptr; + guint32 lhost = htonl(127 * 256 * 256 * 256 + 1); + long unsigned int add; + int source = fd; + int len; + + if (fd < 0) + source = socket(PF_INET, SOCK_STREAM, 0); + + ifc.ifc_len = sizeof(buffer); + ifc.ifc_req = (struct ifreq *)buffer; + ioctl(source, SIOCGIFCONF, &ifc); + + if (fd < 0) + close(source); + + memset(ip_ext, 0, sizeof(ip_ext)); + memcpy(ip_ext, "0.0.0.0", 7); + tmp = buffer; + tip = ip_ext; + while (tmp < buffer + ifc.ifc_len) + { + ifr = (struct ifreq *)tmp; + tmp += HX_SIZE_OF_IFREQ(*ifr); + + if (ifr->ifr_addr.sa_family == AF_INET) + { + sinptr = (struct sockaddr_in *)&ifr->ifr_addr; + if (sinptr->sin_addr.s_addr != lhost) + { + add = ntohl(sinptr->sin_addr.s_addr); + len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;", + ((add >> 24) & 255), + ((add >> 16) & 255), + ((add >> 8) & 255), + add & 255); + tip = (char*) ((int) tip + len); + continue; + } + } + } + + return ip_ext; +}
--- a/libpurple/protocols/bonjour/jabber.h Mon Nov 12 20:39:26 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Mon Nov 12 23:10:08 2007 +0000 @@ -75,4 +75,24 @@ void bonjour_jabber_stop(BonjourJabber *data); +typedef enum { + XEP_IQ_SET, + XEP_IQ_GET, + XEP_IQ_RESULT, + XEP_IQ_ERROR, + XEP_IQ_NONE +} XepIqType; + +typedef struct _XepIq { + XepIqType type; + char *id; + xmlnode *node; + char *to; + void *data; +} XepIq; + +XepIq *xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id); +int xep_iq_send(XepIq *iq); +char *purple_network_get_my_ip_ext2(int fd); + #endif /* _BONJOUR_JABBER_H_ */