From: Nathan W. <fac...@us...> - 2003-10-31 02:44:02
|
Update of /cvsroot/gaim/gaim/src/protocols/jabber In directory sc8-pr-cvs1:/tmp/cvs-serv15705/src/protocols/jabber Modified Files: JEPS Makefile.am Makefile.mingw auth.c buddy.c buddy.h iq.c iq.h jabber.c jabber.h jutil.h oob.c oob.h presence.c Added Files: si.c si.h Log Message: this would be the non-working start of file transfer (the real way) for jabber also approximately eleventy billion jabber tweaks --- NEW FILE: si.c --- /* * gaim - Jabber Protocol Plugin * * Copyright (C) 2003, Nathan Walp <fac...@fa...> * * 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 "debug.h" #include "ft.h" #include "notify.h" #include "util.h" #include "buddy.h" #include "jabber.h" #include "iq.h" #include "si.h" #include "si.h" static GaimXfer *jabber_si_xfer_find_by_id(JabberStream *js, const char *id) { GList *xfers; if(!id) return NULL; for(xfers = js->file_transfers; xfers; xfers = xfers->next) { GaimXfer *xfer = xfers->data; JabberSIXfer *jsx = xfer->data; if(!strcmp(jsx->id, id)) return xfer; } return NULL; } static void jabber_si_xfer_ibb_start(JabberStream *js, xmlnode *packet, gpointer data) { GaimXfer *xfer = data; JabberSIXfer *jsx = xfer->data; /* Make sure we didn't get an error back */ /* XXX: OK, here we need to set up a g_idle thing to send messages * until our eyes bleed, but do it without interfering with normal * gaim operations. When we're done, we have to send a <close> like * we sent the <open> to start this damn thing. If we're really * fortunate, Exodus or someone else will implement something to test * against soon */ } void jabber_si_parse(JabberStream *js, xmlnode *packet) { GaimXfer *xfer; JabberSIXfer *jsx; xmlnode *si, *feature, *x, *field, *value; si = xmlnode_get_child(packet, "si"); xfer = jabber_si_xfer_find_by_id(js, xmlnode_get_attrib(si, "id")); if(!xfer) return; jsx = xfer->data; if(!(feature = xmlnode_get_child(si, "feature"))) return; for(x = feature->child; x; x = x->next) { const char *xmlns; if(x->type != NODE_TYPE_TAG) continue; if(!(xmlns = xmlnode_get_attrib(x, "xmlns"))) continue; if(strcmp(xmlns, "jabber:x:data")) continue; for(field = x->child; field; field = field->next) { const char *var; if(field->type != NODE_TYPE_TAG) continue; if(!(var = xmlnode_get_attrib(field, "var"))) continue; if(!strcmp(var, "stream-method")) { if((value = xmlnode_get_child(field, "value"))) { char *val_data = xmlnode_get_data(value); if(!val_data) jsx->stream_method = STREAM_METHOD_UNKNOWN; else if(!strcmp(val_data, "http://jabber.org/protocol/bytestreams")) jsx->stream_method = STREAM_METHOD_BYTESTREAMS; else if(!strcmp(val_data, "http://jabber.org/protocol/ibb")) jsx->stream_method = STREAM_METHOD_IBB; else jsx->stream_method = STREAM_METHOD_UNSUPPORTED; g_free(val_data); } } } } if(jsx->stream_method == STREAM_METHOD_UNKNOWN) { /* XXX */ } else if(jsx->stream_method == STREAM_METHOD_UNSUPPORTED) { /* XXX */ } else if(jsx->stream_method == STREAM_METHOD_BYTESTREAMS) { /* XXX: open the port and stuff */ char *buf; xmlnode *query, *streamhost; JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_SET, "http://jabber.org/protocol/bytestreams"); buf = g_strdup_printf("%s/%s", xfer->who, jsx->resource); xmlnode_set_attrib(iq->node, "to", buf); g_free(buf); query = xmlnode_get_child(iq->node, "query"); xmlnode_set_attrib(query, "sid", jsx->id); streamhost = xmlnode_new_child(query, "streamhost"); xmlnode_set_attrib(streamhost, "jid", gaim_account_get_username(js->gc->account)); xmlnode_set_attrib(streamhost, "host", xfer->local_ip); buf = g_strdup_printf("%d", xfer->local_port); xmlnode_set_attrib(streamhost, "port", buf); g_free(buf); jabber_iq_send(iq); } else if(jsx->stream_method == STREAM_METHOD_IBB) { char *buf; xmlnode *open; JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); buf = g_strdup_printf("%s/%s", xfer->who, jsx->resource); xmlnode_set_attrib(iq->node, "to", buf); g_free(buf); open = xmlnode_new_child(iq->node, "open"); xmlnode_set_attrib(open, "xmlns", "http://jabber.org/protocol/ibb"); xmlnode_set_attrib(open, "sid", jsx->id); jabber_iq_set_callback(iq, jabber_si_xfer_ibb_start, xfer); jabber_iq_send(iq); } } static void jabber_si_xfer_send_request(GaimXfer *xfer) { JabberSIXfer *jsx = xfer->data; JabberIq *iq; xmlnode *si, *file, *feature, *x, *field, *option, *value; char buf[32]; char *to; xfer->filename = g_path_get_basename(xfer->local_filename); iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); to = g_strdup_printf("%s/%s", xfer->who, jsx->resource); xmlnode_set_attrib(iq->node, "to", to); g_free(to); si = xmlnode_new_child(iq->node, "si"); xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si"); jsx->id = jabber_get_next_id(jsx->js); xmlnode_set_attrib(si, "id", jsx->id); xmlnode_set_attrib(si, "profile", "http://jabber.org/protocol/si/profile/file-transfer"); file = xmlnode_new_child(si, "file"); xmlnode_set_attrib(file, "xmlns", "http://jabber.org/protocol/si/profile/file-transfer"); xmlnode_set_attrib(file, "name", xfer->filename); g_snprintf(buf, sizeof(buf), "%d", xfer->size); xmlnode_set_attrib(file, "size", buf); /* maybe later we'll do hash and date attribs */ feature = xmlnode_new_child(si, "feature"); xmlnode_set_attrib(feature, "xmlns", "http://jabber.org/protocol/feature-neg"); x = xmlnode_new_child(feature, "x"); xmlnode_set_attrib(x, "xmlns", "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"); option = xmlnode_new_child(field, "option"); value = xmlnode_new_child(option, "value"); xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); option = xmlnode_new_child(field, "option"); value = xmlnode_new_child(option, "value"); xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); jabber_iq_send(iq); } void jabber_si_xfer_init(GaimXfer *xfer) { JabberSIXfer *jsx = xfer->data; if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { JabberBuddy *jb; JabberBuddyResource *jbr = NULL; GList *resources; GList *xfer_resources = NULL; jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); if(!jb) return; for(resources = jb->resources; resources; resources = resources->next) { jbr = resources->data; if(jbr->capabilities & JABBER_CAP_SI_FILE_XFER) xfer_resources = g_list_append(xfer_resources, jbr); } if(g_list_length(xfer_resources) == 1) { jbr = xfer_resources->data; jsx->resource = g_strdup(jbr->name); jabber_si_xfer_send_request(xfer); } else if(g_list_length(xfer_resources) == 0) { char *buf = g_strdup_printf(_("Could not send %s to %s, protocol not supported."), xfer->filename, xfer->who); gaim_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), buf); g_free(buf); } else { /* XXX: ask which resource to send to! */ } g_list_free(xfer_resources); } } void jabber_si_xfer_start(GaimXfer *xfer) { gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_start\n"); } void jabber_si_xfer_end(GaimXfer *xfer) { gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_end\n"); } void jabber_si_xfer_cancel_send(GaimXfer *xfer) { gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n"); } void jabber_si_xfer_cancel_recv(GaimXfer *xfer) { gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n"); } void jabber_si_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size) { gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_ack\n"); } --- NEW FILE: si.h --- /** * @file jutil.h utility functions * * gaim * * Copyright (C) 2003 Nathan Walp <fac...@fa...> * * 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 _GAIM_JABBER_SI_H_ #define _GAIM_JABBER_SI_H_ #include "ft.h" #include "jabber.h" typedef struct _JabberSIXfer { JabberStream *js; char *id; char *resource; enum { STREAM_METHOD_UNKNOWN, STREAM_METHOD_BYTESTREAMS, STREAM_METHOD_IBB, STREAM_METHOD_UNSUPPORTED } stream_method; } JabberSIXfer; void jabber_si_parse(JabberStream *js, xmlnode *packet); void jabber_si_xfer_init(GaimXfer *xfer); void jabber_si_xfer_start(GaimXfer *xfer); void jabber_si_xfer_end(GaimXfer *xfer); void jabber_si_xfer_cancel_send(GaimXfer *xfer); void jabber_si_xfer_cancel_recv(GaimXfer *xfer); void jabber_si_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size); #endif /* _GAIM_JABBER_SI_H_ */ Index: JEPS =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/JEPS,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -p -r1.4 -r1.5 --- JEPS 17 Oct 2003 20:03:43 -0000 1.4 +++ JEPS 31 Oct 2003 02:43:58 -0000 1.5 @@ -6,14 +6,18 @@ Presence (Invisible) 0022: DONE (replace?) Message Events -0030: NEED +0030: DONE Service Discovery 0045: DONE Multi-User Chat +0047: DONE + In-Band Bytestreams 0054: DONE (ugly) vCard 0060: NEED Pub-Sub +0065: DONE + SOCKS5 Bytestreams 0071: DONE (needs spec confirmation) XHTML-IM 0073: NEED @@ -42,9 +46,9 @@ Software Version 0093: NEED Roster Item Exchange -0095: NEED +0095: DONE Stream Initiation -0096: NEED +0096: DONE File Transfer Stream Initiation Profile 0100: NEED Gateway Interaction (Transports) Index: Makefile.am =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/Makefile.am,v retrieving revision 1.13 retrieving revision 1.14 diff -u -d -p -r1.13 -r1.14 --- Makefile.am 10 Oct 2003 20:35:05 -0000 1.13 +++ Makefile.am 31 Oct 2003 02:43:58 -0000 1.14 @@ -26,7 +26,9 @@ JABBERSOURCES = auth.c \ presence.c \ presence.h \ roster.c \ - roster.h + roster.h \ + si.c \ + si.h AM_CFLAGS = $(st) Index: Makefile.mingw =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/Makefile.mingw,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -p -r1.7 -r1.8 --- Makefile.mingw 6 Oct 2003 02:04:55 -0000 1.7 +++ Makefile.mingw 31 Oct 2003 02:43:58 -0000 1.8 @@ -80,6 +80,7 @@ C_SRC = auth.c \ parser.c \ presence.c \ roster.c \ + si.c \ win32/posix.uname.c Index: auth.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/auth.c,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -p -r1.7 -r1.8 --- auth.c 17 Oct 2003 20:03:43 -0000 1.7 +++ auth.c 31 Oct 2003 02:43:58 -0000 1.8 @@ -91,7 +91,7 @@ jabber_auth_start(JabberStream *js, xmln xmlnode_free(auth); } -static void auth_old_result_cb(JabberStream *js, xmlnode *packet) +static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data) { const char *type = xmlnode_get_attrib(packet, "type"); @@ -122,7 +122,7 @@ static void auth_old_result_cb(JabberStr jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); } -static void auth_old_cb(JabberStream *js, xmlnode *packet) +static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data) { JabberIq *iq; xmlnode *query, *x; @@ -163,7 +163,7 @@ static void auth_old_cb(JabberStream *js xmlnode_insert_data(x, pw, -1); } - jabber_iq_set_callback(iq, auth_old_result_cb); + jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } @@ -179,7 +179,7 @@ void jabber_auth_start_old(JabberStream username = xmlnode_new_child(query, "username"); xmlnode_insert_data(username, js->user->node, -1); - jabber_iq_set_callback(iq, auth_old_cb); + jabber_iq_set_callback(iq, auth_old_cb, NULL); jabber_iq_send(iq); } @@ -225,8 +225,8 @@ generate_response_value(JabberID *jid, c y = g_strndup(result, 16); - a1 = g_strdup_printf("%s:%s:%s:%s@%s/%s", y, nonce, cnonce, jid->node, - jid->domain, jid->resource); + a1 = g_strdup_printf("%s:%s:%s:%s@%s", y, nonce, cnonce, jid->node, + jid->domain); md5_init(&ctx); md5_append(&ctx, a1, strlen(a1)); @@ -270,7 +270,14 @@ jabber_auth_handle_challenge(JabberStrea char *enc_out; GHashTable *parts; + if(!enc_in) { + gaim_connection_error(js->gc, _("Invalid response from server")); + return; + } + gaim_base64_decode(enc_in, &dec_in, NULL); + gaim_debug(GAIM_DEBUG_MISC, "jabber", "decoded challenge (%d): %s\n", + strlen(dec_in), dec_in); parts = parse_challenge(dec_in); @@ -331,8 +338,8 @@ jabber_auth_handle_challenge(JabberStrea g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); g_string_append_printf(response, ",response=%s", auth_resp); g_string_append_printf(response, ",charset=utf-8"); - g_string_append_printf(response, ",authzid=\"%s@%s/%s\"", - js->user->node, js->user->domain, js->user->resource); + g_string_append_printf(response, ",authzid=\"%s@%s\"", + js->user->node, js->user->domain); g_free(auth_resp); g_free(cnonce); Index: buddy.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/buddy.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -p -r1.8 -r1.9 --- buddy.c 20 Oct 2003 20:41:42 -0000 1.8 +++ buddy.c 31 Oct 2003 02:43:58 -0000 1.9 @@ -25,13 +25,14 @@ #include "notify.h" #include "request.h" #include "util.h" +#include "xmlnode.h" #include "buddy.h" #include "chat.h" #include "jabber.h" #include "iq.h" #include "presence.h" -#include "xmlnode.h" +#include "si.h" void jabber_buddy_free(JabberBuddy *jb) @@ -530,7 +531,7 @@ void jabber_setup_set_info(GaimConnectio ******/ -static void jabber_vcard_parse(JabberStream *js, xmlnode *packet) +static void jabber_vcard_parse(JabberStream *js, xmlnode *packet, gpointer data) { GList *resources; const char *from = xmlnode_get_attrib(packet, "from"); @@ -789,7 +790,7 @@ void jabber_buddy_get_info(GaimConnectio vcard = xmlnode_new_child(iq->node, "vCard"); xmlnode_set_attrib(vcard, "xmlns", "vcard-temp"); - jabber_iq_set_callback(iq, jabber_vcard_parse); + jabber_iq_set_callback(iq, jabber_vcard_parse, NULL); jabber_iq_send(iq); } @@ -809,6 +810,31 @@ void jabber_buddy_get_info_chat(GaimConn g_free(full_jid); } + + +static void jabber_buddy_ask_send_file(GaimConnection *gc, const char *name) +{ + JabberStream *js = gc->proto_data; + GaimXfer *xfer; + JabberSIXfer *jsx; + + xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, name); + + xfer->data = jsx = g_new0(JabberSIXfer, 1); + jsx->js = js; + + gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); + gaim_xfer_set_start_fnc(xfer, jabber_si_xfer_start); + gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); + gaim_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send); + gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv); + gaim_xfer_set_ack_fnc(xfer, jabber_si_xfer_ack); + + js->file_transfers = g_list_append(js->file_transfers, xfer); + + gaim_xfer_request(xfer); +} + static void jabber_buddy_set_invisibility(JabberStream *js, const char *who, gboolean invisible) { @@ -869,6 +895,25 @@ GList *jabber_buddy_menu(GaimConnection struct proto_buddy_menu *pbm; JabberStream *js = gc->proto_data; JabberBuddy *jb = jabber_buddy_find(js, name, TRUE); + GList *resources; + gboolean has_file_xfer = FALSE; + + if(!jb) + return m; + + for(resources = jb->resources; resources; resources = resources->next) { + JabberBuddyResource *jbr = resources->data; + if(jbr->capabilities & JABBER_CAP_SI_FILE_XFER) + has_file_xfer = TRUE; + } + + if(has_file_xfer) { + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Send File"); + pbm->callback = jabber_buddy_ask_send_file; + pbm->gc = gc; + m = g_list_append(m, pbm); + } pbm = g_new0(struct proto_buddy_menu, 1); if(jb->invisible & JABBER_INVIS_BUDDY) { Index: buddy.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/buddy.h,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -p -r1.2 -r1.3 --- buddy.h 2 Oct 2003 01:58:26 -0000 1.2 +++ buddy.h 31 Oct 2003 02:43:58 -0000 1.3 @@ -48,8 +48,11 @@ typedef struct _JabberBuddyResource { int state; char *status; enum { - JABBER_CAP_XHTML = 1 << 1, - JABBER_CAP_COMPOSING = 1 << 2 + JABBER_CAP_XHTML = 1 << 1, + JABBER_CAP_COMPOSING = 1 << 2, + JABBER_CAP_SI = 1 << 3, + JABBER_CAP_SI_FILE_XFER = 1 << 4, + JABBER_CAP_BYTESTREAMS = 1 << 5 } capabilities; } JabberBuddyResource; Index: iq.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/iq.c,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -p -r1.5 -r1.6 --- iq.c 6 Oct 2003 02:04:55 -0000 1.5 +++ iq.c 31 Oct 2003 02:43:58 -0000 1.6 @@ -22,9 +22,11 @@ #include "debug.h" #include "prefs.h" +#include "buddy.h" #include "iq.h" #include "oob.h" #include "roster.h" +#include "si.h" #ifdef _WIN32 #include "utsname.h" @@ -79,9 +81,16 @@ JabberIq *jabber_iq_new_query(JabberStre return iq; } -void jabber_iq_set_callback(JabberIq *iq, JabberCallback *callback) +typedef struct _JabberCallbackData { + JabberIqCallback *callback; + gpointer data; +} JabberCallbackData; + +void +jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *callback, gpointer data) { iq->callback = callback; + iq->callback_data = data; } void jabber_iq_set_id(JabberIq *iq, const char *id) @@ -100,12 +109,17 @@ void jabber_iq_set_id(JabberIq *iq, cons void jabber_iq_send(JabberIq *iq) { + JabberCallbackData *jcd; g_return_if_fail(iq != NULL); jabber_send(iq->js, iq->node); - if(iq->id && iq->callback) - g_hash_table_insert(iq->js->callbacks, g_strdup(iq->id), iq->callback); + if(iq->id && iq->callback) { + jcd = g_new0(JabberCallbackData, 1); + jcd->callback = iq->callback; + jcd->data = iq->callback_data; + g_hash_table_insert(iq->js->callbacks, g_strdup(iq->id), jcd); + } jabber_iq_free(iq); } @@ -205,35 +219,159 @@ static void jabber_iq_handle_version(Jab jabber_iq_send(iq); } +#define SUPPORT_FEATURE(x) \ + feature = xmlnode_new_child(query, "feature"); \ + xmlnode_set_attrib(feature, "var", x); + + +void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { + const char *from = xmlnode_get_attrib(packet, "from"); + const char *type = xmlnode_get_attrib(packet, "type"); + + if(!from || !type) + return; + + if(!strcmp(type, "get")) { + xmlnode *query, *identity, *feature; + JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, + "http://jabber.org/protocol/disco#info"); + + jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id")); + + xmlnode_set_attrib(iq->node, "to", from); + query = xmlnode_get_child(iq->node, "query"); + + identity = xmlnode_new_child(query, "identity"); + xmlnode_set_attrib(identity, "category", "client"); + xmlnode_set_attrib(identity, "type", "pc"); /* XXX: bot, console, + * handheld, pc, phone, + * web */ + + SUPPORT_FEATURE("jabber:iq:last") + SUPPORT_FEATURE("jabber:iq:oob") + SUPPORT_FEATURE("jabber:iq:time") + SUPPORT_FEATURE("jabber:iq:version") + SUPPORT_FEATURE("jabber:x:conference") + SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams") + SUPPORT_FEATURE("http://jabber.org/protocol/disco#info") + SUPPORT_FEATURE("http://jabber.org/protocol/disco#items") + SUPPORT_FEATURE("http://jabber.org/protocol/muc") + SUPPORT_FEATURE("http://jabber.org/protocol/muc#user") + SUPPORT_FEATURE("http://jabber.org/protocol/si") + SUPPORT_FEATURE("http://jabber.org/protocol/si/profile/file-transfer") + + jabber_iq_send(iq); + }else if(!strcmp(type, "result")) { + xmlnode *query = xmlnode_get_child(packet, "query"); + xmlnode *child; + JabberID *jid; + JabberBuddy *jb; + JabberBuddyResource *jbr = NULL; + + if(!(jid = jabber_id_new(from))) + return; + + if(jid->resource && (jb = jabber_buddy_find(js, from, TRUE))) + jbr = jabber_buddy_find_resource(jb, jid->resource); + + if(!jbr) { + jabber_id_free(jid); + return; + } + + for(child = query->child; child; child = child->next) { + if(child->type != NODE_TYPE_TAG) + continue; + + if(!strcmp(child->name, "feature")) { + const char *var = xmlnode_get_attrib(child, "var"); + if(!var) + continue; + + if(!strcmp(var, "http://jabber.org/protocol/si")) + jbr->capabilities |= JABBER_CAP_SI; + else if(!strcmp(var, + "http://jabber.org/protocol/si/profile/file-transfer")) + jbr->capabilities |= JABBER_CAP_SI_FILE_XFER; + else if(!strcmp(var, "http://jabber.org/protocol/bytestreams")) + jbr->capabilities |= JABBER_CAP_BYTESTREAMS; + } + } + jabber_id_free(jid); + } +} + +void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) { + const char *from = xmlnode_get_attrib(packet, "from"); + const char *type = xmlnode_get_attrib(packet, "type"); + + if(!strcmp(type, "get")) { + JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, + "http://jabber.org/protocol/disco#items"); + + jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id")); + + xmlnode_set_attrib(iq->node, "to", from); + jabber_iq_send(iq); + } +} + void jabber_iq_parse(JabberStream *js, xmlnode *packet) { + JabberCallbackData *jcd; xmlnode *query; const char *xmlns; + const char *type, *id; query = xmlnode_get_child(packet, "query"); - if(!query) - return; + if(query) { - xmlns = xmlnode_get_attrib(query, "xmlns"); + xmlns = xmlnode_get_attrib(query, "xmlns"); - if(!xmlns) + if(!xmlns) + return; + + if(!strcmp(xmlns, "jabber:iq:roster")) { + jabber_roster_parse(js, packet); + return; + } else if(!strcmp(xmlns, "jabber:iq:last")) { + jabber_iq_handle_last(js, packet); + return; + } else if(!strcmp(xmlns, "jabber:iq:time")) { + jabber_iq_handle_time(js, packet); + return; + } else if(!strcmp(xmlns, "jabber:iq:version")) { + jabber_iq_handle_version(js, packet); + return; + } else if(!strcmp(xmlns, "jabber:iq:register")) { + jabber_register_parse(js, packet); + return; + } else if(!strcmp(xmlns, "jabber:iq:oob")) { + jabber_oob_parse(js, packet); + return; + } else if(!strcmp(xmlns, "http://jabber.org/protocol/disco#info")) { + jabber_disco_info_parse(js, packet); + return; + } else if(!strcmp(xmlns, "http://jabber.org/protocol/disco#items")) { + jabber_disco_items_parse(js, packet); + return; + } + } else if(xmlnode_get_child(packet, "si")) { + jabber_si_parse(js, packet); return; + } - if(!strcmp(xmlns, "jabber:iq:roster")) { - jabber_roster_parse(js, packet); - } else if(!strcmp(xmlns, "jabber:iq:last")) { - jabber_iq_handle_last(js, packet); - } else if(!strcmp(xmlns, "jabber:iq:time")) { - jabber_iq_handle_time(js, packet); - } else if(!strcmp(xmlns, "jabber:iq:version")) { - jabber_iq_handle_version(js, packet); - } else if(!strcmp(xmlns, "jabber:iq:register")) { - jabber_register_parse(js, packet); - } else if(!strcmp(xmlns, "jabber:iq:oob")) { - jabber_oob_parse(js, packet); - } else { - gaim_debug(GAIM_DEBUG_WARNING, "jabber", "Unknown query: %s\n", xmlns); + /* If we got here, no pre-defined handlers got it, lets see if a special + * callback got registered */ + + type = xmlnode_get_attrib(packet, "type"); + id = xmlnode_get_attrib(packet, "id"); + + if(type && (!strcmp(type, "result") || !strcmp(type, "error")) && id + && *id && (jcd = g_hash_table_lookup(js->callbacks, id))) { + jcd->callback(js, packet, jcd->data); + g_free(jcd); } } Index: iq.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/iq.h,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -p -r1.2 -r1.3 --- iq.h 6 Oct 2003 02:04:55 -0000 1.2 +++ iq.h 31 Oct 2003 02:43:58 -0000 1.3 @@ -34,12 +34,15 @@ typedef enum { JABBER_IQ_NONE } JabberIqType; +typedef void (JabberIqCallback)(JabberStream *js, xmlnode *packet, gpointer data); + struct _JabberIq { JabberIqType type; char *id; xmlnode *node; - JabberCallback *callback; + JabberIqCallback *callback; + gpointer callback_data; JabberStream *js; }; @@ -50,7 +53,7 @@ JabberIq *jabber_iq_new_query(JabberStre void jabber_iq_parse(JabberStream *js, xmlnode *packet); -void jabber_iq_set_callback(JabberIq *iq, JabberCallback *cb); +void jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *cb, gpointer data); void jabber_iq_set_id(JabberIq *iq, const char *id); void jabber_iq_send(JabberIq *iq); Index: jabber.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/jabber.c,v retrieving revision 1.207 retrieving revision 1.208 diff -u -d -p -r1.207 -r1.208 --- jabber.c 24 Oct 2003 05:46:01 -0000 1.207 +++ jabber.c 31 Oct 2003 02:43:58 -0000 1.208 @@ -63,8 +63,8 @@ static void jabber_stream_init(JabberStr open_stream = g_strdup_printf("<stream:stream to='%s' " "xmlns='jabber:client' " - "xmlns:stream='http://etherx.jabber.org/streams'>", - /* "version='1.0'>" */ + "xmlns:stream='http://etherx.jabber.org/streams' " + "version='1.0'>", js->user->domain); jabber_send_raw(js, open_stream); @@ -72,7 +72,8 @@ static void jabber_stream_init(JabberStr g_free(open_stream); } -static void jabber_session_initialized_cb(JabberStream *js, xmlnode *packet) +static void +jabber_session_initialized_cb(JabberStream *js, xmlnode *packet, gpointer data) { const char *type = xmlnode_get_attrib(packet, "type"); if(type && !strcmp(type, "result")) { @@ -87,7 +88,7 @@ static void jabber_session_init(JabberSt JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); xmlnode *session; - jabber_iq_set_callback(iq, jabber_session_initialized_cb); + jabber_iq_set_callback(iq, jabber_session_initialized_cb, NULL); session = xmlnode_new_child(iq->node, "session"); xmlnode_set_attrib(session, "xmlns", "urn:ietf:params:xml:ns:xmpp-session"); @@ -95,6 +96,32 @@ static void jabber_session_init(JabberSt jabber_iq_send(iq); } +static void jabber_bind_result_cb(JabberStream *js, xmlnode *packet, + gpointer data) +{ + /* XXX: check for errors, re-set our ow js->user JID */ + + jabber_session_init(js); +} + +static void jabber_stream_features_parse(JabberStream *js, xmlnode *packet) +{ + if(xmlnode_get_child(packet, "mechanisms")) { + jabber_auth_start(js, packet); + } else if(xmlnode_get_child(packet, "bind")) { + xmlnode *bind, *resource; + JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); + bind = xmlnode_new_child(iq->node, "bind"); + xmlnode_set_attrib(bind, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind"); + resource = xmlnode_new_child(bind, "resource"); + xmlnode_insert_data(resource, js->user->resource, -1); + + jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL); + + jabber_iq_send(iq); + } +} + static void jabber_stream_handle_error(JabberStream *js, xmlnode *packet) { xmlnode *textnode; @@ -171,29 +198,14 @@ static void tls_init(JabberStream *js); void jabber_process_packet(JabberStream *js, xmlnode *packet) { - const char *id = xmlnode_get_attrib(packet, "id"); - const char *type = xmlnode_get_attrib(packet, "type"); - JabberCallback *callback; - if(!strcmp(packet->name, "iq")) { - if(type && (!strcmp(type, "result") || !strcmp(type, "error")) && id - && *id && (callback = g_hash_table_lookup(js->callbacks, id))) - callback(js, packet); - else - jabber_iq_parse(js, packet); + jabber_iq_parse(js, packet); } else if(!strcmp(packet->name, "presence")) { jabber_presence_parse(js, packet); } else if(!strcmp(packet->name, "message")) { jabber_message_parse(js, packet); } else if(!strcmp(packet->name, "stream:features")) { - if(!js->registration && js->state == JABBER_STREAM_AUTHENTICATING) { - jabber_auth_start(js, packet); - } else if(js->state == JABBER_STREAM_REINITIALIZING) { - jabber_session_init(js); - } else { - gaim_debug(GAIM_DEBUG_WARNING, "jabber", - "Unexpected stream:features packet, ignoring\n", js->state); - } + jabber_stream_features_parse(js, packet); } else if(!strcmp(packet->name, "stream:error")) { jabber_stream_handle_error(js, packet); } else if(!strcmp(packet->name, "challenge")) { @@ -355,7 +367,7 @@ jabber_login(GaimAccount *account) js = gc->proto_data = g_new0(JabberStream, 1); js->gc = gc; js->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); + g_free, g_free); js->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_buddy_free); js->chats = g_hash_table_new_full(g_str_hash, g_str_equal, @@ -417,7 +429,7 @@ jabber_connection_schedule_close(JabberS } static void -jabber_registration_result_cb(JabberStream *js, xmlnode *packet) +jabber_registration_result_cb(JabberStream *js, xmlnode *packet, gpointer data) { const char *type = xmlnode_get_attrib(packet, "type"); char *buf; @@ -512,7 +524,7 @@ jabber_register_cb(JabberStream *js, Gai gaim_account_set_username(js->gc->account, username); g_free(username); - jabber_iq_set_callback(iq, jabber_registration_result_cb); + jabber_iq_set_callback(iq, jabber_registration_result_cb, NULL); jabber_iq_send(iq); @@ -653,7 +665,7 @@ static void jabber_register_account(Gaim js->gc = gc; js->registration = TRUE; js->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); + g_free, g_free); js->user = jabber_id_new(gaim_account_get_username(account)); js->next_id = g_random_int(); @@ -724,6 +736,15 @@ static void jabber_close(GaimConnection g_free(js); } +static void jabber_server_probe(JabberStream *js) +{ + JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, + "http://jabber.org/protocol/disco#items"); + + xmlnode_set_attrib(iq->node, "to", js->user->domain); + jabber_iq_send(iq); +} + void jabber_stream_set_state(JabberStream *js, JabberStreamState state) { js->state = state; @@ -757,6 +778,7 @@ void jabber_stream_set_state(JabberStrea gaim_connection_set_state(js->gc, GAIM_CONNECTED); jabber_roster_request(js); jabber_presence_send(js->gc, js->gc->away_state, js->gc->away); + jabber_server_probe(js); serv_finish_login(js->gc); break; } @@ -898,7 +920,9 @@ static GList *jabber_away_states(GaimCon return m; } -static void jabber_password_change_result_cb(JabberStream *js, xmlnode *packet) +static void +jabber_password_change_result_cb(JabberStream *js, xmlnode *packet, + gpointer data) { const char *type; @@ -954,7 +978,7 @@ static void jabber_password_change_cb(Ja y = xmlnode_new_child(query, "password"); xmlnode_insert_data(y, p1, -1); - jabber_iq_set_callback(iq, jabber_password_change_result_cb); + jabber_iq_set_callback(iq, jabber_password_change_result_cb, NULL); jabber_iq_send(iq); Index: jabber.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/jabber.h,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -p -r1.9 -r1.10 --- jabber.h 17 Oct 2003 20:03:44 -0000 1.9 +++ jabber.h 31 Oct 2003 02:43:58 -0000 1.10 @@ -68,6 +68,7 @@ typedef struct _JabberStream GHashTable *callbacks; int next_id; + GList *oob_file_transfers; GList *file_transfers; time_t idle; @@ -78,8 +79,6 @@ typedef struct _JabberStream gboolean registration; } JabberStream; - -typedef void (JabberCallback)(JabberStream *js, xmlnode *packet); void jabber_process_packet(JabberStream *js, xmlnode *packet); void jabber_send(JabberStream *js, xmlnode *data); Index: jutil.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/jutil.h,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -p -r1.6 -r1.7 --- jutil.h 24 Oct 2003 05:46:01 -0000 1.6 +++ jutil.h 31 Oct 2003 02:43:58 -0000 1.7 @@ -19,8 +19,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _GAIM_JABBER_JID_H_ -#define _GAIM_JABBER_JID_H_ +#ifndef _GAIM_JABBER_JUTIL_H_ +#define _GAIM_JABBER_JUTIL_H_ #include "account.h" @@ -46,4 +46,4 @@ gboolean jabber_nodeprep_validate(const gboolean jabber_nameprep_validate(const char *); gboolean jabber_resourceprep_validate(const char *); -#endif /* _GAIM_JABBER_JID_H_ */ +#endif /* _GAIM_JABBER_JUTIL_H_ */ Index: oob.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/oob.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -p -r1.2 -r1.3 --- oob.c 11 Oct 2003 01:58:59 -0000 1.2 +++ oob.c 31 Oct 2003 02:43:58 -0000 1.3 @@ -49,7 +49,8 @@ static void jabber_oob_xfer_init(GaimXfe static void jabber_oob_xfer_free(GaimXfer *xfer) { JabberOOBXfer *jox = xfer->data; - jox->js->file_transfers = g_list_remove(jox->js->file_transfers, xfer); + jox->js->oob_file_transfers = g_list_remove(jox->js->oob_file_transfers, + xfer); g_string_free(jox->headers, TRUE); g_free(jox->address); @@ -177,7 +178,7 @@ void jabber_oob_parse(JabberStream *js, gaim_xfer_set_read_fnc(xfer, jabber_oob_xfer_read); gaim_xfer_set_start_fnc(xfer, jabber_oob_xfer_start); - js->file_transfers = g_list_append(js->file_transfers, xfer); + js->oob_file_transfers = g_list_append(js->oob_file_transfers, xfer); gaim_xfer_request(xfer); } Index: oob.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/oob.h,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -p -r1.1 -r1.2 --- oob.h 6 Oct 2003 02:04:55 -0000 1.1 +++ oob.h 31 Oct 2003 02:43:58 -0000 1.2 @@ -24,4 +24,4 @@ void jabber_oob_parse(JabberStream *js, xmlnode *packet); -#endif /* _GAIM_JABBER_JID_H_ */ +#endif /* _GAIM_JABBER_OOB_H_ */ Index: presence.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/jabber/presence.c,v retrieving revision 1.17 retrieving revision 1.18 diff -u -d -p -r1.17 -r1.18 --- presence.c 24 Oct 2003 05:46:01 -0000 1.17 +++ presence.c 31 Oct 2003 02:43:58 -0000 1.18 @@ -145,7 +145,7 @@ void jabber_presence_parse(JabberStream JabberID *jid; JabberChat *chat; JabberBuddy *jb; - JabberBuddyResource *jbr; + JabberBuddyResource *jbr = FALSE; GaimBuddy *b; char *buddy_name; int state = 0; @@ -298,6 +298,7 @@ void jabber_presence_parse(JabberStream } g_free(room_jid); } else { + gboolean newly_online = FALSE; if(state != JABBER_STATE_ERROR && !(jb->subscription & JABBER_SUB_TO)) { gaim_debug(GAIM_DEBUG_INFO, "jabber", "got unexpected presence from %s, ignoring\n", from); @@ -314,19 +315,30 @@ void jabber_presence_parse(JabberStream } if(state == JABBER_STATE_ERROR || - (type && !strcasecmp(type, "unavailable"))) + (type && !strcasecmp(type, "unavailable"))) { jabber_buddy_remove_resource(jb, jid->resource); - else + } else { + if(!(jbr = jabber_buddy_find_resource(jb, jid->resource))) + newly_online = TRUE; jabber_buddy_track_resource(jb, jid->resource, priority, state, status); + } - jbr = jabber_buddy_find_resource(jb, jid->resource); + if(!jbr) + jbr = jabber_buddy_find_resource(jb, jid->resource); if(jbr) serv_got_update(js->gc, buddy_name, 1, 0, b->signon, b->idle, jbr->state); else serv_got_update(js->gc, buddy_name, 0, 0, 0, 0, 0); + + if(newly_online) { + JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, + "http://jabber.org/protocol/disco#info"); + xmlnode_set_attrib(iq->node, "to", from); + jabber_iq_send(iq); + } g_free(buddy_name); } |