Handle WiFi authentication using an agent

Register an agent within the applet which shows an appropriate dialog
when credentials are requested upon connecting to a secured wireless
network.

Thanks to Julien Massot for providing the underlying agent library code.
diff --git a/applet/Makefile.am b/applet/Makefile.am
index fe582ef..2e7c157 100644
--- a/applet/Makefile.am
+++ b/applet/Makefile.am
@@ -2,7 +2,8 @@
 bin_PROGRAMS = connman-applet
 
 connman_applet_SOURCES = main.c \
-	properties.h properties.c status.h status.c
+	properties.h properties.c status.h \
+	status.c agent.h agent.c
 
 connman_applet_LDADD = $(top_builddir)/common/libcommon.a \
 					@GTK_LIBS@ @DBUS_LIBS@
diff --git a/applet/agent.c b/applet/agent.c
new file mode 100644
index 0000000..65bed08
--- /dev/null
+++ b/applet/agent.c
@@ -0,0 +1,209 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Agent implementation based on code from bluez-gnome
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess@hadess.net>
+ *  Copyright (C) 2012  Intel Corporation
+ *
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <connman-agent.h>
+
+#include "agent.h"
+
+struct input_data {
+	gboolean numeric;
+	gpointer request_data;
+	GtkWidget *dialog;
+	GHashTable *entries;
+};
+
+static struct input_data *input_data_inst = NULL;
+
+static void input_free(struct input_data *input)
+{
+	gtk_widget_destroy(input->dialog);
+
+	g_hash_table_destroy(input->entries);
+
+	if( input_data_inst == input )
+		input_data_inst = NULL;
+
+	g_free(input);
+}
+
+static void request_input_callback(GtkWidget *dialog,
+				gint response, gpointer user_data)
+{
+	GHashTableIter iter;
+	gpointer key, value;
+	GValue *retvalue = NULL;
+	const gchar *text;
+	struct input_data *input = user_data;
+
+	if (response == GTK_RESPONSE_OK) {
+		GHashTable *reply = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+		g_hash_table_iter_init (&iter, input->entries);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			text = gtk_entry_get_text((GtkEntry *)value);
+			if(strlen(text)) {
+				retvalue = g_slice_new0(GValue);
+				g_value_init(retvalue, G_TYPE_STRING);
+				g_value_set_string(retvalue, text);
+				g_hash_table_insert(reply, g_strdup(key), retvalue);
+			}
+		}
+
+		connman_agent_request_input_set_reply(input->request_data, reply);
+	} else {
+		connman_agent_request_input_abort(input->request_data);
+	}
+
+	input_free(input);
+}
+
+static void show_dialog(gpointer data, gpointer user_data)
+{
+	struct input_data *input = data;
+
+	gtk_widget_show_all(input->dialog);
+
+	gtk_window_present(GTK_WINDOW(input->dialog));
+}
+
+static void request_input_dialog(GHashTable *request,
+						gpointer request_data)
+{
+	GtkWidget *dialog;
+	GtkWidget *label;
+	GtkWidget *table;
+	GtkWidget *entry;
+	struct input_data *input;
+	GHashTableIter iter;
+	gpointer key, value;
+	int elems, i;
+
+	input = g_try_malloc0(sizeof(*input));
+	if (!input)
+		return;
+
+	input->request_data = request_data;
+
+	input->entries = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+	dialog = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(dialog), _("Connection Manager"));
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
+	gtk_window_set_urgency_hint(GTK_WINDOW(dialog), TRUE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	input->dialog = dialog;
+
+	gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_OK, GTK_RESPONSE_OK);
+
+	elems = g_hash_table_size(request);
+	table = gtk_table_new(elems+1, 2, FALSE);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 20);
+	gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
+
+	label = gtk_label_new(_("Please provide some network information:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_table_attach(GTK_TABLE(table), label, 0, 2, 0, 1,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	g_hash_table_iter_init (&iter, request);
+	i=1;
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		label = gtk_label_new((const char *)key);
+		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+		gtk_table_attach(GTK_TABLE(table), label, 0, 1, i, i+1,
+					GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+		entry = gtk_entry_new();
+		gtk_entry_set_max_length(GTK_ENTRY(entry), 64);
+		gtk_entry_set_width_chars(GTK_ENTRY(entry), 16);
+		gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+		gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i, i+1,
+					GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+		g_hash_table_insert(input->entries, g_strdup(key), entry);
+
+		i++;
+	}
+
+	input_data_inst = input;
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+				G_CALLBACK(request_input_callback), input);
+
+	show_dialog(input, NULL);
+}
+
+static void request_input(const char *service_id,
+                              GHashTable *request, gpointer request_data, gpointer user_data)
+{
+	request_input_dialog(request, request_data);
+}
+
+static gboolean cancel_request(DBusGMethodInvocation *context,
+							gpointer user_data)
+{
+	if( input_data_inst ) {
+		connman_agent_request_input_abort(input_data_inst->request_data);
+
+		input_free(input_data_inst);
+	}
+
+	return TRUE;
+}
+
+int setup_agents(void)
+{
+	ConnmanAgent *agent = connman_agent_new();
+	connman_agent_setup(agent, "/org/gnome/connman/applet");
+
+	connman_agent_set_request_input_func(agent, request_input, agent);
+	connman_agent_set_cancel_func(agent, cancel_request, agent);
+
+	connman_agent_register(agent);
+
+	return 0;
+}
+
+void cleanup_agents(void)
+{
+}
diff --git a/applet/agent.h b/applet/agent.h
new file mode 100644
index 0000000..d85676b
--- /dev/null
+++ b/applet/agent.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Agent implementation based on code from bluez-gnome
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess@hadess.net>
+ *  Copyright (C) 2012  Intel Corporation
+ *
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int setup_agents(void);
+void cleanup_agents(void);
diff --git a/applet/main.c b/applet/main.c
index 0cf82bf..f12d371 100644
--- a/applet/main.c
+++ b/applet/main.c
@@ -32,6 +32,7 @@
 
 #include "properties.h"
 #include "status.h"
+#include "agent.h"
 
 static gboolean global_ready = FALSE;
 static gint global_strength = -1;
@@ -132,6 +133,7 @@
 					"/", "net.connman.Manager");
 
 	properties_create(manager, manager_property_changed, NULL);
+	setup_agents();
 }
 
 static void manager_cleanup(void)
@@ -148,6 +150,7 @@
 	if (*new != '\0') {
 		status_offline();
 		properties_enable(manager);
+		setup_agents();
 	} else {
 		properties_disable(manager);
 		status_unavailable();
diff --git a/common/Makefile.am b/common/Makefile.am
index ef1267a..5bfff19 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -3,19 +3,21 @@
 
 libcommon_a_SOURCES = connman-dbus.c connman-dbus.h connman-dbus-glue.h \
 				connman-client.h connman-client.c \
-							instance.h instance.c
+							instance.h instance.c \
+				connman-agent.h connman-agent.c
 
 BUILT_SOURCES = marshal.h marshal.c \
 			connman-dbus-glue.h \
-				instance-glue.h
+				instance-glue.h \
+				connman-agent-glue.h
 
-nodist_libcommon_a_SOURCES = connman-dbus-glue.h instance-glue.h
+nodist_libcommon_a_SOURCES = connman-dbus-glue.h instance-glue.h connman-agent-glue.h
 
 CLEANFILES = $(BUILT_SOURCES)
 
 AM_CFLAGS = @DBUS_CFLAGS@ @GTK_CFLAGS@
 
-EXTRA_DIST = marshal.list instance.xml connman-dbus.xml
+EXTRA_DIST = marshal.list instance.xml connman-dbus.xml connman-agent.xml
 
 MAINTAINERCLEANFILES = Makefile.in
 
@@ -30,3 +32,6 @@
 
 connman-dbus-glue.h: connman-dbus.xml
 	$(DBUS_BINDING_TOOL) --prefix=connman --mode=glib-client --output=$@ $<
+
+connman-agent-glue.h: connman-agent.xml
+	$(DBUS_BINDING_TOOL) --prefix=connman_agent --mode=glib-server --output=$@ $<
diff --git a/common/connman-agent.c b/common/connman-agent.c
new file mode 100644
index 0000000..ef9ed1c
--- /dev/null
+++ b/common/connman-agent.c
@@ -0,0 +1,426 @@
+/*
+ *  Connection Manager Agent implementation
+ *
+ *  Author(s):
+ *  - Julien MASSOT <jmassot@aldebaran-robotics.com>
+ *  - Paul Eggleton <paul.eggleton@linux.intel.com>
+ *
+ *  Copyright (C) 2012 Aldebaran Robotics
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <stdio.h>
+
+#include "connman-agent.h"
+#include "connman-dbus.h"
+
+#define CONNMAN_AGENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+  CONNMAN_TYPE_AGENT, ConnmanAgentPrivate))
+
+typedef enum {
+  AGENT_ERROR_REJECT,
+  AGENT_ERROR_RETRY
+} AgentError;
+
+#define AGENT_ERROR (agent_error_quark())
+
+#define AGENT_ERROR_TYPE (agent_error_get_type())
+
+static GQuark agent_error_quark(void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string("Agent");
+
+	return quark;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType agent_error_get_type(void)
+{
+	static GType etype = 0;
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY(AGENT_ERROR_REJECT, "Rejected"),
+			ENUM_ENTRY(AGENT_ERROR_RETRY, "Retry"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static("Agent", values);
+	}
+
+	return etype;
+}
+
+typedef struct _ConnmanAgentPrivate ConnmanAgentPrivate;
+
+typedef struct _PendingRequest PendingRequest;
+
+struct _PendingRequest {
+	DBusGMethodInvocation *context;
+	ConnmanAgent *agent;
+};
+
+struct _ConnmanAgentPrivate {
+	gchar *busname;
+	gchar *path;
+	DBusGConnection *connection;
+	DBusGProxy *connman_proxy;
+
+	ConnmanAgentRequestInputFunc input_func;
+	gpointer input_data;
+
+	ConnmanAgentCancelFunc cancel_func;
+	gpointer cancel_data;
+
+	ConnmanAgentReleaseFunc release_func;
+	gpointer release_data;
+
+	ConnmanAgentDebugFunc debug_func;
+	gpointer debug_data;
+
+};
+
+G_DEFINE_TYPE(ConnmanAgent, connman_agent, G_TYPE_OBJECT)
+
+static inline void debug(ConnmanAgent *agent, const char *format, ...)
+{
+	char str[256];
+	va_list ap;
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	if (priv->debug_func == NULL)
+		return;
+
+	va_start(ap, format);
+
+	if (vsnprintf(str, sizeof(str), format, ap) > 0)
+		priv->debug_func(str, priv->debug_data);
+
+	va_end(ap);
+}
+
+gboolean connman_agent_request_input_set_reply(gpointer request_data, GHashTable *reply)
+{
+	PendingRequest *pendingrequest = request_data;
+
+	if (request_data == NULL)
+		return FALSE;
+
+	dbus_g_method_return(pendingrequest->context, reply);
+
+	g_free(pendingrequest);
+
+	return FALSE;
+}
+
+gboolean connman_agent_request_input_abort(gpointer request_data)
+{
+	PendingRequest *pendingrequest = request_data;
+	GError *result;
+	if (request_data == NULL)
+		return FALSE;
+
+	result = g_error_new(AGENT_ERROR, AGENT_ERROR_REJECT,
+	                     "Input request rejected");
+	dbus_g_method_return_error(pendingrequest->context, result);
+	g_clear_error(&result);
+	g_free(pendingrequest);
+
+	return FALSE;
+}
+
+static gboolean connman_agent_request_input_cb(const GHashTable *reply, gpointer user_data)
+{
+
+	PendingRequest *pendingrequest = user_data;
+
+	dbus_g_method_return(pendingrequest->context, reply);
+
+	g_free(pendingrequest);
+	return FALSE;
+}
+
+gboolean connman_agent_report_error(ConnmanAgent *agent,
+                                    const char *path, const char *error,
+                                    DBusGMethodInvocation *context)
+{
+	GError *result;
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	debug(agent, "connection %s, reports an error: %s", path, error);
+	result = g_error_new(AGENT_ERROR, AGENT_ERROR_RETRY,
+	                     "Retry");
+	dbus_g_method_return_error(context, result);
+	g_clear_error(&result);
+
+	return FALSE;
+}
+
+gboolean connman_agent_request_input(ConnmanAgent *agent,
+                                     const char *path, GHashTable *fields,
+                                     DBusGMethodInvocation *context)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	char *name = NULL, *type = NULL;
+	char **id = NULL;
+	PendingRequest *pendingrequest = NULL;
+
+	debug(agent, "request %s, sender %s", path, sender);
+
+	if (fields == NULL)
+		return FALSE;
+
+	if (priv->input_func != NULL) {
+		id = g_strsplit(path, "/net/connman/service/", 2);
+		if (g_strv_length(id) == 2) {
+			pendingrequest = g_try_new0(PendingRequest, 1);
+			pendingrequest->context = context;
+			pendingrequest->agent   = agent;
+			priv->input_func(id[1], fields, pendingrequest, priv->input_data);
+		}
+		g_strfreev(id);
+	}
+
+	return FALSE;
+}
+
+gboolean connman_agent_cancel(ConnmanAgent *agent,
+                              DBusGMethodInvocation *context)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	gboolean result = FALSE;
+
+	debug(agent, "Request Canceled %s", sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->cancel_func)
+		result = priv->cancel_func(context, priv->cancel_data);
+
+	return result;
+}
+
+gboolean connman_agent_release(ConnmanAgent *agent,
+                               DBusGMethodInvocation *context)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+
+	debug(agent, "agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+#include "connman-agent-glue.h"
+
+static void connman_agent_init(ConnmanAgent *agent)
+{
+  debug(agent, "agent %p", agent);
+}
+
+static void connman_agent_finalize(GObject *agent)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	if (priv->connman_proxy != NULL) {
+		g_object_unref(priv->connman_proxy);
+	}
+
+	g_free(priv->path);
+	g_free(priv->busname);
+	dbus_g_connection_unref(priv->connection);
+
+	G_OBJECT_CLASS(connman_agent_parent_class)->finalize(agent);
+}
+
+static void connman_agent_class_init(ConnmanAgentClass *klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+
+	g_type_class_add_private(klass, sizeof(ConnmanAgentPrivate));
+
+	object_class->finalize = connman_agent_finalize;
+
+	dbus_g_object_type_install_info(CONNMAN_TYPE_AGENT,
+	                                &dbus_glib_connman_agent_object_info);
+}
+
+ConnmanAgent *connman_agent_new(void)
+{
+	ConnmanAgent *agent;
+	g_type_init();
+
+	agent = CONNMAN_AGENT(g_object_new(CONNMAN_TYPE_AGENT, NULL));
+
+	return agent;
+}
+
+gboolean connman_agent_setup(ConnmanAgent *agent, const char *path)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+	DBusGProxy *proxy;
+	GObject *object;
+	GError *error = NULL;
+
+	debug(agent, "agent_setup %p", agent);
+
+	if (priv->path != NULL)
+		return FALSE;
+
+	priv->path = g_strdup(path);
+	priv->connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+	if (error != NULL) {
+		g_printerr("Connecting to system bus failed: %s\n",
+		           error->message);
+		g_error_free(error);
+		return FALSE;
+	}
+
+	proxy = dbus_g_proxy_new_for_name_owner(priv->connection, CONNMAN_SERVICE,
+	                                        CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, NULL);
+
+	g_free(priv->busname);
+
+	if (proxy != NULL) {
+		priv->busname = g_strdup(dbus_g_proxy_get_bus_name(proxy));
+		g_object_unref(proxy);
+	} else
+		priv->busname = NULL;
+
+	object = dbus_g_connection_lookup_g_object(priv->connection, priv->path);
+	if (object != NULL)
+		g_object_unref(object);
+
+	return TRUE;
+}
+
+
+gboolean connman_agent_register(ConnmanAgent *agent)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+	DBusGProxy *proxy;
+	GObject *object;
+	GError *error = NULL;
+	gchar *path;
+
+	debug(agent, "register agent %p", agent);
+
+	if (priv->connman_proxy != NULL)
+		return FALSE;
+
+	priv->connman_proxy = dbus_g_proxy_new_for_name_owner(priv->connection, CONNMAN_SERVICE,
+	                                                      CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, NULL);
+
+	g_free(priv->busname);
+
+	priv->busname = g_strdup(dbus_g_proxy_get_bus_name(priv->connman_proxy));
+
+	object = dbus_g_connection_lookup_g_object(priv->connection, priv->path);
+	if (object != NULL)
+		g_object_unref(object);
+
+	dbus_g_connection_register_g_object(priv->connection,
+	                                    priv->path, G_OBJECT(agent));
+
+	dbus_g_proxy_call(priv->connman_proxy, "RegisterAgent", &error,
+	                  DBUS_TYPE_G_OBJECT_PATH, priv->path,
+	                  G_TYPE_INVALID, G_TYPE_INVALID);
+
+	if (error != NULL) {
+		g_printerr("Agent registration failed: %s\n",
+		           error->message);
+		g_error_free(error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean connman_agent_unregister(ConnmanAgent *agent)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+	GError *error = NULL;
+
+	debug(agent, "unregister agent %p", agent);
+
+	if (priv->connman_proxy == NULL)
+		return FALSE;
+
+	dbus_g_proxy_call(priv->connman_proxy, "UnregisterAgent", &error,
+	                  DBUS_TYPE_G_OBJECT_PATH, priv->path,
+	                  G_TYPE_INVALID, G_TYPE_INVALID);
+
+	if (error != NULL) {
+		g_printerr("Agent unregistration failed: %s\n",
+		           error->message);
+		g_error_free(error);
+	}
+
+	g_object_unref(priv->connman_proxy);
+	priv->connman_proxy = NULL;
+
+	g_free(priv->path);
+	priv->path = NULL;
+
+	return TRUE;
+}
+
+void connman_agent_set_request_input_func(ConnmanAgent *agent,
+                                          ConnmanAgentRequestInputFunc func, gpointer data)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	priv->input_func = func;
+	priv->input_data = data;
+}
+
+void connman_agent_set_cancel_func(ConnmanAgent *agent,
+                                   ConnmanAgentCancelFunc func, gpointer data)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	priv->cancel_func = func;
+	priv->cancel_data = data;
+}
+
+void connman_agent_set_release_func(ConnmanAgent *agent,
+                                    ConnmanAgentReleaseFunc func, gpointer data)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	priv->release_func = func;
+	priv->release_data = data;
+}
+
+void connman_agent_set_debug_func(ConnmanAgent *agent, ConnmanAgentDebugFunc func, gpointer data)
+{
+	ConnmanAgentPrivate *priv = CONNMAN_AGENT_GET_PRIVATE(agent);
+
+	priv->debug_func = func;
+	priv->debug_data = data;
+}
diff --git a/common/connman-agent.h b/common/connman-agent.h
new file mode 100644
index 0000000..f9941f0
--- /dev/null
+++ b/common/connman-agent.h
@@ -0,0 +1,77 @@
+/*
+ *  Connection Manager Agent implementation
+ *
+ *  Author(s):
+ *  - Julien MASSOT <jmassot@aldebaran-robotics.com>
+ *  - Paul Eggleton <paul.eggleton@linux.intel.com>
+ *
+ *  Copyright (C) 2012 Aldebaran Robotics
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef   	CONNMAN_AGENT_H_
+# define   	CONNMAN_AGENT_H_
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define CONNMAN_TYPE_AGENT (connman_agent_get_type())
+#define CONNMAN_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                        CONNMAN_TYPE_AGENT, ConnmanAgent))
+#define CONNMAN_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                        CONNMAN_TYPE_AGENT, ConnmanAgentClass))
+#define CONNMAN_IS_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                                        CONNMAN_TYPE_AGENT))
+#define CONNMAN_IS_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+                                                        CONNMAN_TYPE_AGENT))
+#define CONNMAN_GET_AGENT_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+                                        CONNMAN_TYPE_AGENT, ConnmanAgentClass))
+
+typedef struct _ConnmanAgent ConnmanAgent;
+typedef struct _ConnmanAgentClass ConnmanAgentClass;
+
+struct _ConnmanAgent {
+	GObject parent;
+};
+
+struct _ConnmanAgentClass {
+	GObjectClass parent_class;
+};
+
+GType connman_agent_get_type(void);
+
+ConnmanAgent *connman_agent_new(void);
+
+gboolean connman_agent_setup(ConnmanAgent *agent, const char *path);
+
+gboolean connman_agent_register(ConnmanAgent *agent);
+gboolean connman_agent_unregister(ConnmanAgent *agent);
+gboolean connman_agent_request_input_set_reply(gpointer request_data, GHashTable *reply);
+gboolean connman_agent_request_input_abort(gpointer request_data);
+
+typedef void (*ConnmanAgentRequestInputFunc) (const char *service_id, GHashTable *request, gpointer request_data, gpointer user_data);
+typedef gboolean (*ConnmanAgentCancelFunc) (DBusGMethodInvocation *context, gpointer data);
+typedef gboolean (*ConnmanAgentReleaseFunc) (DBusGMethodInvocation *context, gpointer data);
+typedef void (*ConnmanAgentDebugFunc) (const char *str, gpointer user_data);
+
+void connman_agent_set_request_input_func(ConnmanAgent *agent, ConnmanAgentRequestInputFunc func, gpointer data);
+void connman_agent_set_cancel_func(ConnmanAgent *agent, ConnmanAgentCancelFunc func, gpointer data);
+void connman_agent_set_debug_func(ConnmanAgent *agent, ConnmanAgentDebugFunc func, gpointer data);
+
+G_END_DECLS
+#endif 	    /* !CONNMAN_AGENT_H_ */
diff --git a/common/connman-agent.xml b/common/connman-agent.xml
new file mode 100644
index 0000000..ed9ee8b
--- /dev/null
+++ b/common/connman-agent.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/net/connman/Agent">
+  <interface name="net.connman.Agent">
+    <method name="ReportError">
+       <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+       <arg type="o" direction="in"/>
+       <arg type="s" direction="in"/>
+    </method>
+
+    <method name="RequestInput">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="o" direction="in"/>
+      <arg type="a{sv}" direction="in"/>
+      <arg type="a{sv}" direction="out"/>
+    </method>
+
+    <method name="Cancel">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    </method>
+
+    <method name="Release">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+   </method>
+  </interface>
+</node>
diff --git a/common/marshal.list b/common/marshal.list
index 8b174d0..3c6317b 100644
--- a/common/marshal.list
+++ b/common/marshal.list
@@ -1,3 +1,5 @@
 VOID:STRING,BOXED
+VOID:OBJECT,BOXED
+VOID:OBJECT
 VOID:BOXED
 VOID:STRING