Browse Source

dbus: Move introspection support into a separate file

Jouni Malinen 15 years ago
parent
commit
04551ee6d6

+ 1 - 0
wpa_supplicant/Makefile

@@ -1083,6 +1083,7 @@ ifndef DBUS_INCLUDE
 DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
 endif
 ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_OBJS += dbus/dbus_new_introspect.o
 DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
 DBUS_INCLUDE += $(shell xml2-config --cflags)
 DBUS_LIBS += $(shell xml2-config --libs)

+ 1 - 0
wpa_supplicant/dbus/Makefile

@@ -70,6 +70,7 @@ LIB_OBJS= \
 	dbus_new.o \
 	dbus_new_handlers.o \
 	dbus_new_helpers.o \
+	dbus_new_introspect.o \
 	dbus_dict_helpers.o
 
 ifdef CONFIG_WPS

+ 10 - 527
wpa_supplicant/dbus/dbus_new_helpers.c

@@ -16,534 +16,10 @@
 #include "includes.h"
 
 #include "common.h"
-#include "eloop.h"
 #include "dbus_common.h"
 #include "dbus_common_i.h"
 #include "dbus_new_helpers.h"
 
-/**
- * struct wpa_dbus_method_desc - DBus method description
- */
-struct wpa_dbus_method_desc {
-	/* pointer to next description in list */
-	struct wpa_dbus_method_desc *next;
-
-	/* method interface */
-	char *dbus_interface;
-	/* method name */
-	char *dbus_method;
-
-	/* method handling function */
-	WPADBusMethodHandler method_handler;
-
-	/* number of method arguments */
-	int args_num;
-	/* array of arguments */
-	struct wpa_dbus_argument args[];
-};
-
-
-/**
- * struct wpa_dbus_signal_desc - DBus signal description
- */
-struct wpa_dbus_signal_desc {
-	/* pointer to next description in list */
-	struct wpa_dbus_signal_desc *next;
-
-	/* signal interface */
-	char *dbus_interface;
-	/* signal name */
-	char *dbus_signal;
-
-	/* number of signal arguments */
-	int args_num;
-	/* array of arguments */
-	struct wpa_dbus_argument args[0];
-};
-
-
-/**
- * struct wpa_dbus_property_desc - DBus property description
- */
-struct wpa_dbus_property_desc {
-	/* pointer to next description in list */
-	struct wpa_dbus_property_desc *next;
-
-	/* property interface */
-	char *dbus_interface;
-	/* property name */
-	char *dbus_property;
-	/* property type signature in DBus type notation */
-	char *type;
-
-	/* property access permissions */
-	enum dbus_prop_access access;
-
-	/* property getter function */
-	WPADBusPropertyAccessor getter;
-	/* property setter function */
-	WPADBusPropertyAccessor setter;
-};
-
-
-#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
-#include <libxml/tree.h>
-
-struct interfaces {
-	struct interfaces *next;
-	char *dbus_interface;
-	xmlNodePtr interface_node;
-};
-#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
-
-
-#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
-
-/**
- * extract_interfaces - Extract interfaces from methods, signals and props
- * @obj_dsc: Description of object from which interfaces will be extracted
- * @root_node: root node of XML introspection document
- * Returns: List of interfaces found in object description
- *
- * Iterates over all methods, signals and properties registered with
- * object and collects all declared DBus interfaces and create interface's
- * node in XML root node for each. Returned list elements contains interface
- * name and XML node of corresponding interface.
- */
-static struct interfaces * extract_interfaces(
-	struct wpa_dbus_object_desc *obj_dsc, xmlNodePtr root_node)
-{
-	struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
-	struct wpa_dbus_signal_desc *signal_dsc = obj_dsc->signals;
-	struct wpa_dbus_property_desc *property_dsc = obj_dsc->properties;
-	struct interfaces *head = NULL;
-	struct interfaces *iface, *last;
-	int len;
-
-	/* extract interfaces from methods */
-	while (method_dsc) {
-		iface = head;
-		last = NULL;
-
-		/* go to next method if its interface is already extracted */
-		while (iface) {
-			if (!os_strcmp(iface->dbus_interface,
-				       method_dsc->dbus_interface))
-				break;
-			last = iface;
-			iface = iface->next;
-		}
-		if (iface) {
-			method_dsc = method_dsc->next;
-			continue;
-		}
-
-		iface = os_zalloc(sizeof(struct interfaces));
-		if (!iface) {
-			wpa_printf(MSG_ERROR, "Not enough memory to create "
-				"interface introspection data");
-			method_dsc = method_dsc->next;
-			continue;
-		}
-
-		if (last)
-			last->next = iface;
-		else
-			head = iface;
-
-		len = os_strlen(method_dsc->dbus_interface) + 1;
-		iface->dbus_interface = os_malloc(len);
-		if (!iface->dbus_interface) {
-			wpa_printf(MSG_ERROR, "Not enough memory to create "
-				   "interface introspection data (interface "
-				   "name)");
-			method_dsc = method_dsc->next;
-			continue;
-		}
-		os_strncpy(iface->dbus_interface, method_dsc->dbus_interface,
-			   len);
-
-		iface->interface_node = xmlNewChild(root_node, NULL,
-						    BAD_CAST "interface",
-						    NULL);
-		xmlNewProp(iface->interface_node, BAD_CAST "name",
-			   BAD_CAST method_dsc->dbus_interface);
-
-		method_dsc = method_dsc->next;
-	}
-
-	/* extract interfaces from signals */
-	while (signal_dsc) {
-		iface = head;
-		last = NULL;
-
-		/* go to next signal if its interface is already extracted */
-		while (iface) {
-			if (!os_strcmp(iface->dbus_interface,
-				       signal_dsc->dbus_interface))
-				break;
-			last = iface;
-			iface = iface->next;
-		}
-		if (iface) {
-			signal_dsc = signal_dsc->next;
-			continue;
-		}
-
-		iface = os_zalloc(sizeof(struct interfaces));
-		if (!iface) {
-			wpa_printf(MSG_ERROR, "Not enough memory to create "
-				   "interface introspection data");
-			signal_dsc = signal_dsc->next;
-			continue;
-		}
-
-		if (last)
-			last->next = iface;
-		else
-			head = iface;
-
-		len = os_strlen(signal_dsc->dbus_interface) + 1;
-		iface->dbus_interface = os_malloc(len);
-		if (!iface->dbus_interface) {
-			wpa_printf(MSG_ERROR, "Not enough memory to create "
-				   "interface introspection data (interface "
-				   "name)");
-			signal_dsc = signal_dsc->next;
-			continue;
-		}
-		os_strncpy(iface->dbus_interface, signal_dsc->dbus_interface,
-			   len);
-
-		iface->interface_node = xmlNewChild(root_node, NULL,
-						    BAD_CAST "interface",
-						    NULL);
-		xmlNewProp(iface->interface_node, BAD_CAST "name",
-			   BAD_CAST signal_dsc->dbus_interface);
-
-		signal_dsc = signal_dsc->next;
-	}
-
-	/* extract interfaces from properties */
-	while (property_dsc) {
-		iface = head;
-		last = NULL;
-
-		/* go to next property if its interface is already extracted */
-		while (iface) {
-			if (!os_strcmp(iface->dbus_interface,
-				       property_dsc->dbus_interface))
-				break;
-			last = iface;
-			iface = iface->next;
-		}
-		if (iface) {
-			property_dsc = property_dsc->next;
-			continue;
-		}
-
-		iface = os_zalloc(sizeof(struct interfaces));
-		if (!iface) {
-			wpa_printf(MSG_ERROR, "Not enough memory to create "
-				   "interface introspection data");
-			property_dsc = property_dsc->next;
-			continue;
-		}
-
-		if (last)
-			last->next = iface;
-		else
-			head = iface;
-
-		len = os_strlen(property_dsc->dbus_interface) + 1;
-		iface->dbus_interface = os_malloc(len);
-		if (!iface->dbus_interface) {
-			wpa_printf(MSG_ERROR, "Not enough memory to create "
-				   "interface introspection data (interface "
-				   "name)");
-			property_dsc = property_dsc->next;
-			continue;
-		}
-		os_strncpy(iface->dbus_interface, property_dsc->dbus_interface,
-			   len);
-
-		iface->interface_node = xmlNewChild(root_node, NULL,
-						    BAD_CAST "interface",
-						    NULL);
-		xmlNewProp(iface->interface_node, BAD_CAST "name",
-			   BAD_CAST property_dsc->dbus_interface);
-
-		property_dsc = property_dsc->next;
-	}
-
-	return head;
-}
-
-
-/**
- * introspect - Responds for Introspect calls on object
- * @message: Message with Introspect call
- * @obj_dsc: Object description on which Introspect was called
- * Returns: Message with introspection result XML string as only argument
- *
- * Iterates over all methods, signals and properties registered with
- * object and generates introspection data for the object as XML string.
- */
-static DBusMessage * introspect(DBusMessage *message,
-				struct wpa_dbus_object_desc *obj_dsc)
-{
-
-	DBusMessage *reply;
-	struct interfaces *ifaces, *tmp;
-	struct wpa_dbus_signal_desc *signal_dsc;
-	struct wpa_dbus_method_desc *method_dsc;
-	struct wpa_dbus_property_desc *property_dsc;
-	xmlChar *intro_str;
-	char **children;
-	int i, s;
-
-	xmlDocPtr doc = NULL;
-	xmlNodePtr root_node = NULL, node = NULL, iface_node = NULL;
-	xmlNodePtr method_node = NULL, signal_node = NULL;
-	xmlNodePtr property_node = NULL, arg_node = NULL;
-
-	/* root node and dtd */
-	doc = xmlNewDoc(BAD_CAST "1.0");
-	root_node = xmlNewNode(NULL, BAD_CAST "node");
-	xmlDocSetRootElement(doc, root_node);
-	xmlCreateIntSubset(doc, BAD_CAST "node",
-			   BAD_CAST DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER,
-			   BAD_CAST DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER);
-
-	/* Add Introspectable interface */
-	iface_node = xmlNewChild(root_node, NULL, BAD_CAST "interface", NULL);
-	xmlNewProp(iface_node, BAD_CAST "name",
-		   BAD_CAST WPA_DBUS_INTROSPECTION_INTERFACE);
-
-	/* Add Introspect method */
-	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
-	xmlNewProp(method_node, BAD_CAST "name",
-		   BAD_CAST WPA_DBUS_INTROSPECTION_METHOD);
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "data");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "out");
-
-
-	/* Add Properties interface */
-	iface_node = xmlNewChild(root_node, NULL,
-				 BAD_CAST "interface", NULL);
-	xmlNewProp(iface_node, BAD_CAST "name",
-		   BAD_CAST WPA_DBUS_PROPERTIES_INTERFACE);
-
-	/* Add Get method */
-	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
-	xmlNewProp(method_node, BAD_CAST "name",
-		   BAD_CAST WPA_DBUS_PROPERTIES_GET);
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "interface");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "propname");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "value");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "v");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "out");
-	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
-
-	/* Add GetAll method */
-	xmlNewProp(method_node, BAD_CAST "name",
-		   BAD_CAST WPA_DBUS_PROPERTIES_GETALL);
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "interface");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "props");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "a{sv}");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "out");
-	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
-
-	/* Add Set method */
-	xmlNewProp(method_node, BAD_CAST "name",
-		   BAD_CAST WPA_DBUS_PROPERTIES_SET);
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "interface");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "propname");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
-	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
-	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "value");
-	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "v");
-	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
-
-	/* get all interfaces registered with object */
-	ifaces = extract_interfaces(obj_dsc, root_node);
-
-	/* create methods' nodes */
-	method_dsc = obj_dsc->methods;
-	while (method_dsc) {
-
-		struct interfaces *iface = ifaces;
-		while (iface) {
-			if (!os_strcmp(iface->dbus_interface,
-				       method_dsc->dbus_interface))
-				break;
-			iface = iface->next;
-		}
-		if (!iface)
-			continue;
-
-		iface_node = iface->interface_node;
-		method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method",
-					  NULL);
-		xmlNewProp(method_node, BAD_CAST "name",
-			   BAD_CAST method_dsc->dbus_method);
-
-		/* create args' nodes */
-		for (i = 0; i < method_dsc->args_num; i++) {
-			struct wpa_dbus_argument arg = method_dsc->args[i];
-			arg_node = xmlNewChild(method_node, NULL,
-					       BAD_CAST "arg", NULL);
-			if (arg.name && strlen(arg.name)) {
-				xmlNewProp(arg_node, BAD_CAST "name",
-					   BAD_CAST arg.name);
-			}
-			xmlNewProp(arg_node, BAD_CAST "type",
-				   BAD_CAST arg.type);
-			xmlNewProp(arg_node, BAD_CAST "direction",
-				   BAD_CAST (arg.dir == ARG_IN ?
-					     "in" : "out"));
-		}
-		method_dsc = method_dsc->next;
-	}
-
-	/* create signals' nodes */
-	signal_dsc = obj_dsc->signals;
-	while (signal_dsc) {
-
-		struct interfaces *iface = ifaces;
-		while (iface) {
-			if (!os_strcmp(iface->dbus_interface,
-				       signal_dsc->dbus_interface))
-				break;
-			iface = iface->next;
-		}
-		if (!iface)
-			continue;
-
-		iface_node = iface->interface_node;
-		signal_node = xmlNewChild(iface_node, NULL, BAD_CAST "signal",
-					  NULL);
-		xmlNewProp(signal_node, BAD_CAST "name",
-			   BAD_CAST signal_dsc->dbus_signal);
-
-		/* create args' nodes */
-		for (i = 0; i < signal_dsc->args_num; i++) {
-			struct wpa_dbus_argument arg = signal_dsc->args[i];
-			arg_node = xmlNewChild(signal_node, NULL,
-					       BAD_CAST "arg", NULL);
-			if (arg.name && strlen(arg.name)) {
-				xmlNewProp(arg_node, BAD_CAST "name",
-					   BAD_CAST arg.name);
-			}
-			xmlNewProp(arg_node, BAD_CAST "type",
-				   BAD_CAST arg.type);
-		}
-		signal_dsc = signal_dsc->next;
-	}
-
-	/* create properties' nodes */
-	property_dsc = obj_dsc->properties;
-	while (property_dsc) {
-
-		struct interfaces *iface = ifaces;
-		while (iface) {
-			if (!os_strcmp(iface->dbus_interface,
-				       property_dsc->dbus_interface))
-				break;
-			iface = iface->next;
-		}
-		if (!iface)
-			continue;
-
-		iface_node = iface->interface_node;
-		property_node = xmlNewChild(iface_node, NULL,
-					    BAD_CAST "property", NULL);
-		xmlNewProp(property_node, BAD_CAST "name",
-			   BAD_CAST property_dsc->dbus_property);
-		xmlNewProp(property_node, BAD_CAST "type",
-			   BAD_CAST property_dsc->type);
-		xmlNewProp(property_node, BAD_CAST "access", BAD_CAST
-			   (property_dsc->access == R ? "read" :
-			    (property_dsc->access == W ?
-			     "write" : "readwrite")));
-
-		property_dsc = property_dsc->next;
-	}
-
-	/* add child nodes to introspection tree; */
-	dbus_connection_list_registered(obj_dsc->connection,
-					dbus_message_get_path(message),
-					&children);
-	for (i = 0; children[i]; i++) {
-		node = xmlNewChild(root_node, NULL, BAD_CAST "node", NULL);
-		xmlNewProp(node, BAD_CAST "name", BAD_CAST children[i]);
-	}
-	dbus_free_string_array(children);
-
-
-	xmlDocDumpFormatMemory(doc, &intro_str, &s, 1);
-
-	xmlFreeDoc(doc);
-
-	while (ifaces) {
-		tmp = ifaces;
-		ifaces = ifaces->next;
-		os_free(tmp->dbus_interface);
-		os_free(tmp);
-	}
-
-	reply = dbus_message_new_method_return(message);
-	if (reply == NULL) {
-		xmlFree(intro_str);
-		return NULL;
-	}
-
-	dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
-				 DBUS_TYPE_INVALID);
-
-	xmlFree(intro_str);
-
-	return reply;
-}
-
-#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
-
-/**
- * introspect - Responds for Introspect calls on object
- * @message: Message with Introspect call
- * @obj_dsc: Object description on which Introspect was called
- * Returns: Message with introspection result XML string as only argument
- *
- * Returns error informing that introspection support was not compiled.
- */
-static DBusMessage * introspect(DBusMessage *message,
-				struct wpa_dbus_object_desc *obj_dsc)
-{
-	return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
-				      "wpa_supplicant was compiled without "
-				      "introspection support.");
-}
-
-#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
-
 
 /**
  * recursive_iter_copy - Reads arguments from one iterator and
@@ -935,9 +411,16 @@ static DBusHandlerResult message_handler(DBusConnection *connection,
 	if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
 			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
 	    !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
-			WPAS_DBUS_INTERFACE_MAX))
-		reply = introspect(message, obj_dsc);
-	else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
+			WPAS_DBUS_INTERFACE_MAX)) {
+#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+		reply = wpa_dbus_introspect(message, obj_dsc);
+#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
+		reply = dbus_message_new_error(
+			message, DBUS_ERROR_UNKNOWN_METHOD,
+			"wpa_supplicant was compiled without "
+			"introspection support.");
+#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
+	} else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
 			     WPAS_DBUS_INTERFACE_MAX)) {
 		/* if message is properties method call */
 		reply = properties_handler(message, obj_dsc);

+ 66 - 0
wpa_supplicant/dbus/dbus_new_helpers.h

@@ -52,6 +52,69 @@ struct wpa_dbus_argument {
 
 #define END_ARGS { NULL, NULL, ARG_IN }
 
+/**
+ * struct wpa_dbus_method_desc - DBus method description
+ */
+struct wpa_dbus_method_desc {
+	/* pointer to next description in list */
+	struct wpa_dbus_method_desc *next;
+
+	/* method interface */
+	char *dbus_interface;
+	/* method name */
+	char *dbus_method;
+
+	/* method handling function */
+	WPADBusMethodHandler method_handler;
+
+	/* number of method arguments */
+	int args_num;
+	/* array of arguments */
+	struct wpa_dbus_argument args[];
+};
+
+/**
+ * struct wpa_dbus_signal_desc - DBus signal description
+ */
+struct wpa_dbus_signal_desc {
+	/* pointer to next description in list */
+	struct wpa_dbus_signal_desc *next;
+
+	/* signal interface */
+	char *dbus_interface;
+	/* signal name */
+	char *dbus_signal;
+
+	/* number of signal arguments */
+	int args_num;
+	/* array of arguments */
+	struct wpa_dbus_argument args[0];
+};
+
+/**
+ * struct wpa_dbus_property_desc - DBus property description
+ */
+struct wpa_dbus_property_desc {
+	/* pointer to next description in list */
+	struct wpa_dbus_property_desc *next;
+
+	/* property interface */
+	char *dbus_interface;
+	/* property name */
+	char *dbus_property;
+	/* property type signature in DBus type notation */
+	char *type;
+
+	/* property access permissions */
+	enum dbus_prop_access access;
+
+	/* property getter function */
+	WPADBusPropertyAccessor getter;
+	/* property setter function */
+	WPADBusPropertyAccessor setter;
+};
+
+
 #ifndef SIGPOLL
 #ifdef SIGIO
 /*
@@ -118,4 +181,7 @@ void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
 				    const char *path, const char *interface,
 				    DBusMessageIter *dict_iter);
 
+DBusMessage * wpa_dbus_introspect(DBusMessage *message,
+				  struct wpa_dbus_object_desc *obj_dsc);
+
 #endif /* WPA_DBUS_CTRL_H */

+ 454 - 0
wpa_supplicant/dbus/dbus_new_introspect.c

@@ -0,0 +1,454 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+#include <libxml/tree.h>
+
+#include "utils/common.h"
+#include "dbus_common_i.h"
+#include "dbus_new_helpers.h"
+
+
+struct interfaces {
+	struct interfaces *next;
+	char *dbus_interface;
+	xmlNodePtr interface_node;
+};
+
+/**
+ * extract_interfaces - Extract interfaces from methods, signals and props
+ * @obj_dsc: Description of object from which interfaces will be extracted
+ * @root_node: root node of XML introspection document
+ * Returns: List of interfaces found in object description
+ *
+ * Iterates over all methods, signals and properties registered with
+ * object and collects all declared DBus interfaces and create interface's
+ * node in XML root node for each. Returned list elements contains interface
+ * name and XML node of corresponding interface.
+ */
+static struct interfaces * extract_interfaces(
+	struct wpa_dbus_object_desc *obj_dsc, xmlNodePtr root_node)
+{
+	struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
+	struct wpa_dbus_signal_desc *signal_dsc = obj_dsc->signals;
+	struct wpa_dbus_property_desc *property_dsc = obj_dsc->properties;
+	struct interfaces *head = NULL;
+	struct interfaces *iface, *last;
+	int len;
+
+	/* extract interfaces from methods */
+	while (method_dsc) {
+		iface = head;
+		last = NULL;
+
+		/* go to next method if its interface is already extracted */
+		while (iface) {
+			if (!os_strcmp(iface->dbus_interface,
+				       method_dsc->dbus_interface))
+				break;
+			last = iface;
+			iface = iface->next;
+		}
+		if (iface) {
+			method_dsc = method_dsc->next;
+			continue;
+		}
+
+		iface = os_zalloc(sizeof(struct interfaces));
+		if (!iface) {
+			wpa_printf(MSG_ERROR, "Not enough memory to create "
+				"interface introspection data");
+			method_dsc = method_dsc->next;
+			continue;
+		}
+
+		if (last)
+			last->next = iface;
+		else
+			head = iface;
+
+		len = os_strlen(method_dsc->dbus_interface) + 1;
+		iface->dbus_interface = os_malloc(len);
+		if (!iface->dbus_interface) {
+			wpa_printf(MSG_ERROR, "Not enough memory to create "
+				   "interface introspection data (interface "
+				   "name)");
+			method_dsc = method_dsc->next;
+			continue;
+		}
+		os_strncpy(iface->dbus_interface, method_dsc->dbus_interface,
+			   len);
+
+		iface->interface_node = xmlNewChild(root_node, NULL,
+						    BAD_CAST "interface",
+						    NULL);
+		xmlNewProp(iface->interface_node, BAD_CAST "name",
+			   BAD_CAST method_dsc->dbus_interface);
+
+		method_dsc = method_dsc->next;
+	}
+
+	/* extract interfaces from signals */
+	while (signal_dsc) {
+		iface = head;
+		last = NULL;
+
+		/* go to next signal if its interface is already extracted */
+		while (iface) {
+			if (!os_strcmp(iface->dbus_interface,
+				       signal_dsc->dbus_interface))
+				break;
+			last = iface;
+			iface = iface->next;
+		}
+		if (iface) {
+			signal_dsc = signal_dsc->next;
+			continue;
+		}
+
+		iface = os_zalloc(sizeof(struct interfaces));
+		if (!iface) {
+			wpa_printf(MSG_ERROR, "Not enough memory to create "
+				   "interface introspection data");
+			signal_dsc = signal_dsc->next;
+			continue;
+		}
+
+		if (last)
+			last->next = iface;
+		else
+			head = iface;
+
+		len = os_strlen(signal_dsc->dbus_interface) + 1;
+		iface->dbus_interface = os_malloc(len);
+		if (!iface->dbus_interface) {
+			wpa_printf(MSG_ERROR, "Not enough memory to create "
+				   "interface introspection data (interface "
+				   "name)");
+			signal_dsc = signal_dsc->next;
+			continue;
+		}
+		os_strncpy(iface->dbus_interface, signal_dsc->dbus_interface,
+			   len);
+
+		iface->interface_node = xmlNewChild(root_node, NULL,
+						    BAD_CAST "interface",
+						    NULL);
+		xmlNewProp(iface->interface_node, BAD_CAST "name",
+			   BAD_CAST signal_dsc->dbus_interface);
+
+		signal_dsc = signal_dsc->next;
+	}
+
+	/* extract interfaces from properties */
+	while (property_dsc) {
+		iface = head;
+		last = NULL;
+
+		/* go to next property if its interface is already extracted */
+		while (iface) {
+			if (!os_strcmp(iface->dbus_interface,
+				       property_dsc->dbus_interface))
+				break;
+			last = iface;
+			iface = iface->next;
+		}
+		if (iface) {
+			property_dsc = property_dsc->next;
+			continue;
+		}
+
+		iface = os_zalloc(sizeof(struct interfaces));
+		if (!iface) {
+			wpa_printf(MSG_ERROR, "Not enough memory to create "
+				   "interface introspection data");
+			property_dsc = property_dsc->next;
+			continue;
+		}
+
+		if (last)
+			last->next = iface;
+		else
+			head = iface;
+
+		len = os_strlen(property_dsc->dbus_interface) + 1;
+		iface->dbus_interface = os_malloc(len);
+		if (!iface->dbus_interface) {
+			wpa_printf(MSG_ERROR, "Not enough memory to create "
+				   "interface introspection data (interface "
+				   "name)");
+			property_dsc = property_dsc->next;
+			continue;
+		}
+		os_strncpy(iface->dbus_interface, property_dsc->dbus_interface,
+			   len);
+
+		iface->interface_node = xmlNewChild(root_node, NULL,
+						    BAD_CAST "interface",
+						    NULL);
+		xmlNewProp(iface->interface_node, BAD_CAST "name",
+			   BAD_CAST property_dsc->dbus_interface);
+
+		property_dsc = property_dsc->next;
+	}
+
+	return head;
+}
+
+
+/**
+ * wpa_dbus_introspect - Responds for Introspect calls on object
+ * @message: Message with Introspect call
+ * @obj_dsc: Object description on which Introspect was called
+ * Returns: Message with introspection result XML string as only argument
+ *
+ * Iterates over all methods, signals and properties registered with
+ * object and generates introspection data for the object as XML string.
+ */
+DBusMessage * wpa_dbus_introspect(DBusMessage *message,
+				  struct wpa_dbus_object_desc *obj_dsc)
+{
+
+	DBusMessage *reply;
+	struct interfaces *ifaces, *tmp;
+	struct wpa_dbus_signal_desc *signal_dsc;
+	struct wpa_dbus_method_desc *method_dsc;
+	struct wpa_dbus_property_desc *property_dsc;
+	xmlChar *intro_str;
+	char **children;
+	int i, s;
+
+	xmlDocPtr doc = NULL;
+	xmlNodePtr root_node = NULL, node = NULL, iface_node = NULL;
+	xmlNodePtr method_node = NULL, signal_node = NULL;
+	xmlNodePtr property_node = NULL, arg_node = NULL;
+
+	/* root node and dtd */
+	doc = xmlNewDoc(BAD_CAST "1.0");
+	root_node = xmlNewNode(NULL, BAD_CAST "node");
+	xmlDocSetRootElement(doc, root_node);
+	xmlCreateIntSubset(doc, BAD_CAST "node",
+			   BAD_CAST DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER,
+			   BAD_CAST DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER);
+
+	/* Add Introspectable interface */
+	iface_node = xmlNewChild(root_node, NULL, BAD_CAST "interface", NULL);
+	xmlNewProp(iface_node, BAD_CAST "name",
+		   BAD_CAST WPA_DBUS_INTROSPECTION_INTERFACE);
+
+	/* Add Introspect method */
+	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
+	xmlNewProp(method_node, BAD_CAST "name",
+		   BAD_CAST WPA_DBUS_INTROSPECTION_METHOD);
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "data");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "out");
+
+
+	/* Add Properties interface */
+	iface_node = xmlNewChild(root_node, NULL,
+				 BAD_CAST "interface", NULL);
+	xmlNewProp(iface_node, BAD_CAST "name",
+		   BAD_CAST WPA_DBUS_PROPERTIES_INTERFACE);
+
+	/* Add Get method */
+	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
+	xmlNewProp(method_node, BAD_CAST "name",
+		   BAD_CAST WPA_DBUS_PROPERTIES_GET);
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "interface");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "propname");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "value");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "v");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "out");
+	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
+
+	/* Add GetAll method */
+	xmlNewProp(method_node, BAD_CAST "name",
+		   BAD_CAST WPA_DBUS_PROPERTIES_GETALL);
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "interface");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "props");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "a{sv}");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "out");
+	method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method", NULL);
+
+	/* Add Set method */
+	xmlNewProp(method_node, BAD_CAST "name",
+		   BAD_CAST WPA_DBUS_PROPERTIES_SET);
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "interface");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "propname");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "s");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
+	arg_node = xmlNewChild(method_node, NULL, BAD_CAST "arg", NULL);
+	xmlNewProp(arg_node, BAD_CAST "name", BAD_CAST "value");
+	xmlNewProp(arg_node, BAD_CAST "type", BAD_CAST "v");
+	xmlNewProp(arg_node, BAD_CAST "direction", BAD_CAST "in");
+
+	/* get all interfaces registered with object */
+	ifaces = extract_interfaces(obj_dsc, root_node);
+
+	/* create methods' nodes */
+	method_dsc = obj_dsc->methods;
+	while (method_dsc) {
+
+		struct interfaces *iface = ifaces;
+		while (iface) {
+			if (!os_strcmp(iface->dbus_interface,
+				       method_dsc->dbus_interface))
+				break;
+			iface = iface->next;
+		}
+		if (!iface)
+			continue;
+
+		iface_node = iface->interface_node;
+		method_node = xmlNewChild(iface_node, NULL, BAD_CAST "method",
+					  NULL);
+		xmlNewProp(method_node, BAD_CAST "name",
+			   BAD_CAST method_dsc->dbus_method);
+
+		/* create args' nodes */
+		for (i = 0; i < method_dsc->args_num; i++) {
+			struct wpa_dbus_argument arg = method_dsc->args[i];
+			arg_node = xmlNewChild(method_node, NULL,
+					       BAD_CAST "arg", NULL);
+			if (arg.name && strlen(arg.name)) {
+				xmlNewProp(arg_node, BAD_CAST "name",
+					   BAD_CAST arg.name);
+			}
+			xmlNewProp(arg_node, BAD_CAST "type",
+				   BAD_CAST arg.type);
+			xmlNewProp(arg_node, BAD_CAST "direction",
+				   BAD_CAST (arg.dir == ARG_IN ?
+					     "in" : "out"));
+		}
+		method_dsc = method_dsc->next;
+	}
+
+	/* create signals' nodes */
+	signal_dsc = obj_dsc->signals;
+	while (signal_dsc) {
+
+		struct interfaces *iface = ifaces;
+		while (iface) {
+			if (!os_strcmp(iface->dbus_interface,
+				       signal_dsc->dbus_interface))
+				break;
+			iface = iface->next;
+		}
+		if (!iface)
+			continue;
+
+		iface_node = iface->interface_node;
+		signal_node = xmlNewChild(iface_node, NULL, BAD_CAST "signal",
+					  NULL);
+		xmlNewProp(signal_node, BAD_CAST "name",
+			   BAD_CAST signal_dsc->dbus_signal);
+
+		/* create args' nodes */
+		for (i = 0; i < signal_dsc->args_num; i++) {
+			struct wpa_dbus_argument arg = signal_dsc->args[i];
+			arg_node = xmlNewChild(signal_node, NULL,
+					       BAD_CAST "arg", NULL);
+			if (arg.name && strlen(arg.name)) {
+				xmlNewProp(arg_node, BAD_CAST "name",
+					   BAD_CAST arg.name);
+			}
+			xmlNewProp(arg_node, BAD_CAST "type",
+				   BAD_CAST arg.type);
+		}
+		signal_dsc = signal_dsc->next;
+	}
+
+	/* create properties' nodes */
+	property_dsc = obj_dsc->properties;
+	while (property_dsc) {
+
+		struct interfaces *iface = ifaces;
+		while (iface) {
+			if (!os_strcmp(iface->dbus_interface,
+				       property_dsc->dbus_interface))
+				break;
+			iface = iface->next;
+		}
+		if (!iface)
+			continue;
+
+		iface_node = iface->interface_node;
+		property_node = xmlNewChild(iface_node, NULL,
+					    BAD_CAST "property", NULL);
+		xmlNewProp(property_node, BAD_CAST "name",
+			   BAD_CAST property_dsc->dbus_property);
+		xmlNewProp(property_node, BAD_CAST "type",
+			   BAD_CAST property_dsc->type);
+		xmlNewProp(property_node, BAD_CAST "access", BAD_CAST
+			   (property_dsc->access == R ? "read" :
+			    (property_dsc->access == W ?
+			     "write" : "readwrite")));
+
+		property_dsc = property_dsc->next;
+	}
+
+	/* add child nodes to introspection tree; */
+	dbus_connection_list_registered(obj_dsc->connection,
+					dbus_message_get_path(message),
+					&children);
+	for (i = 0; children[i]; i++) {
+		node = xmlNewChild(root_node, NULL, BAD_CAST "node", NULL);
+		xmlNewProp(node, BAD_CAST "name", BAD_CAST children[i]);
+	}
+	dbus_free_string_array(children);
+
+
+	xmlDocDumpFormatMemory(doc, &intro_str, &s, 1);
+
+	xmlFreeDoc(doc);
+
+	while (ifaces) {
+		tmp = ifaces;
+		ifaces = ifaces->next;
+		os_free(tmp->dbus_interface);
+		os_free(tmp);
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL) {
+		xmlFree(intro_str);
+		return NULL;
+	}
+
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
+				 DBUS_TYPE_INVALID);
+
+	xmlFree(intro_str);
+
+	return reply;
+}
+