diff libpurple/protocols/jabber/disco.c @ 26329:f5e613e05332

Applied disco-2.patch from nops with some modifications: * Alphabetized includes and Makefiles * Removed purple_disco_set_ui_ops(NULL) in finch; ops is NULL by default. * A few string changes * Removed DISCO_PREF_LAST_SERVER. Default to our server, but store the last requested in the JabberStream* and use it if available.
author Paul Aurich <paul@darkrain42.org>
date Sun, 29 Mar 2009 19:29:22 +0000
parents 5f9a24d1c25e
children f19c214201db
line wrap: on
line diff
--- a/libpurple/protocols/jabber/disco.c	Sat Mar 28 16:58:32 2009 +0000
+++ b/libpurple/protocols/jabber/disco.c	Sun Mar 29 19:29:22 2009 +0000
@@ -1,5 +1,5 @@
 /*
- * purple - Jabber Protocol Plugin
+ * purple - Jabber Service Discovery
  *
  * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
  *
@@ -22,6 +22,8 @@
 #include "internal.h"
 #include "prefs.h"
 #include "debug.h"
+#include "request.h"
+#include "notify.h"
 
 #include "buddy.h"
 #include "google.h"
@@ -32,7 +34,8 @@
 #include "roster.h"
 #include "pep.h"
 #include "adhoccommands.h"
-
+#include "xdata.h"
+#include "libpurple/disco.h"
 
 struct _jabber_disco_info_cb_data {
 	gpointer data;
@@ -268,6 +271,8 @@
 					capabilities |= JABBER_CAP_IQ_REGISTER;
 				else if(!strcmp(var, "http://www.xmpp.org/extensions/xep-0199.html#ns"))
 					capabilities |= JABBER_CAP_PING;
+				else if(!strcmp(var, "http://jabber.org/protocol/disco#items"))
+					capabilities |= JABBER_CAP_ITEMS;
 				else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
 					capabilities |= JABBER_CAP_ADHOC;
 				}
@@ -311,7 +316,8 @@
 	}
 }
 
-void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) {
+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");
 
@@ -396,6 +402,12 @@
 
 }
 
+struct _disco_data {
+	PurpleDiscoList *list;
+	PurpleDiscoService *parent;
+	char *node;
+};
+
 static void
 jabber_disco_server_info_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
 {
@@ -558,4 +570,371 @@
 	jabber_iq_send(iq);
 }
 
+static PurpleDiscoServiceCategory
+jabber_disco_category_from_string(const gchar *str)
+{
+	if (!strcasecmp(str, "gateway"))
+		return PURPLE_DISCO_SERVICE_CAT_GATEWAY;
+	else if (!strcasecmp(str, "directory"))
+		return PURPLE_DISCO_SERVICE_CAT_DIRECTORY;
+	else if (!strcasecmp(str, "conference"))
+		return PURPLE_DISCO_SERVICE_CAT_MUC;
 
+	return PURPLE_DISCO_SERVICE_CAT_NONE;
+}
+
+static PurpleDiscoServiceType
+jabber_disco_type_from_string(const gchar *str)
+{
+	if (!strcasecmp(str, "xmpp"))
+		return PURPLE_DISCO_SERVICE_TYPE_XMPP;
+	else if (!strcasecmp(str, "icq"))
+		return PURPLE_DISCO_SERVICE_TYPE_ICQ;
+	else if (!strcasecmp(str, "mrim"))
+		return PURPLE_DISCO_SERVICE_TYPE_MAIL;
+	else if (!strcasecmp(str, "user"))
+		return PURPLE_DISCO_SERVICE_TYPE_USER;
+	else if (!strcasecmp(str, "yahoo"))
+		return PURPLE_DISCO_SERVICE_TYPE_YAHOO;
+	else if (!strcasecmp(str, "irc"))
+		return PURPLE_DISCO_SERVICE_TYPE_IRC;
+	else if (!strcasecmp(str, "gadu-gadu"))
+		return PURPLE_DISCO_SERVICE_TYPE_GG;
+	else if (!strcasecmp(str, "aim"))
+		return PURPLE_DISCO_SERVICE_TYPE_AIM;
+	else if (!strcasecmp(str, "qq"))
+		return PURPLE_DISCO_SERVICE_TYPE_QQ;
+	else if (!strcasecmp(str, "msn"))
+		return PURPLE_DISCO_SERVICE_TYPE_MSN;
+
+	return PURPLE_DISCO_SERVICE_TYPE_NONE;
+}
+
+static void
+jabber_disco_service_info_cb(JabberStream *js, xmlnode *packet, gpointer data);
+
+static void
+jabber_disco_service_items_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	struct _disco_data *disco_data = data;
+	PurpleDiscoList *list = disco_data->list;
+	PurpleDiscoService *parent = disco_data->parent;
+	const char *parent_node = disco_data->node;
+	xmlnode *query = xmlnode_get_child(packet, "query");
+	const char *from = xmlnode_get_attrib(packet, "from");
+	const char *result = xmlnode_get_attrib(packet, "type");
+	xmlnode *child;
+	gboolean has_items = FALSE;
+
+	purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) - 1);
+
+	if (!from || !result || !query || (strcmp(result, "result")
+			|| !purple_disco_list_get_proto_data(list))) {
+		if (!purple_disco_list_get_fetch_count(list))
+			purple_disco_set_in_progress(list, FALSE);
+
+		purple_disco_list_unref(list);
+		return;
+	}
+
+	query = xmlnode_get_child(packet, "query");
+
+	for(child = xmlnode_get_child(query, "item"); child;
+			child = xmlnode_get_next_twin(child)) {
+		JabberIq *iq;
+		xmlnode *q;
+		const char *jid, *node;
+		struct _disco_data *disco_data;
+		char *full_node;
+
+		if(!(jid = xmlnode_get_attrib(child, "jid")) || !purple_disco_list_get_proto_data(list))
+			continue;
+
+		node = xmlnode_get_attrib(child, "node");
+
+		if (parent_node) {
+			if (node) {
+				full_node = g_new0(char, strlen(parent_node) + 1 + strlen(node) + 1);
+				strcat(full_node, parent_node);
+				strcat(full_node, "/");
+				strcat(full_node, node);
+			} else {
+				continue;
+			}
+		} else {
+			full_node = g_strdup(node);
+		}
+
+		disco_data = g_new0(struct _disco_data, 1);
+		disco_data->list = list;
+		disco_data->parent = parent;
+		disco_data->node = full_node;
+
+		has_items = TRUE;
+		purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
+		purple_disco_list_ref(list);
+		iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
+		xmlnode_set_attrib(iq->node, "to", jid);
+		if (full_node && (q = xmlnode_get_child(iq->node, "query")))
+			xmlnode_set_attrib(q, "node", full_node);
+		jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data);
+
+		jabber_iq_send(iq);
+	}
+
+	if (!purple_disco_list_get_fetch_count(list))
+		purple_disco_set_in_progress(list, FALSE);
+	purple_disco_list_unref(list);
+
+	g_free(disco_data->node);
+	g_free(disco_data);
+}
+
+static void
+jabber_disco_service_info_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	struct _disco_data *disco_data = data;
+	PurpleDiscoList *list = disco_data->list;
+	PurpleDiscoService *parent = disco_data->parent;
+	char *node = g_strdup(disco_data->node);
+	xmlnode *query, *ident, *child;
+	const char *from = xmlnode_get_attrib(packet, "from");
+	const char *result = xmlnode_get_attrib(packet, "type");
+	const char *acat, *atype, *adesc, *anode;
+	char *aname;
+	PurpleDiscoService *s;
+	PurpleDiscoServiceCategory cat;
+	PurpleDiscoServiceType type;
+	int flags = PURPLE_DISCO_FLAG_ADD;
+
+	g_free(disco_data->node);
+	g_free(disco_data);
+	purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) - 1);
+
+	if (!from || !result || (strcmp(result, "result") || !purple_disco_list_get_proto_data(list))
+			|| (!(query = xmlnode_get_child(packet, "query")))
+			|| (!(ident = xmlnode_get_child(query, "identity")))) {
+		if (!purple_disco_list_get_fetch_count(list))
+			purple_disco_set_in_progress(list, FALSE);
+
+		purple_disco_list_unref(list);
+		return;
+	}
+
+	acat = xmlnode_get_attrib(ident, "category");
+	atype = xmlnode_get_attrib(ident, "type");
+	adesc = xmlnode_get_attrib(ident, "name");
+	anode = xmlnode_get_attrib(query, "node");
+
+	if (anode) {
+		aname = g_new0(char, strlen(from) + strlen(anode) + 1);
+		strcat(aname, from);
+		strcat(aname, anode);
+	} else {
+		aname = g_strdup(from);
+	}
+
+	cat = jabber_disco_category_from_string(acat);
+	type = jabber_disco_type_from_string(atype);
+
+	for (child = xmlnode_get_child(query, "feature"); child;
+			child = xmlnode_get_next_twin(child)) {
+		const char *var;
+
+		if (!(var = xmlnode_get_attrib(child, "var")))
+			continue;
+		
+		if (!strcmp(var, "jabber:iq:register"))
+			flags |= PURPLE_DISCO_FLAG_REGISTER;
+		
+		if (!strcmp(var, "http://jabber.org/protocol/disco#items"))
+			flags |= PURPLE_DISCO_FLAG_BROWSE;
+
+		if (!strcmp(var, "http://jabber.org/protocol/muc"))
+			cat = PURPLE_DISCO_SERVICE_CAT_MUC;
+	}
+
+	purple_debug_info("disco", "service %s, category %s (%d), type %s (%d), description %s, flags %04x\n",
+			aname,
+			acat, cat,
+			atype, type,
+			adesc, flags);
+
+	s = purple_disco_list_service_new(cat, aname, type, adesc, flags);
+	purple_disco_list_service_add(list, s, parent);
+
+	/* if (flags & PURPLE_DISCO_FLAG_BROWSE) - not all browsable services has this future */
+	{
+		xmlnode *q;
+		JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
+			
+		purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
+		purple_disco_list_ref(list);
+		disco_data = g_new0(struct _disco_data, 1);
+		disco_data->list = list;
+		disco_data->parent = s;
+
+		xmlnode_set_attrib(iq->node, "to", from);
+		jabber_iq_set_callback(iq, jabber_disco_service_items_cb, disco_data);
+		if (anode && (q = xmlnode_get_child(iq->node, "query")))
+			xmlnode_set_attrib(q, "node", node);
+		jabber_iq_send(iq);
+	}
+
+	if (!purple_disco_list_get_fetch_count(list))
+		purple_disco_set_in_progress(list, FALSE);
+
+	purple_disco_list_unref(list);
+
+	g_free(aname);
+	g_free(node);
+}
+
+static void
+jabber_disco_server_items_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	PurpleDiscoList *list = data;
+	xmlnode *query, *child;
+	const char *from = xmlnode_get_attrib(packet, "from");
+	const char *type = xmlnode_get_attrib(packet, "type");
+	gboolean has_items = FALSE;
+
+	if (!from || !type)
+		return;
+
+	if (strcmp(type, "result"))
+		return;
+
+	query = xmlnode_get_child(packet, "query");
+
+	for(child = xmlnode_get_child(query, "item"); child;
+			child = xmlnode_get_next_twin(child)) {
+		JabberIq *iq;
+		const char *jid;
+		struct _disco_data *disco_data;
+
+		if(!(jid = xmlnode_get_attrib(child, "jid")) || !purple_disco_list_get_proto_data(list))
+			continue;
+
+		disco_data = g_new0(struct _disco_data, 1);
+		disco_data->list = list;
+
+		has_items = TRUE;
+		purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
+		purple_disco_list_ref(list);
+		iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
+		xmlnode_set_attrib(iq->node, "to", jid);
+		jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data);
+
+		jabber_iq_send(iq);
+	}
+
+	if (!has_items)
+		purple_disco_set_in_progress(list, FALSE);
+
+	purple_disco_list_unref(list);
+}
+
+static void
+jabber_disco_server_info_cb(JabberStream *js, const char *who, JabberCapabilities caps, gpointer data)
+{
+	PurpleDiscoList *list = data;
+	JabberIq *iq;
+
+	if (caps & JABBER_CAP_ITEMS) {
+		iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
+		xmlnode_set_attrib(iq->node, "to", who);
+		jabber_iq_set_callback(iq, jabber_disco_server_items_cb, list);
+	
+		if (purple_disco_list_get_proto_data(list))
+			jabber_iq_send(iq);
+		else
+			purple_disco_list_unref(list);
+	
+	} else {
+		purple_notify_error(NULL, _("Error"), _("Server doesn't support service discovery"), NULL); 
+		purple_disco_set_in_progress(list, FALSE);
+		purple_disco_list_unref(list);
+	}
+}
+
+static void
+jabber_disco_server_cb(PurpleDiscoList *list, PurpleRequestFields *fields)
+{
+	JabberStream *js;
+	const char *server_name;
+
+	server_name = purple_request_fields_get_string(fields, "server");
+
+	js = purple_disco_list_get_proto_data(list);
+	if (!js) {
+		purple_debug_error("jabber", "Service discovery requested for %s "
+		                             "without proto_data", server_name);
+		return;
+	}
+
+	purple_disco_set_in_progress(list, TRUE);
+	purple_debug_misc("jabber", "Service discovery for %s\n", server_name);
+	if (js->last_disco_server)
+		g_free(js->last_disco_server);
+	js->last_disco_server = g_strdup(server_name);
+
+	jabber_disco_info_do(js, server_name, jabber_disco_server_info_cb, list);
+}
+
+void
+jabber_disco_get_list(PurpleConnection *gc, PurpleDiscoList *list)
+{
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *g;
+	PurpleRequestField *f;
+	JabberStream *js;
+	const char *last_server;
+	
+	purple_debug_misc("disco.c", "get_list\n");
+
+	js = purple_connection_get_protocol_data(gc);
+	purple_disco_list_set_proto_data(list, js);
+
+	last_server = js->last_disco_server;
+	if (last_server == NULL)
+		last_server = js->user->domain;
+
+
+	fields = purple_request_fields_new();
+	g = purple_request_field_group_new(NULL);
+	f = purple_request_field_string_new("server", _("Server"), 
+		last_server ? last_server : js->user->domain, FALSE);
+
+	purple_request_field_group_add_field(g, f);
+	purple_request_fields_add_group(fields, g);
+	
+	purple_disco_list_ref(list);
+	
+	purple_request_fields(gc,
+		_("Server name request"),
+		_("Enter server name"),
+		NULL,
+		fields,
+		_("OK"), G_CALLBACK(jabber_disco_server_cb),
+		_("Cancel"), NULL,
+		purple_connection_get_account(gc), NULL, NULL, list);
+}
+
+void
+jabber_disco_cancel(PurpleDiscoList *list)
+{
+	purple_disco_list_set_proto_data(list, NULL);
+	purple_disco_set_in_progress(list, FALSE);	
+}
+
+int
+jabber_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service)
+{
+	JabberStream *js = gc->proto_data;
+	
+	jabber_register_gateway(js, service->name);
+
+	return 0;
+}
+