ADDRESS_BOOK_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.AddressBook4"
CALENDAR_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Calendar3"
SOURCES_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Sources1"
+USER_PROMPTER_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.UserPrompter0"
AC_DEFINE_UNQUOTED(
ADDRESS_BOOK_DBUS_SERVICE_NAME,
["$SOURCES_DBUS_SERVICE_NAME"],
[D-Bus service name for the source registry])
+AC_DEFINE_UNQUOTED(
+ USER_PROMPTER_DBUS_SERVICE_NAME,
+ ["$USER_PROMPTER_DBUS_SERVICE_NAME"],
+ [D-Bus service name for the user prompter])
+
AC_SUBST(ADDRESS_BOOK_DBUS_SERVICE_NAME)
AC_SUBST(CALENDAR_DBUS_SERVICE_NAME)
AC_SUBST(SOURCES_DBUS_SERVICE_NAME)
+AC_SUBST(USER_PROMPTER_DBUS_SERVICE_NAME)
dnl ******************************
dnl Libtool versioning
dnl D-BUS service stuff
dnl *******************
m4_pattern_allow([AM_V_GEN])
-EVO_SUBST_SERVICE_RULE='%.service: %.service.in Makefile ; $(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|" -e s"|\@ADDRESS_BOOK_DBUS_SERVICE_NAME\@|$(ADDRESS_BOOK_DBUS_SERVICE_NAME)|" -e "s|\@CALENDAR_DBUS_SERVICE_NAME\@|$(CALENDAR_DBUS_SERVICE_NAME)|" -e "s|\@SOURCES_DBUS_SERVICE_NAME\@|$(SOURCES_DBUS_SERVICE_NAME)|" $< > $@'
+EVO_SUBST_SERVICE_RULE='%.service: %.service.in Makefile ; $(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|" -e s"|\@ADDRESS_BOOK_DBUS_SERVICE_NAME\@|$(ADDRESS_BOOK_DBUS_SERVICE_NAME)|" -e "s|\@CALENDAR_DBUS_SERVICE_NAME\@|$(CALENDAR_DBUS_SERVICE_NAME)|" -e "s|\@SOURCES_DBUS_SERVICE_NAME\@|$(SOURCES_DBUS_SERVICE_NAME)|" -e "s|\@USER_PROMPTER_DBUS_SERVICE_NAME\@|$(USER_PROMPTER_DBUS_SERVICE_NAME)|" $< > $@'
AC_SUBST(EVO_SUBST_SERVICE_RULE)
dnl ******************************
modules/cache-reaper/Makefile
modules/gnome-online-accounts/Makefile
modules/google-backend/Makefile
+modules/trust-prompt/Makefile
modules/yahoo-backend/Makefile
private/Makefile
services/Makefile
services/evolution-addressbook-factory/Makefile
services/evolution-calendar-factory/Makefile
services/evolution-source-registry/Makefile
+services/evolution-user-prompter/Makefile
tests/Makefile
tests/libebook/Makefile
tests/libebook/client/Makefile
e-server-side-source.c \
e-source-registry-server.c \
e-sqlite3-vfs.c \
+ e-user-prompter.c \
+ e-user-prompter-server.c \
+ e-user-prompter-server-extension.c \
e-file-cache.c
libebackend_1_2_la_LIBADD = \
e-server-side-source.h \
e-source-registry-server.h \
e-sqlite3-vfs.h \
+ e-user-prompter.h \
+ e-user-prompter-server.h \
+ e-user-prompter-server-extension.h \
e-file-cache.h
%-$(API_VERSION).pc: %.pc
*
* <informalexample>
* <programlisting>
- * #include <libebackend/e-extensible.h>
+ * #include <libebackend/libebackend.h>
*
* G_DEFINE_TYPE_WITH_CODE (
* ECustomWidget, e_custom_widget, GTK_TYPE_WIDGET,
* <informalexample>
* <programlisting>
* static void
- * e_custom_widget_init (ECustomWidget *widget)
+ * e_custom_widget_constructed (ECustomWidget *widget)
* {
- * Initialization code goes here...
+ * Construction code goes here, same as call to parent's 'constructed'...
*
* e_extensible_load_extensions (E_EXTENSIBLE (widget));
* }
--- /dev/null
+/*
+ * e-user-prompter-server-extension.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-user-prompter-server-extension
+ * @short_description: Extension for a server-side user prompter
+ *
+ * The #EUserPrompterServerExtension is a base struct for extension
+ * of EUserPrompterServer, to provide customized or specialized dialog
+ * prompts.
+ *
+ * A descendant defines two virtual functions,
+ * the EUserPrompterServerExtensionClass::register_dialogs which is used as
+ * a convenient function, where the descendant registers all the dialogs it
+ * provides on the server with e_user_prompter_server_register().
+ *
+ * The next function is EUserPrompterServerExtensionClass::prompt, which is
+ * used to initiate user prompt. The implementor should not block main thread
+ * with this function, because this is treated fully asynchronously.
+ * User's response is passed to the server with
+ * e_user_prompter_server_extension_response() call.
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+
+#include "e-user-prompter-server-extension.h"
+
+#define E_USER_PROMPTER_SERVER_EXTENSION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER_EXTENSION, EUserPrompterServerExtensionPrivate))
+
+struct _EUserPrompterServerExtensionPrivate {
+ gint dummy; /* not used */
+};
+
+G_DEFINE_ABSTRACT_TYPE (EUserPrompterServerExtension, e_user_prompter_server_extension, E_TYPE_EXTENSION)
+
+static void
+user_prompter_server_extension_constructed (GObject *object)
+{
+ EExtensible *extensible;
+ EUserPrompterServer *server;
+ EExtension *extension;
+ EUserPrompterServerExtensionClass *klass;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_user_prompter_server_extension_parent_class)->constructed (object);
+
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER_EXTENSION (object));
+
+ extension = E_EXTENSION (object);
+ g_return_if_fail (extension != NULL);
+
+ extensible = e_extension_get_extensible (extension);
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER (extensible));
+
+ server = E_USER_PROMPTER_SERVER (extensible);
+
+ klass = E_USER_PROMPTER_SERVER_EXTENSION_GET_CLASS (extension);
+ g_return_if_fail (klass->register_dialogs);
+
+ klass->register_dialogs (extension, server);
+}
+
+static void
+e_user_prompter_server_extension_class_init (EUserPrompterServerExtensionClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ g_type_class_add_private (class, sizeof (EUserPrompterServerExtensionPrivate));
+
+ class->register_dialogs = NULL;
+ class->prompt = NULL;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = user_prompter_server_extension_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_USER_PROMPTER_SERVER;
+}
+
+static void
+e_user_prompter_server_extension_init (EUserPrompterServerExtension *extension)
+{
+ extension->priv = E_USER_PROMPTER_SERVER_EXTENSION_GET_PRIVATE (extension);
+}
+
+/**
+ * e_user_prompter_server_extension_prompt:
+ * @extension: an #EUserPrompterServerExtension
+ * @prompt_id: Prompt identificator, which is used in call to e_user_prompter_server_extension_response()
+ * @dialog_name: Name of a dialog to run
+ * @parameters: (allow-none): Optional extension parameters for the dialog, as passed by a caller
+ *
+ * Instructs extension to show dialog @dialog_name. If it cannot be found,
+ * or any error, then return %FALSE. The caller can pass optional @parameters,
+ * if @extension uses any. Meaning of @parameters is known only to the caller
+ * and to the dialog implementor, it's not interpretted nor checked for correctness
+ * in any way in #EUserPrompterServer. The only limitation of @parameters is that
+ * the array elements are strings.
+ *
+ * The @prompt_id is used as an identificator of the prompt itself,
+ * and is used in e_user_prompter_server_extension_response() call,
+ * which finishes the prompt.
+ *
+ * Note: The function call should not block main loop, it should
+ * just show dialog and return.
+ *
+ * Returns: Whether dialog was found and shown.
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_user_prompter_server_extension_prompt (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ const gchar *dialog_name,
+ const ENamedParameters *parameters)
+{
+ EUserPrompterServerExtensionClass *klass;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER_SERVER_EXTENSION (extension), FALSE);
+
+ klass = E_USER_PROMPTER_SERVER_EXTENSION_GET_CLASS (extension);
+ g_return_val_if_fail (klass->prompt != NULL, FALSE);
+
+ return klass->prompt (extension, prompt_id, dialog_name, parameters);
+}
+
+/**
+ * e_user_prompter_server_extension_response:
+ * @extension: an #EUserPrompterServerExtension
+ * @prompt_id: Prompt identificator
+ * @response: Response of the prompt
+ * @values: (allow-none): Additional response values, if extension defines any
+ *
+ * A conveniente wrapper function around e_user_prompter_server_response(),
+ * which ends previous call of e_user_prompter_server_extension_prompt().
+ * The @response and @values is known only to the caller and to the dialog implementor,
+ * it's not interpretted nor checked for correctness in any way in #EUserPrompterServer.
+ * The only limitation of @values is that the array elements are strings.
+ *
+ * Since: 3.8
+ **/
+void
+e_user_prompter_server_extension_response (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ gint response,
+ const ENamedParameters *values)
+{
+ EExtensible *extensible;
+ EUserPrompterServer *server;
+
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER_EXTENSION (extension));
+
+ extensible = e_extension_get_extensible (E_EXTENSION (extension));
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER (extensible));
+
+ server = E_USER_PROMPTER_SERVER (extensible);
+
+ e_user_prompter_server_response (server, prompt_id, response, values);
+}
--- /dev/null
+/*
+ * e-user-prompter-server-extension.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_USER_PROMPTER_SERVER_EXTENSION_H
+#define E_USER_PROMPTER_SERVER_EXTENSION_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_USER_PROMPTER_SERVER_EXTENSION \
+ (e_user_prompter_server_extension_get_type ())
+#define E_USER_PROMPTER_SERVER_EXTENSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER_EXTENSION, EUserPrompterServerExtension))
+#define E_USER_PROMPTER_SERVER_EXTENSION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_USER_PROMPTER_SERVER_EXTENSION, EUserPrompterServerExtensionClass))
+#define E_IS_USER_PROMPTER_SERVER_EXTENSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER_EXTENSION))
+#define E_IS_USER_PROMPTER_SERVER_EXTENSION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_USER_PROMPTER_SERVER_EXTENSION))
+#define E_USER_PROMPTER_SERVER_EXTENSION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER_EXTENSION, EUserPrompterServerExtensionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EUserPrompterServerExtension EUserPrompterServerExtension;
+typedef struct _EUserPrompterServerExtensionClass EUserPrompterServerExtensionClass;
+typedef struct _EUserPrompterServerExtensionPrivate EUserPrompterServerExtensionPrivate;
+
+/* Forward declaration for EUserPrompterServer object */
+struct _EUserPrompterServer;
+
+/**
+ * EUserPrompterServerExtension:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.8
+ **/
+struct _EUserPrompterServerExtension {
+ EExtension parent;
+ EUserPrompterServerExtensionPrivate *priv;
+};
+
+struct _EUserPrompterServerExtensionClass {
+ EExtensionClass parent_class;
+
+ /* virtual methods */
+ void (*register_dialogs) (EExtension *extension,
+ struct _EUserPrompterServer *server);
+ gboolean (*prompt) (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ const gchar *dialog_name,
+ const ENamedParameters *parameters);
+};
+
+GType e_user_prompter_server_extension_get_type (void);
+gboolean e_user_prompter_server_extension_prompt (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ const gchar *dialog_name,
+ const ENamedParameters *parameters);
+void e_user_prompter_server_extension_response (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ gint response,
+ const ENamedParameters *values);
+
+G_END_DECLS
+
+#endif /* E_USER_PROMPTER_SERVER_EXTENSION_H */
--- /dev/null
+/*
+ * e-user-prompter-server.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-user-prompter-server
+ * @short_description: Server-side user prompter
+ *
+ * The #EUserPrompterServer is the heart of the user prompter D-Bus service.
+ * Acting as a global singleton for user prompts from backends.
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/libedataserver.h>
+
+/* Private D-Bus classes. */
+#include "e-dbus-user-prompter.h"
+
+#include "libedataserver/e-marshal.h"
+
+#include "e-user-prompter-server.h"
+
+#define E_USER_PROMPTER_SERVER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER, EUserPrompterServerPrivate))
+
+struct _EUserPrompterServerPrivate {
+ EDBusUserPrompter *dbus_prompter;
+
+ GHashTable *extensions;
+
+ GRecMutex lock;
+ guint schedule_id;
+ GSList *prompts;
+ gint last_prompt_id;
+};
+
+enum {
+ PROMPT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE_WITH_CODE (EUserPrompterServer, e_user_prompter_server, E_TYPE_DBUS_SERVER,
+ G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+typedef struct _PromptRequest {
+ gint id;
+ gboolean is_extension_prompt;
+
+ /* 'Prompt' properties */
+ gchar *type;
+ gchar *title;
+ gchar *primary_text;
+ gchar *secondary_text;
+ gboolean use_markup;
+ GSList *button_captions;
+
+ /* 'ExtensionPrompt' properties */
+ gchar *dialog_name;
+ ENamedParameters *parameters;
+} PromptRequest;
+
+static void
+prompt_request_free (gpointer data)
+{
+ PromptRequest *pr = data;
+
+ if (pr) {
+ g_free (pr->type);
+ g_free (pr->title);
+ g_free (pr->primary_text);
+ g_free (pr->secondary_text);
+ g_slist_free_full (pr->button_captions, g_free);
+
+ g_free (pr->dialog_name);
+ e_named_parameters_free (pr->parameters);
+
+ g_free (pr);
+ }
+}
+
+static gint
+add_prompt (EUserPrompterServer *server,
+ gboolean is_extension_prompt,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const gchar *const *button_captions,
+ const gchar *dialog_name,
+ const gchar *const *parameters)
+{
+ PromptRequest *pr;
+ gint id;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER_SERVER (server), -1);
+
+ g_rec_mutex_lock (&server->priv->lock);
+
+ server->priv->last_prompt_id++;
+
+ pr = g_new0 (PromptRequest, 1);
+ pr->is_extension_prompt = is_extension_prompt;
+ pr->id = server->priv->last_prompt_id;
+ pr->type = g_strdup (type);
+ pr->title = g_strdup (title);
+ pr->primary_text = g_strdup (primary_text);
+ pr->secondary_text = g_strdup (secondary_text);
+ pr->use_markup = use_markup;
+ pr->button_captions = e_util_strv_to_slist (button_captions);
+ pr->dialog_name = g_strdup (dialog_name);
+ pr->parameters = parameters ? e_named_parameters_new_strv (parameters) : NULL;
+
+ server->priv->prompts = g_slist_append (server->priv->prompts, pr);
+
+ id = pr->id;
+
+ e_dbus_server_hold (E_DBUS_SERVER (server));
+
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ return id;
+}
+
+static gboolean
+remove_prompt (EUserPrompterServer *server,
+ gint prompt_id,
+ gboolean *is_extension_prompt)
+{
+ GSList *iter;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER_SERVER (server), FALSE);
+
+ g_rec_mutex_lock (&server->priv->lock);
+
+ for (iter = server->priv->prompts; iter; iter = g_slist_next (iter)) {
+ PromptRequest *pr = iter->data;
+
+ if (pr && pr->id == prompt_id) {
+ server->priv->prompts = g_slist_remove (server->priv->prompts, pr);
+
+ if (is_extension_prompt)
+ *is_extension_prompt = pr->is_extension_prompt;
+
+ prompt_request_free (pr);
+ e_dbus_server_release (E_DBUS_SERVER (server));
+
+ g_rec_mutex_unlock (&server->priv->lock);
+ return TRUE;
+ }
+ }
+
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ g_warn_if_reached ();
+
+ return FALSE;
+}
+
+static void
+do_show_prompt (EUserPrompterServer *server)
+{
+ PromptRequest *pr;
+
+ g_return_if_fail (server->priv->prompts != NULL);
+
+ pr = server->priv->prompts->data;
+ g_return_if_fail (pr != NULL);
+
+ if (pr->is_extension_prompt) {
+ EUserPrompterServerExtension *extension;
+
+ extension = g_hash_table_lookup (server->priv->extensions, pr->dialog_name);
+ g_return_if_fail (extension != NULL);
+
+ if (!e_user_prompter_server_extension_prompt (extension, pr->id, pr->dialog_name, pr->parameters)) {
+ e_user_prompter_server_response (server, pr->id, -1, NULL);
+ }
+ } else {
+ g_signal_emit (server, signals[PROMPT], 0, pr->id, pr->type, pr->title, pr->primary_text, pr->secondary_text, pr->use_markup, pr->button_captions);
+ }
+}
+
+static gboolean
+show_prompt_idle_cb (gpointer user_data)
+{
+ EUserPrompterServer *server = user_data;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER_SERVER (server), FALSE);
+
+ g_rec_mutex_lock (&server->priv->lock);
+ if (server->priv->prompts) {
+ do_show_prompt (server);
+ /* keep the schedule_id set, until user responds */
+ } else {
+ server->priv->schedule_id = 0;
+ }
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ return FALSE;
+}
+
+static void
+maybe_schedule_prompt (EUserPrompterServer *server)
+{
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER (server));
+
+ g_rec_mutex_lock (&server->priv->lock);
+ if (!server->priv->schedule_id && server->priv->prompts)
+ server->priv->schedule_id = g_idle_add (show_prompt_idle_cb, server);
+ g_rec_mutex_unlock (&server->priv->lock);
+}
+
+static gboolean
+user_prompter_server_prompt_cb (EDBusUserPrompter *dbus_prompter,
+ GDBusMethodInvocation *invocation,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const gchar *const *button_captions,
+ EUserPrompterServer *server)
+{
+ gint id;
+
+ g_rec_mutex_lock (&server->priv->lock);
+
+ id = add_prompt (server, FALSE, type, title, primary_text, secondary_text, use_markup, button_captions, NULL, NULL);
+
+ e_dbus_user_prompter_complete_prompt (dbus_prompter, invocation, id);
+
+ maybe_schedule_prompt (server);
+
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ return TRUE;
+}
+
+static gboolean
+user_prompter_server_extension_prompt_cb (EDBusUserPrompter *dbus_prompter,
+ GDBusMethodInvocation *invocation,
+ const gchar *dialog_name,
+ const gchar *const *parameters,
+ EUserPrompterServer *server)
+{
+ gint id;
+
+ g_rec_mutex_lock (&server->priv->lock);
+
+ if (!dialog_name || !g_hash_table_lookup (server->priv->extensions, dialog_name)) {
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("Extension dialog '%s' not found."), dialog_name ? dialog_name : "[null]");
+
+ return TRUE;
+ }
+
+ id = add_prompt (server, TRUE, NULL, NULL, NULL, NULL, FALSE, NULL, dialog_name, parameters);
+
+ e_dbus_user_prompter_complete_extension_prompt (dbus_prompter, invocation, id);
+
+ maybe_schedule_prompt (server);
+
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ return TRUE;
+}
+
+static void
+user_prompter_server_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_user_prompter_server_parent_class)->constructed (object);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static void
+user_prompter_server_dispose (GObject *object)
+{
+ EUserPrompterServerPrivate *priv;
+
+ priv = E_USER_PROMPTER_SERVER_GET_PRIVATE (object);
+
+ if (priv->dbus_prompter != NULL) {
+ g_object_unref (priv->dbus_prompter);
+ priv->dbus_prompter = NULL;
+ }
+
+ g_slist_free_full (priv->prompts, prompt_request_free);
+ g_hash_table_remove_all (priv->extensions);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_user_prompter_server_parent_class)->dispose (object);
+}
+
+static void
+user_prompter_server_finalize (GObject *object)
+{
+ EUserPrompterServerPrivate *priv;
+
+ priv = E_USER_PROMPTER_SERVER_GET_PRIVATE (object);
+
+ g_rec_mutex_clear (&priv->lock);
+ g_hash_table_destroy (priv->extensions);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_user_prompter_server_parent_class)->finalize (object);
+}
+
+static void
+user_prompter_server_bus_acquired (EDBusServer *server,
+ GDBusConnection *connection)
+{
+ EUserPrompterServerPrivate *priv;
+ GError *error = NULL;
+
+ priv = E_USER_PROMPTER_SERVER_GET_PRIVATE (server);
+
+ g_dbus_interface_skeleton_export (
+ G_DBUS_INTERFACE_SKELETON (priv->dbus_prompter),
+ connection, E_USER_PROMPTER_SERVER_OBJECT_PATH, &error);
+
+ /* Terminate the server if we can't export the interface. */
+ if (error != NULL) {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_NORMAL);
+ g_error_free (error);
+ }
+
+ /* Chain up to parent's bus_acquired() method. */
+ E_DBUS_SERVER_CLASS (e_user_prompter_server_parent_class)->
+ bus_acquired (server, connection);
+}
+
+static void
+user_prompter_server_quit_server (EDBusServer *server,
+ EDBusServerExitCode code)
+{
+ EUserPrompterServerPrivate *priv;
+
+ priv = E_USER_PROMPTER_SERVER_GET_PRIVATE (server);
+
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (priv->dbus_prompter));
+
+ /* Chain up to parent's quit_server() method. */
+ E_DBUS_SERVER_CLASS (e_user_prompter_server_parent_class)->quit_server (server, code);
+}
+
+static void
+e_user_prompter_server_class_init (EUserPrompterServerClass *class)
+{
+ GObjectClass *object_class;
+ EDBusServerClass *dbus_server_class;
+
+ g_type_class_add_private (class, sizeof (EUserPrompterServerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = user_prompter_server_constructed;
+ object_class->dispose = user_prompter_server_dispose;
+ object_class->finalize = user_prompter_server_finalize;
+
+ dbus_server_class = E_DBUS_SERVER_CLASS (class);
+ dbus_server_class->bus_name = USER_PROMPTER_DBUS_SERVICE_NAME;
+ dbus_server_class->module_directory = MODULE_DIRECTORY;
+ dbus_server_class->bus_acquired = user_prompter_server_bus_acquired;
+ dbus_server_class->quit_server = user_prompter_server_quit_server;
+
+ signals[PROMPT] = g_signal_new (
+ "prompt",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EUserPrompterServerClass, prompt),
+ NULL, NULL,
+ e_marshal_VOID__INT_STRING_STRING_STRING_STRING_BOOLEAN_POINTER,
+ G_TYPE_NONE, 7,
+ G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
+}
+
+static void
+e_user_prompter_server_init (EUserPrompterServer *server)
+{
+ server->priv = E_USER_PROMPTER_SERVER_GET_PRIVATE (server);
+ server->priv->dbus_prompter = e_dbus_user_prompter_skeleton_new ();
+ server->priv->prompts = NULL;
+ server->priv->extensions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ g_rec_mutex_init (&server->priv->lock);
+
+ g_signal_connect (server->priv->dbus_prompter, "handle-prompt",
+ G_CALLBACK (user_prompter_server_prompt_cb), server);
+
+ g_signal_connect (server->priv->dbus_prompter, "handle-extension-prompt",
+ G_CALLBACK (user_prompter_server_extension_prompt_cb), server);
+}
+
+/**
+ * e_user_prompter_server_new:
+ *
+ * Creates a new instance of #EUserPrompterServer.
+ *
+ * Returns: a new instance of #EUserPrompterServer
+ *
+ * Since: 3.8
+ **/
+EDBusServer *
+e_user_prompter_server_new (void)
+{
+ return g_object_new (E_TYPE_USER_PROMPTER_SERVER, NULL);
+}
+
+/**
+ * e_user_prompter_server_response:
+ * @server: an #EUserPrompterServer
+ * @prompt_id: Id of a prompt, which was responded
+ * @response: Response of the prompt
+ * @extension_values: (allow-none): For extension prompts can pass extra return values
+ *
+ * Finishes prompt initiated by a "prompt" signal or an extension prompt.
+ * The @response for non-extension prompts is a 0-based index of a button
+ * used to close the prompt.
+ *
+ * The @extension_values is ignored for non-extension prompts.
+ *
+ * Since: 3.8
+ **/
+void
+e_user_prompter_server_response (EUserPrompterServer *server,
+ gint prompt_id,
+ gint response,
+ const ENamedParameters *extension_values)
+{
+ gboolean is_extension_prompt = FALSE;
+
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER (server));
+ g_return_if_fail (server->priv->schedule_id != 0);
+
+ g_rec_mutex_lock (&server->priv->lock);
+
+ if (!server->priv->prompts || server->priv->schedule_id == 0) {
+ g_rec_mutex_unlock (&server->priv->lock);
+ g_return_if_reached ();
+ return;
+ }
+
+ if (remove_prompt (server, prompt_id, &is_extension_prompt)) {
+ if (is_extension_prompt) {
+ GPtrArray *values = NULL;
+
+ if (!extension_values || !extension_values->len ||
+ extension_values->pdata[extension_values->len - 1] != NULL) {
+ gint ii;
+
+ values = g_ptr_array_new ();
+ for (ii = 0; extension_values && ii < extension_values->len; ii++) {
+ g_ptr_array_add (values, extension_values->pdata[ii]);
+ }
+ g_ptr_array_add (values, NULL);
+ extension_values = values;
+ }
+
+ e_dbus_user_prompter_emit_extension_response (server->priv->dbus_prompter, prompt_id, response,
+ (const gchar * const *) extension_values->pdata);
+
+ if (values)
+ g_ptr_array_free (values, TRUE);
+ } else {
+ e_dbus_user_prompter_emit_response (server->priv->dbus_prompter, prompt_id, response);
+ }
+ }
+
+ if (server->priv->prompts) {
+ do_show_prompt (server);
+ } else {
+ server->priv->schedule_id = 0;
+ }
+
+ g_rec_mutex_unlock (&server->priv->lock);
+}
+
+/**
+ * e_user_prompter_server_register:
+ * @server: an #EUserPrompterServer
+ * @extension: an #EUserPrompterServerExtension descendant
+ * @dialog_name: name of a dialog, which the @extensions implement
+ *
+ * Registers @extension as a provider of @dialog_name dialog. The names
+ * are compared case sensitively and two extensions cannot provide
+ * the same dialog. If the function succeeds, then it adds its own
+ * reference on the @extension.
+ *
+ * Extensions providing multiple dialogs call this function multiple
+ * times, for each dialog name separately.
+ *
+ * Returns: Whether properly registered @extension
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_user_prompter_server_register (EUserPrompterServer *server,
+ EExtension *extension,
+ const gchar *dialog_name)
+{
+ g_return_val_if_fail (E_IS_USER_PROMPTER_SERVER (server), FALSE);
+ g_return_val_if_fail (E_IS_USER_PROMPTER_SERVER_EXTENSION (extension), FALSE);
+ g_return_val_if_fail (dialog_name != NULL, FALSE);
+ g_return_val_if_fail (*dialog_name != '\0', FALSE);
+
+ g_rec_mutex_lock (&server->priv->lock);
+
+ if (g_hash_table_lookup (server->priv->extensions, dialog_name)) {
+ g_rec_mutex_unlock (&server->priv->lock);
+ return FALSE;
+ }
+
+ g_print ("Registering %s for dialog '%s'\n", G_OBJECT_TYPE_NAME (extension), dialog_name);
+ g_hash_table_insert (server->priv->extensions, g_strdup (dialog_name), g_object_ref (extension));
+
+ g_rec_mutex_unlock (&server->priv->lock);
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * e-user-prompter-server.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_USER_PROMPTER_SERVER_H
+#define E_USER_PROMPTER_SERVER_H
+
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_USER_PROMPTER_SERVER \
+ (e_user_prompter_server_get_type ())
+#define E_USER_PROMPTER_SERVER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER, EUserPrompterServer))
+#define E_USER_PROMPTER_SERVER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_USER_PROMPTER_SERVER, EUserPrompterServerClass))
+#define E_IS_USER_PROMPTER_SERVER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER))
+#define E_IS_USER_PROMPTER_SERVER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_USER_PROMPTER_SERVER))
+#define E_USER_PROMPTER_SERVER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_USER_PROMPTER_SERVER, EUserPrompterServerClass))
+
+/**
+ * E_USER_PROMPTER_SERVER_OBJECT_PATH:
+ *
+ * D-Bus object path of the user prompter.
+ *
+ * Since: 3.8
+ **/
+#define E_USER_PROMPTER_SERVER_OBJECT_PATH \
+ "/org/gnome/evolution/dataserver/UserPrompter"
+
+G_BEGIN_DECLS
+
+typedef struct _EUserPrompterServer EUserPrompterServer;
+typedef struct _EUserPrompterServerClass EUserPrompterServerClass;
+typedef struct _EUserPrompterServerPrivate EUserPrompterServerPrivate;
+
+/**
+ * EUserPrompterServer:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.8
+ **/
+struct _EUserPrompterServer {
+ EDBusServer parent;
+ EUserPrompterServerPrivate *priv;
+};
+
+struct _EUserPrompterServerClass {
+ EDBusServerClass parent_class;
+
+ /* signals */
+ void (* prompt) (EUserPrompterServer *server,
+ gint id,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const GSList *button_captions);
+};
+
+GType e_user_prompter_server_get_type (void);
+EDBusServer * e_user_prompter_server_new (void);
+void e_user_prompter_server_response (EUserPrompterServer *server,
+ gint prompt_id,
+ gint button_index,
+ const ENamedParameters *extension_values);
+
+gboolean e_user_prompter_server_register (EUserPrompterServer *server,
+ EExtension *extension,
+ const gchar *dialog_name);
+
+G_END_DECLS
+
+#endif /* E_USER_PROMPTER_SERVER_H */
--- /dev/null
+/*
+ * e-user-prompter.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-user-prompter
+ * @include: libebackend/libebackend.h
+ * @short_description: Manages user prompts over DBus
+ *
+ * Use this to initiate a user prompt from an #EBackend descendant.
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-dbus-user-prompter.h"
+#include "e-user-prompter.h"
+
+#define E_USER_PROMPTER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_USER_PROMPTER, EUserPrompterPrivate))
+
+struct _EUserPrompterPrivate {
+ gint dummy; /* not used */
+};
+
+G_DEFINE_TYPE (EUserPrompter, e_user_prompter, G_TYPE_OBJECT)
+
+static void
+e_user_prompter_class_init (EUserPrompterClass *class)
+{
+ g_type_class_add_private (class, sizeof (EUserPrompterPrivate));
+}
+
+static void
+e_user_prompter_init (EUserPrompter *prompter)
+{
+ prompter->priv = E_USER_PROMPTER_GET_PRIVATE (prompter);
+}
+
+/**
+ * e_user_prompter_new:
+ *
+ * Creates a new instance of #EUserPrompter.
+ *
+ * Returns: a new instance of #EUserPrompter
+ *
+ * Since: 3.8
+ **/
+EUserPrompter *
+e_user_prompter_new (void)
+{
+ return g_object_new (E_TYPE_USER_PROMPTER, NULL);
+}
+
+typedef struct _PrompterAsyncData {
+ /* Prompt data */
+ gchar *type;
+ gchar *title;
+ gchar *primary_text;
+ gchar *secondary_text;
+ gboolean use_markup;
+ GSList *button_captions;
+
+ /* ExtensionPrompt data */
+ gchar *dialog_name;
+ ENamedParameters *in_parameters;
+ ENamedParameters *out_values;
+
+ /* common data */
+ gint response_button;
+
+ /* callbacks */
+ gchar *response_signal_name;
+ GCallback response_callback;
+ gboolean (* invoke) (EDBusUserPrompter *dbus_prompter,
+ struct _PrompterAsyncData *async_data,
+ GCancellable *cancellable,
+ GError **error);
+
+ /* Internal data */
+ gint prompt_id;
+ GMainLoop *main_loop; /* not owned by the structure */
+} PrompterAsyncData;
+
+static void
+prompter_async_data_free (PrompterAsyncData *async_data)
+{
+ if (!async_data)
+ return;
+
+ g_free (async_data->type);
+ g_free (async_data->title);
+ g_free (async_data->primary_text);
+ g_free (async_data->secondary_text);
+ g_slist_free_full (async_data->button_captions, g_free);
+
+ g_free (async_data->dialog_name);
+ e_named_parameters_free (async_data->in_parameters);
+ e_named_parameters_free (async_data->out_values);
+
+ g_free (async_data->response_signal_name);
+
+ g_free (async_data);
+}
+
+static void
+user_prompter_response_cb (EDBusUserPrompter *dbus_prompter,
+ gint prompt_id,
+ gint response_button,
+ PrompterAsyncData *async_data)
+{
+ g_return_if_fail (async_data != NULL);
+
+ if (async_data->prompt_id == prompt_id) {
+ async_data->response_button = response_button;
+ g_main_loop_quit (async_data->main_loop);
+ }
+}
+
+static gboolean
+user_prompter_prompt_invoke (EDBusUserPrompter *dbus_prompter,
+ struct _PrompterAsyncData *async_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPtrArray *captions;
+ GSList *iter;
+ gboolean success;
+
+ g_return_val_if_fail (dbus_prompter != NULL, FALSE);
+ g_return_val_if_fail (async_data != NULL, FALSE);
+
+ captions = g_ptr_array_new ();
+ for (iter = async_data->button_captions; iter; iter = g_slist_next (iter)) {
+ gchar *caption = iter->data;
+
+ g_ptr_array_add (captions, caption ? caption : (gchar *) "");
+ }
+
+ /* NULL-terminated array */
+ g_ptr_array_add (captions, NULL);
+
+ success = e_dbus_user_prompter_call_prompt_sync (dbus_prompter,
+ async_data->type ? async_data->type : "",
+ async_data->title ? async_data->title : "",
+ async_data->primary_text ? async_data->primary_text : "",
+ async_data->secondary_text ? async_data->secondary_text : "",
+ async_data->use_markup,
+ (const gchar *const *) captions->pdata,
+ &async_data->prompt_id,
+ cancellable,
+ error);
+
+ g_ptr_array_free (captions, TRUE);
+
+ return success;
+}
+
+static void
+user_prompter_extension_response_cb (EDBusUserPrompter *dbus_prompter,
+ gint prompt_id,
+ gint response_button,
+ const gchar * const *arg_values,
+ PrompterAsyncData *async_data)
+{
+ g_return_if_fail (async_data != NULL);
+
+ if (async_data->prompt_id == prompt_id) {
+ async_data->response_button = response_button;
+ if (arg_values)
+ async_data->out_values = e_named_parameters_new_strv (arg_values);
+ g_main_loop_quit (async_data->main_loop);
+ }
+}
+
+static gboolean
+user_prompter_extension_prompt_invoke (EDBusUserPrompter *dbus_prompter,
+ struct _PrompterAsyncData *async_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success;
+ GPtrArray *params;
+ gint ii;
+
+ g_return_val_if_fail (dbus_prompter != NULL, FALSE);
+ g_return_val_if_fail (async_data != NULL, FALSE);
+
+ params = g_ptr_array_new ();
+ for (ii = 0; async_data->in_parameters && ii < async_data->in_parameters->len; ii++) {
+ gchar *param = async_data->in_parameters->pdata[ii];
+
+ if (param)
+ g_ptr_array_add (params, param);
+ }
+
+ /* NULL-terminated array */
+ g_ptr_array_add (params, NULL);
+
+ success = e_dbus_user_prompter_call_extension_prompt_sync (dbus_prompter,
+ async_data->dialog_name,
+ (const gchar *const *) params->pdata,
+ &async_data->prompt_id,
+ cancellable,
+ error);
+
+ g_ptr_array_free (params, TRUE);
+
+ return success;
+}
+
+static void
+user_prompter_prompt_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ EDBusUserPrompter *dbus_prompter;
+ PrompterAsyncData *async_data;
+ GMainContext *main_context;
+ GError *local_error = NULL;
+ gulong handler_id;
+
+ g_return_if_fail (E_IS_USER_PROMPTER (object));
+
+ async_data = g_simple_async_result_get_op_res_gpointer (simple);
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->response_signal_name != NULL);
+ g_return_if_fail (async_data->response_callback != NULL);
+ g_return_if_fail (async_data->invoke != NULL);
+
+ main_context = g_main_context_new ();
+ /* this way the Response signal is delivered here, not to the main thread's context,
+ which can be blocked by the e_user_prompter_prompt_sync() call anyway */
+ g_main_context_push_thread_default (main_context);
+
+ dbus_prompter = e_dbus_user_prompter_proxy_new_for_bus_sync (
+ G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ USER_PROMPTER_DBUS_SERVICE_NAME,
+ "/org/gnome/evolution/dataserver/UserPrompter",
+ cancellable,
+ &local_error);
+
+ if (!dbus_prompter) {
+ g_main_context_pop_thread_default (main_context);
+ g_main_context_unref (main_context);
+ g_dbus_error_strip_remote_error (local_error);
+ g_simple_async_result_take_error (simple, local_error);
+ return;
+ }
+
+ handler_id = g_signal_connect (dbus_prompter, async_data->response_signal_name,
+ async_data->response_callback, async_data);
+
+ if (!async_data->invoke (dbus_prompter, async_data, cancellable, &local_error)) {
+ g_main_context_pop_thread_default (main_context);
+ g_main_context_unref (main_context);
+ g_dbus_error_strip_remote_error (local_error);
+ g_simple_async_result_take_error (simple, local_error);
+ g_signal_handler_disconnect (dbus_prompter, handler_id);
+ g_object_unref (dbus_prompter);
+ return;
+ }
+
+ async_data->main_loop = g_main_loop_new (main_context, FALSE);
+
+ g_main_loop_run (async_data->main_loop);
+
+ g_main_loop_unref (async_data->main_loop);
+ async_data->main_loop = NULL;
+
+ g_signal_handler_disconnect (dbus_prompter, handler_id);
+ g_object_unref (dbus_prompter);
+
+ g_main_context_pop_thread_default (main_context);
+ g_main_context_unref (main_context);
+}
+
+/**
+ * e_user_prompter_prompt:
+ * @prompter: an #EUserPrompter
+ * @type: (allow-none): type of the prompt; can be %NULL
+ * @title: (allow-none): window title of the prompt; can be %NULL
+ * @primary_text: (allow-none): primary text of the prompt; can be %NULL
+ * @secondary_text: (allow-none): secondary text of the prompt; can be %NULL
+ * @use_markup: whether both texts are with markup
+ * @button_captions: (allow-none): captions of buttons to use in the message; can be %NULL
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request
+ * is satisfied
+ * @user_data: (closure): data to pass to the callback function
+ *
+ * Asynchronously prompt a user for a decision.
+ *
+ * The @type can be one of "info", "warning", "question" or "error", to include
+ * an icon in the message prompt; anything else results in no icon in the message.
+ *
+ * If @button_captions is %NULL or empty list, then only one button is shown in
+ * the prompt, a "Dismiss" button.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_user_prompter_prompt_finish() to get the result of the operation.
+ *
+ * Since: 3.8
+ **/
+void
+e_user_prompter_prompt (EUserPrompter *prompter,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const GSList *button_captions,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ PrompterAsyncData *async_data;
+ GSList *iter;
+
+ g_return_if_fail (E_IS_USER_PROMPTER (prompter));
+ g_return_if_fail (callback != NULL);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (prompter), callback, user_data,
+ e_user_prompter_prompt);
+
+ async_data = g_new0 (PrompterAsyncData, 1);
+ async_data->type = g_strdup (type);
+ async_data->title = g_strdup (title);
+ async_data->primary_text = g_strdup (primary_text);
+ async_data->secondary_text = g_strdup (secondary_text);
+ async_data->use_markup = use_markup;
+ async_data->button_captions = g_slist_copy ((GSList *) button_captions);
+ async_data->prompt_id = -1;
+ async_data->response_button = -1;
+
+ async_data->response_signal_name = g_strdup ("response");
+ async_data->response_callback = G_CALLBACK (user_prompter_response_cb);
+ async_data->invoke = user_prompter_prompt_invoke;
+
+ for (iter = async_data->button_captions; iter; iter = g_slist_next (iter)) {
+ iter->data = g_strdup (iter->data);
+ }
+
+ g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) prompter_async_data_free);
+ g_simple_async_result_run_in_thread (simple, user_prompter_prompt_thread, G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (simple);
+}
+
+/**
+ * e_user_prompter_prompt_finish:
+ * @prompter: an #EUserPrompter
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_user_prompter_prompt().
+ *
+ * If an error occurred, the function sets @error and returns -1.
+ *
+ * Returns: 0-based index of a button being used by a user as a response,
+ * corresponding to 'button_captions' from e_user_prompter_prompt() call.
+ *
+ * Since: 3.8
+ **/
+gint
+e_user_prompter_prompt_finish (EUserPrompter *prompter,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ PrompterAsyncData *async_data;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER (prompter), -1);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompter), e_user_prompter_prompt), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ async_data = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return -1;
+
+ g_return_val_if_fail (async_data != NULL, -1);
+
+ return async_data->response_button;
+}
+
+/**
+ * e_user_prompter_prompt_sync:
+ * @prompter: an #EUserPrompter
+ * @type: (allow-none): type of the prompt; can be %NULL
+ * @title: (allow-none): window title of the prompt; can be %NULL
+ * @primary_text: (allow-none): primary text of the prompt; can be %NULL
+ * @secondary_text: (allow-none): secondary text of the prompt; can be %NULL
+ * @use_markup: whether both texts are with markup
+ * @button_captions: (allow-none): captions of buttons to use in the message; can be %NULL
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Prompts a user for a decision.
+ *
+ * The @type can be one of "info", "warning", "question" or "error", to include
+ * an icon in the message prompt; anything else results in no icon in the message.
+ *
+ * If @button_captions is %NULL or empty list, then only one button is shown in
+ * the prompt, a "Dismiss" button.
+ *
+ * If an error occurred, the function sets @error and returns -1.
+ *
+ * Returns: 0-based index of a button being used by a user as a response,
+ * corresponding to @button_captions list.
+ *
+ * Since: 3.8
+ **/
+gint
+e_user_prompter_prompt_sync (EUserPrompter *prompter,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const GSList *button_captions,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EAsyncClosure *closure;
+ GAsyncResult *result;
+ gint response_button;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER (prompter), -1);
+
+ closure = e_async_closure_new ();
+
+ e_user_prompter_prompt (prompter, type, title, primary_text, secondary_text, use_markup, button_captions,
+ cancellable, e_async_closure_callback, closure);
+
+ result = e_async_closure_wait (closure);
+
+ response_button = e_user_prompter_prompt_finish (prompter, result, error);
+
+ e_async_closure_free (closure);
+
+ return response_button;
+}
+
+/**
+ * e_user_prompter_extension_prompt:
+ * @prompter: an #EUserPrompter
+ * @dialog_name: name of a dialog to invoke
+ * @in_parameters: (allow-none): optional parameters to pass to extension; can be %NULL
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request
+ * is satisfied
+ * @user_data: (closure): data to pass to the callback function
+ *
+ * Asynchronously prompt a user for a decision on an extension-provided dialog.
+ * The caller usually provides an extension for #EUserPrompterServer, a descendant
+ * of #EUserPrompterServerExtension, which registers itself as a dialog provider.
+ * The extension defines @dialog_name, same as meaning of @in_parameters;
+ * only the extension and the caller know about meaning of these.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_user_prompter_extension_prompt_finish() to get the result of the operation.
+ * If there is no extension providing given dialog name, the operation finishes with
+ * a G_IO_ERROR, G_IO_ERROR_NOT_FOUND #GError.
+ *
+ * Since: 3.8
+ **/
+void
+e_user_prompter_extension_prompt (EUserPrompter *prompter,
+ const gchar *dialog_name,
+ const ENamedParameters *in_parameters,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ PrompterAsyncData *async_data;
+
+ g_return_if_fail (E_IS_USER_PROMPTER (prompter));
+ g_return_if_fail (dialog_name != NULL);
+ g_return_if_fail (callback != NULL);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (prompter), callback, user_data,
+ e_user_prompter_extension_prompt);
+
+ async_data = g_new0 (PrompterAsyncData, 1);
+ async_data->dialog_name = g_strdup (dialog_name);
+ if (in_parameters) {
+ async_data->in_parameters = e_named_parameters_new ();
+ e_named_parameters_assign (async_data->in_parameters, in_parameters);
+ } else {
+ async_data->in_parameters = NULL;
+ }
+
+ async_data->prompt_id = -1;
+ async_data->response_button = -1;
+ async_data->out_values = NULL;
+
+ async_data->response_signal_name = g_strdup ("extension-response");
+ async_data->response_callback = G_CALLBACK (user_prompter_extension_response_cb);
+ async_data->invoke = user_prompter_extension_prompt_invoke;
+
+ g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) prompter_async_data_free);
+ g_simple_async_result_run_in_thread (simple, user_prompter_prompt_thread, G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (simple);
+}
+
+/**
+ * e_user_prompter_extension_prompt_finish:
+ * @prompter: an #EUserPrompter
+ * @result: a #GAsyncResult
+ * @out_values: (allow-none): Where to store values from the extension, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_user_prompter_extension_prompt().
+ * Caller can provide @out_values to get additional values provided by the extension.
+ * In case the caller is not interested in additional values, it can pass %NULL @out_values.
+ * The @out_values will be cleared first, then any values will be added there.
+ * Only the caller and the extension know about meaning of the result code and
+ * additional values.
+ *
+ * If an error occurred, the function sets @error and returns -1. If there is
+ * no extension providing given dialog name, the operation finishes with
+ * a G_IO_ERROR, G_IO_ERROR_NOT_FOUND @error.
+ *
+ * Returns: Result code of the prompt, as defined by the extension, or -1 on error.
+ *
+ * Since: 3.8
+ **/
+gint
+e_user_prompter_extension_prompt_finish (EUserPrompter *prompter,
+ GAsyncResult *result,
+ ENamedParameters *out_values,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ PrompterAsyncData *async_data;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER (prompter), -1);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompter), e_user_prompter_extension_prompt), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ async_data = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return -1;
+
+ g_return_val_if_fail (async_data != NULL, -1);
+
+ if (out_values)
+ e_named_parameters_assign (out_values, async_data->out_values);
+
+ return async_data->response_button;
+}
+
+/**
+ * e_user_prompter_extension_prompt:
+ * @prompter: an #EUserPrompter
+ * @dialog_name: name of a dialog to invoke
+ * @in_parameters: (allow-none): optional parameters to pass to extension; can be %NULL
+ * @out_values: (allow-none): Where to store values from the extension, or %NULL
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Synchronously prompt a user for a decision on an extension-provided dialog.
+ * The caller usually provides an extension for #EUserPrompterServer, a descendant
+ * of #EUserPrompterServerExtension, which registers itself as a dialog provider.
+ * The extension defines @dialog_name, same as meaning of @in_parameters;
+ * only the extension and the caller know about meaning of these.
+ *
+ * Caller can provide @out_values to get additional values provided by the extension.
+ * In case the caller is not interested in additional values, it can pass %NULL @out_values.
+ * The @out_values will be cleared first, then any values will be added there.
+ * Only the caller and the extension know about meaning of the result code and
+ * additional values.
+ *
+ * If an error occurred, the function sets @error and returns -1. If there is
+ * no extension providing given dialog name, the operation finishes with
+ * a G_IO_ERROR, G_IO_ERROR_NOT_FOUND @error.
+ *
+ * Returns: Result code of the prompt, as defined by the extension, or -1 on error.
+ *
+ * Since: 3.8
+ **/
+gint
+e_user_prompter_extension_prompt_sync (EUserPrompter *prompter,
+ const gchar *dialog_name,
+ const ENamedParameters *in_parameters,
+ ENamedParameters *out_values,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EAsyncClosure *closure;
+ GAsyncResult *result;
+ gint response_button;
+
+ g_return_val_if_fail (E_IS_USER_PROMPTER (prompter), -1);
+ g_return_val_if_fail (dialog_name != NULL, -1);
+
+ closure = e_async_closure_new ();
+
+ e_user_prompter_extension_prompt (prompter, dialog_name, in_parameters,
+ cancellable, e_async_closure_callback, closure);
+
+ result = e_async_closure_wait (closure);
+
+ response_button = e_user_prompter_extension_prompt_finish (prompter, result, out_values, error);
+
+ e_async_closure_free (closure);
+
+ return response_button;
+}
--- /dev/null
+/*
+ * e-user-prompter.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION)
+#error "Only <libebackend/libebackend.h> should be included directly."
+#endif
+
+#ifndef E_USER_PROMPTER_H
+#define E_USER_PROMPTER_H
+
+#include <glib.h>
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_USER_PROMPTER \
+ (e_user_prompter_get_type ())
+#define E_USER_PROMPTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_USER_PROMPTER, EUserPrompter))
+#define E_IS_USER_PROMPTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_USER_PROMPTER))
+#define E_USER_PROMPTER_GET_INTERFACE(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE \
+ ((obj), E_TYPE_USER_PROMPTER, EUserPrompterInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _EUserPrompter EUserPrompter;
+typedef struct _EUserPrompterClass EUserPrompterClass;
+typedef struct _EUserPrompterPrivate EUserPrompterPrivate;
+
+/**
+ * EUserPrompter:
+ *
+ * Since: 3.8
+ **/
+struct _EUserPrompter {
+ GObject parent;
+ EUserPrompterPrivate *priv;
+};
+
+struct _EUserPrompterClass {
+ GObjectClass parent;
+};
+
+GType e_user_prompter_get_type (void);
+EUserPrompter * e_user_prompter_new (void);
+void e_user_prompter_prompt (EUserPrompter *prompter,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const GSList *button_captions,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gint e_user_prompter_prompt_finish (EUserPrompter *prompter,
+ GAsyncResult *result,
+ GError **error);
+gint e_user_prompter_prompt_sync (EUserPrompter *prompter,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const GSList *button_captions,
+ GCancellable *cancellable,
+ GError **error);
+void e_user_prompter_extension_prompt (EUserPrompter *prompter,
+ const gchar *dialog_name,
+ const ENamedParameters *in_parameters,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gint e_user_prompter_extension_prompt_finish (EUserPrompter *prompter,
+ GAsyncResult *result,
+ ENamedParameters *out_values,
+ GError **error);
+gint e_user_prompter_extension_prompt_sync (EUserPrompter *prompter,
+ const gchar *dialog_name,
+ const ENamedParameters *in_parameters,
+ ENamedParameters *out_values,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_USER_PROMPTER_H */
#include <libebackend/e-server-side-source.h>
#include <libebackend/e-source-registry-server.h>
#include <libebackend/e-sqlite3-vfs.h>
+#include <libebackend/e-user-prompter.h>
+#include <libebackend/e-user-prompter-server.h>
+#include <libebackend/e-user-prompter-server-extension.h>
#undef __LIBEBACKEND_H_INSIDE__
{
return default_dbus_timeout;
}
+
+/**
+ * e_named_parameters_new:
+ *
+ * Creates a new instance of an #ENamedParameters. This should be freed
+ * with e_named_parameters_free(), when no longer needed. Names are
+ * compared case insensitively.
+ *
+ * The structure is not thread safe, if the caller requires thread safety,
+ * then it should provide it on its own.
+ *
+ * Returns: newly allocated #ENamedParameters
+ *
+ * Since: 3.8
+ **/
+ENamedParameters *
+e_named_parameters_new (void)
+{
+ return g_ptr_array_new_with_free_func (g_free);
+}
+
+/**
+ * e_named_parameters_new_strv:
+ * @strv: NULL-terminated string array to be used as a content of a newly
+ * created #ENamedParameters
+ *
+ * Creates a new instance of an #ENamedParameters, with initial content
+ * being taken from @strv. This should be freed with e_named_parameters_free(),
+ * when no longer needed. Names are compared case insensitively.
+ *
+ * The structure is not thread safe, if the caller requires thread safety,
+ * then it should provide it on its own.
+ *
+ * Returns: newly allocated #ENamedParameters
+ *
+ * Since: 3.8
+ **/
+ENamedParameters *
+e_named_parameters_new_strv (const gchar * const *strv)
+{
+ ENamedParameters *parameters;
+ gint ii;
+
+ g_return_val_if_fail (strv != NULL, NULL);
+
+ parameters = e_named_parameters_new ();
+ for (ii = 0; strv[ii]; ii++) {
+ g_ptr_array_add (parameters, g_strdup (strv[ii]));
+ }
+
+ return parameters;
+}
+
+/**
+ * e_named_parameters_free:
+ * @parameters: an #ENamedParameters
+ *
+ * Frees an instance of #ENamedParameters, previously allocated
+ * with e_named_parameters_new(). Function does nothing, if
+ * @parameters is %NULL.
+ *
+ * Since: 3.8
+ **/
+void
+e_named_parameters_free (ENamedParameters *parameters)
+{
+ if (!parameters)
+ return;
+
+ g_ptr_array_free (parameters, TRUE);
+}
+
+/**
+ * e_named_parameters_clear:
+ * @parameters: an #ENamedParameters
+ *
+ * Removes all stored parameters from @parameters.
+ *
+ * Since: 3.8
+ **/
+void
+e_named_parameters_clear (ENamedParameters *parameters)
+{
+ g_return_if_fail (parameters != NULL);
+
+ if (parameters->len)
+ g_ptr_array_remove_range (parameters, 0, parameters->len);
+}
+
+/**
+ * e_named_parameters_assign:
+ * @parameters: an #ENamedParameters to assign values to
+ * @from: (allow-none): an #ENamedParameters to get values from, or %NULL
+ *
+ * Makes content of the @parameters the same as @from.
+ * Functions clears content of @parameters if @from is %NULL.
+ *
+ * Since: 3.8
+ **/
+void
+e_named_parameters_assign (ENamedParameters *parameters,
+ const ENamedParameters *from)
+{
+ g_return_if_fail (parameters != NULL);
+
+ e_named_parameters_clear (parameters);
+
+ if (from) {
+ gint ii;
+
+ for (ii = 0; ii < from->len; ii++) {
+ g_ptr_array_add (parameters, g_strdup (from->pdata[ii]));
+ }
+ }
+}
+
+static gint
+get_parameter_index (const ENamedParameters *parameters,
+ const gchar *name)
+{
+ gint ii, name_len;
+
+ g_return_val_if_fail (parameters != NULL, -1);
+ g_return_val_if_fail (name != NULL, -1);
+
+ name_len = strlen (name);
+
+ for (ii = 0; ii < parameters->len; ii++) {
+ const gchar *name_and_value = g_ptr_array_index (parameters, ii);
+
+ if (name_and_value && g_ascii_strncasecmp (name_and_value, name, name_len) == 0 &&
+ name_and_value[name_len] == ':')
+ return ii;
+ }
+
+ return -1;
+}
+
+/**
+ * e_named_parameters_set:
+ * @parameters: an #ENamedParameters
+ * @name: name of a parameter to set
+ * @value: (allow-none): value to set, or %NULL to unset
+ *
+ * Sets parameter named @name to value @value. If @value is NULL,
+ * then the parameter is removed. @value can be an empty string.
+ *
+ * Note: There is a restriction on parameter names, it cannot be empty or
+ * contain a colon character (':'), otherwise it can be pretty much anything.
+ *
+ * Since: 3.8
+ **/
+void
+e_named_parameters_set (ENamedParameters *parameters,
+ const gchar *name,
+ const gchar *value)
+{
+ gint index;
+ gchar *name_and_value;
+
+ g_return_if_fail (parameters != NULL);
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (strchr (name, ':') == NULL);
+ g_return_if_fail (*name != '\0');
+
+ index = get_parameter_index (parameters, name);
+ if (!value) {
+ if (index != -1)
+ g_ptr_array_remove_index (parameters, index);
+ return;
+ }
+
+ name_and_value = g_strconcat (name, ":", value, NULL);
+ if (index != -1) {
+ g_free (parameters->pdata[index]);
+ parameters->pdata[index] = name_and_value;
+ } else {
+ g_ptr_array_add (parameters, name_and_value);
+ }
+}
+
+/**
+ * e_named_parameters_get:
+ * @parameters: an #ENamedParameters
+ * @name: name of a parameter to get
+ *
+ * Returns current value of a parameter with name @name. If not such
+ * exists, then returns %NULL.
+ *
+ * Returns: value of a parameter named @name, or %NULL.
+ *
+ * Since: 3.8
+ **/
+const gchar *
+e_named_parameters_get (const ENamedParameters *parameters,
+ const gchar *name)
+{
+ gint index;
+ const gchar *name_and_value;
+
+ g_return_val_if_fail (parameters != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ index = get_parameter_index (parameters, name);
+ if (index == -1)
+ return NULL;
+
+ name_and_value = g_ptr_array_index (parameters, index);
+
+ return name_and_value + strlen (name) + 1;
+}
+
+/**
+ * e_named_parameters_test:
+ * @parameters: an #ENamedParameters
+ * @name: name of a parameter to test
+ * @value: value to test
+ * @case_sensitively: whether to compare case sensitively
+ *
+ * Compares current value of parameter named @name with given @value
+ * and returns whether they are equal, either case sensitively or
+ * insensitively, based on @case_sensitively argument. Function
+ * returns %FALSE, if no such parameter exists.
+ *
+ * Returns: Whether parameter of given name has stored value of given value.
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_named_parameters_test (const ENamedParameters *parameters,
+ const gchar *name,
+ const gchar *value,
+ gboolean case_sensitively)
+{
+ const gchar *stored_value;
+
+ g_return_val_if_fail (parameters != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ stored_value = e_named_parameters_get (parameters, name);
+ if (!stored_value)
+ return FALSE;
+
+ if (case_sensitively)
+ return strcmp (stored_value, value) == 0;
+
+ return g_ascii_strcasecmp (stored_value, value) == 0;
+}
void e_data_server_util_set_dbus_call_timeout
(gint timeout_msec);
+/* utility functions for easier processing of named parameters */
+typedef GPtrArray ENamedParameters;
+
+ENamedParameters * e_named_parameters_new (void);
+ENamedParameters * e_named_parameters_new_strv (const gchar * const *strv);
+void e_named_parameters_free (ENamedParameters *parameters);
+void e_named_parameters_clear (ENamedParameters *parameters);
+void e_named_parameters_assign (ENamedParameters *parameters,
+ const ENamedParameters *from);
+void e_named_parameters_set (ENamedParameters *parameters,
+ const gchar *name,
+ const gchar *value);
+const gchar * e_named_parameters_get (const ENamedParameters *parameters,
+ const gchar *name);
+gboolean e_named_parameters_test (const ENamedParameters *parameters,
+ const gchar *name,
+ const gchar *value,
+ gboolean case_sensitively);
+
G_END_DECLS
#endif /* E_DATA_SERVER_UTIL_H */
NONE:OBJECT,BOXED
+NONE:INT,STRING,STRING,STRING,STRING,BOOLEAN,POINTER
SUBDIRS = \
cache-reaper \
google-backend \
+ trust-prompt \
yahoo-backend \
$(GNOME_ONLINE_ACCOUNTS_DIR) \
$(NULL)
--- /dev/null
+NULL =
+
+module_LTLIBRARIES = module-trust-prompt.la
+
+module_trust_prompt_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"module-trust-prompt\" \
+ $(E_BACKEND_CFLAGS) \
+ $(E_DATA_SERVER_CFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(NULL)
+
+module_trust_prompt_la_SOURCES = \
+ module-trust-prompt.c \
+ $(NULL)
+
+module_trust_prompt_la_LIBADD = \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(E_BACKEND_LIBS) \
+ $(E_DATA_SERVER_LIBS) \
+ $(CAMEL_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(NULL)
+
+module_trust_prompt_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED) \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
--- /dev/null
+/*
+ * module-trust-prompt.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+
+#include <cert.h>
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TRUST_PROMPT (e_trust_prompt_get_type ())
+#define E_TRUST_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_TRUST_PROMPT, ETrustPrompt))
+
+typedef struct _ETrustPrompt ETrustPrompt;
+typedef struct _ETrustPromptClass ETrustPromptClass;
+
+struct _ETrustPrompt {
+ EUserPrompterServerExtension parent;
+
+ gboolean nss_initialized;
+};
+
+struct _ETrustPromptClass {
+ EUserPrompterServerExtensionClass parent_class;
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_trust_prompt_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (ETrustPrompt, e_trust_prompt, E_TYPE_USER_PROMPTER_SERVER_EXTENSION)
+
+static gboolean trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ const ENamedParameters *parameters);
+#define TRUST_PROMPT_DIALOG "ETrustPrompt::trust-prompt"
+
+static void
+trust_prompt_register_dialogs (EExtension *extension,
+ EUserPrompterServer *server)
+{
+ ETrustPrompt *trust_prompt = E_TRUST_PROMPT (extension);
+
+ if (!trust_prompt->nss_initialized) {
+ trust_prompt->nss_initialized = TRUE;
+
+ /* Use camel_init() to initialise NSS consistently... */
+ camel_init (e_get_user_data_dir (), TRUE);
+ }
+
+ e_user_prompter_server_register (server, extension, TRUST_PROMPT_DIALOG);
+}
+
+static gboolean
+trust_prompt_prompt (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ const gchar *dialog_name,
+ const ENamedParameters *parameters)
+{
+ if (g_strcmp0 (dialog_name, TRUST_PROMPT_DIALOG) == 0)
+ return trust_prompt_show_trust_prompt (extension, prompt_id, parameters);
+
+ return FALSE;
+}
+
+static void
+trust_prompt_finalize (GObject *object)
+{
+ ETrustPrompt *trust_prompt = E_TRUST_PROMPT (object);
+
+ if (trust_prompt->nss_initialized)
+ camel_shutdown ();
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_trust_prompt_parent_class)->finalize (object);
+}
+
+static void
+e_trust_prompt_class_init (ETrustPromptClass *class)
+{
+ GObjectClass *object_class;
+ EUserPrompterServerExtensionClass *server_extension_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = trust_prompt_finalize;
+
+ server_extension_class = E_USER_PROMPTER_SERVER_EXTENSION_CLASS (class);
+ server_extension_class->register_dialogs = trust_prompt_register_dialogs;
+ server_extension_class->prompt = trust_prompt_prompt;
+}
+
+static void
+e_trust_prompt_class_finalize (ETrustPromptClass *class)
+{
+}
+
+static void
+e_trust_prompt_init (ETrustPrompt *trust_prompt)
+{
+ trust_prompt->nss_initialized = FALSE;
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_trust_prompt_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
+
+/* dialog definitions */
+
+/* ETrustPrompt::trust-prompt
+ The dialog expects these parameters:
+ "host" - host from which the certificate is received
+ "certificate" - a base64-encoded DER certificate, for which ask on trust
+ "certificate-errors" - a hexa-decimal integer (as string) corresponding to GTlsCertificateFlags
+
+ Result of the dialog is:
+ 0 - reject
+ 1 - accept permanently
+ 2 - accept temporarily
+ -1 - user didn't choose any of the above
+
+ The dialog doesn't provide any additional values in the response.
+ */
+
+static gchar *
+cert_fingerprint (CERTCertificate *cert)
+{
+ GChecksum *checksum;
+ guint8 *digest;
+ gsize length;
+ guchar fingerprint[50], *f;
+ gint i;
+ const gchar tohex[16] = "0123456789abcdef";
+
+ length = g_checksum_type_get_length (G_CHECKSUM_MD5);
+ digest = g_alloca (length);
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, cert->derCert.data, cert->derCert.len);
+ g_checksum_get_digest (checksum, digest, &length);
+ g_checksum_free (checksum);
+
+ for (i = 0,f = fingerprint; i < length; i++) {
+ guint c = digest[i];
+
+ *f++ = tohex[(c >> 4) & 0xf];
+ *f++ = tohex[c & 0xf];
+ *f++ = ':';
+ }
+
+ fingerprint[47] = 0;
+
+ return g_strdup ((gchar *) fingerprint);
+}
+
+static gchar *
+cert_errors_to_reason (GTlsCertificateFlags flags)
+{
+ struct _convert_table {
+ GTlsCertificateFlags flag;
+ const gchar *description;
+ } convert_table[] = {
+ { G_TLS_CERTIFICATE_UNKNOWN_CA,
+ N_("The signing certificate authority is not known.") },
+ { G_TLS_CERTIFICATE_BAD_IDENTITY,
+ N_("The certificate does not match the expected identity of the site that it was retrieved from.") },
+ { G_TLS_CERTIFICATE_NOT_ACTIVATED,
+ N_("The certificate's activation time is still in the future.") },
+ { G_TLS_CERTIFICATE_EXPIRED,
+ N_("The certificate has expired.") },
+ { G_TLS_CERTIFICATE_REVOKED,
+ N_("The certificate has been revoked according to the connection's certificate revocation list.") },
+ { G_TLS_CERTIFICATE_INSECURE,
+ N_("The certificate's algorithm is considered insecure.") }
+ };
+
+ GString *reason = g_string_new ("");
+ gint ii;
+
+ for (ii = 0; ii < G_N_ELEMENTS (convert_table); ii++) {
+ if ((flags & convert_table[ii].flag) != 0) {
+ if (reason->len > 0)
+ g_string_append (reason, "\n");
+
+ g_string_append (reason, _(convert_table[ii].description));
+ }
+ }
+
+ return g_string_free (reason, FALSE);
+}
+
+static void
+trust_prompt_add_info_line (GtkGrid *grid,
+ const gchar *label_text,
+ const gchar *value_text,
+ gboolean ellipsize,
+ gint *at_row)
+{
+ GtkWidget *widget;
+ PangoAttribute *attr;
+ PangoAttrList *bold;
+
+ g_return_if_fail (grid != NULL);
+ g_return_if_fail (label_text != NULL);
+ g_return_if_fail (at_row != NULL);
+
+ if (!value_text || !*value_text)
+ return;
+
+ bold = pango_attr_list_new ();
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ pango_attr_list_insert (bold, attr);
+
+ widget = gtk_label_new (label_text);
+ gtk_misc_set_padding (GTK_MISC (widget), 12, 0);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+
+ gtk_grid_attach (grid, widget, 1, *at_row, 1, 1);
+
+ widget = gtk_label_new (value_text);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "justify", GTK_JUSTIFY_LEFT,
+ "attributes", bold,
+ "selectable", TRUE,
+ "ellipsize", ellipsize ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE,
+ NULL);
+
+ gtk_grid_attach (grid, widget, 2, *at_row, 1, 1);
+
+ *at_row = (*at_row) + 1;
+
+ pango_attr_list_unref (bold);
+}
+
+#define TRUST_PROMP_ID_KEY "ETrustPrompt::prompt-id-key"
+
+static void
+trust_prompt_response_cb (GtkWidget *dialog,
+ gint response,
+ EUserPrompterServerExtension *extension)
+{
+ gint prompt_id;
+
+ if (response == GTK_RESPONSE_HELP) {
+ /* view certificate */
+ return;
+ }
+
+ prompt_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), TRUST_PROMP_ID_KEY));
+ gtk_widget_destroy (dialog);
+
+ if (response == GTK_RESPONSE_REJECT)
+ response = 0;
+ else if (response == GTK_RESPONSE_ACCEPT)
+ response = 1;
+ else if (response == GTK_RESPONSE_YES)
+ response = 2;
+ else
+ response = -1;
+
+ e_user_prompter_server_extension_response (extension, prompt_id, response, NULL);
+}
+
+static gboolean
+trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
+ gint prompt_id,
+ const ENamedParameters *parameters)
+{
+ const gchar *host, *base64_cert, *cert_errs_str;
+ gchar *tmp, *reason, *issuer, *subject;
+ gint row = 0;
+ gint64 cert_errs;
+ GtkWidget *dialog, *widget;
+ GtkGrid *grid;
+ CERTCertDBHandle *certdb;
+ CERTCertificate *cert;
+ SECItem derCert;
+ gsize derCert_len = 0;
+
+ g_return_val_if_fail (extension != NULL, FALSE);
+ g_return_val_if_fail (parameters != NULL, FALSE);
+
+ host = e_named_parameters_get (parameters, "host");
+ base64_cert = e_named_parameters_get (parameters, "certificate");
+ cert_errs_str = e_named_parameters_get (parameters, "certificate-errors");
+
+ g_return_val_if_fail (host != NULL, FALSE);
+ g_return_val_if_fail (base64_cert != NULL, FALSE);
+ g_return_val_if_fail (cert_errs_str != NULL, FALSE);
+
+ derCert.type = siDERCertBuffer;
+ derCert.data = g_base64_decode (base64_cert, &derCert_len);
+ g_return_val_if_fail (derCert.data != NULL, FALSE);
+ derCert.len = derCert_len;
+
+ certdb = CERT_GetDefaultCertDB ();
+ cert = CERT_NewTempCertificate (certdb, &derCert, NULL, PR_FALSE, PR_TRUE);
+ g_return_val_if_fail (cert != NULL, FALSE);
+
+ cert_errs = g_ascii_strtoll (cert_errs_str, NULL, 16);
+
+ dialog = gtk_dialog_new_with_buttons (_("Certificate trust..."), NULL, 0,
+ _("_View Certificate"), GTK_RESPONSE_HELP,
+ _("_Reject"), GTK_RESPONSE_REJECT,
+ _("Accept _Temporarily"), GTK_RESPONSE_YES,
+ _("_Accept Permanently"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "evolution");
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_HELP, FALSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
+
+ grid = g_object_new (GTK_TYPE_GRID,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "row-homogeneous", FALSE,
+ "row-spacing", 2,
+ "column-homogeneous", FALSE,
+ "column-spacing", 6,
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "vexpand", TRUE,
+ "valign", GTK_ALIGN_FILL,
+ "border-width", 12,
+ NULL);
+
+ widget = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
+
+ widget = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
+ g_object_set (G_OBJECT (widget),
+ "vexpand", FALSE,
+ "valign", GTK_ALIGN_START,
+ "xpad", 6,
+ NULL);
+ gtk_grid_attach (grid, widget, 0, row, 1, 3);
+
+ reason = g_strconcat ("<b>", host, "</b>", NULL);
+ tmp = g_strdup_printf (_("SSL Certificate for '%s' is not trusted. Do you wish to accept it?\n\n"
+ "Detailed information about the certificate:"), reason);
+ g_free (reason);
+ widget = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (widget), tmp);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+ g_free (tmp);
+
+ gtk_grid_attach (grid, widget, 1, row, 2, 1);
+ row++;
+
+ issuer = CERT_NameToAscii (&cert->issuer);
+ subject = CERT_NameToAscii (&cert->subject);
+ reason = cert_errors_to_reason ((GTlsCertificateFlags) cert_errs);
+ tmp = cert_fingerprint (cert);
+
+ trust_prompt_add_info_line (grid, _("Issuer:"), issuer, TRUE, &row);
+ trust_prompt_add_info_line (grid, _("Subject:"), subject, TRUE, &row);
+ trust_prompt_add_info_line (grid, _("Fingerprint:"), tmp, TRUE, &row);
+ trust_prompt_add_info_line (grid, _("Reason:"), reason, FALSE, &row);
+
+ PORT_Free (issuer);
+ PORT_Free (subject);
+ g_free (reason);
+ g_free (tmp);
+
+ g_object_set_data (G_OBJECT (dialog), TRUST_PROMP_ID_KEY, GINT_TO_POINTER (prompt_id));
+ g_signal_connect (dialog, "response", G_CALLBACK (trust_prompt_response_cb), extension);
+
+ gtk_widget_show_all (GTK_WIDGET (grid));
+ gtk_widget_show (dialog);
+
+ CERT_DestroyCertificate (cert);
+ g_free (derCert.data);
+
+ return TRUE;
+}
libebackend/e-collection-backend.c
libebackend/e-server-side-source.c
libebackend/e-source-registry-server.c
+libebackend/e-user-prompter-server.c
libedataserver/e-categories.c
libedataserver/e-client.c
libedataserver/e-source-authenticator.c
libedataserverui/e-source-selector-dialog.c
modules/gnome-online-accounts/goaewsclient.c
modules/google-backend/module-google-backend.c
+modules/trust-prompt/module-trust-prompt.c
modules/yahoo-backend/module-yahoo-backend.c
services/evolution-addressbook-factory/evolution-addressbook-factory.c
services/evolution-calendar-factory/evolution-calendar-factory.c
+services/evolution-user-prompter/evolution-user-prompter.c
tests/libedataserverui/evolution-source-viewer.c
$(top_srcdir)/private/org.gnome.evolution.dataserver.Authenticator.xml \
$(NULL)
+$(GENERATED_DBUS_USER_PROMPTER) : Makefile.am org.gnome.evolution.dataserver.UserPrompter.xml
+ $(AM_V_GEN) gdbus-codegen \
+ --interface-prefix org.gnome.evolution.dataserver. \
+ --c-namespace E_DBus \
+ --generate-c-code e-dbus-user-prompter \
+ --generate-docbook e-dbus-user-prompter \
+ $(top_srcdir)/private/org.gnome.evolution.dataserver.UserPrompter.xml \
+ $(NULL)
+
GENERATED_DBUS_SOURCE = \
e-dbus-source.c \
e-dbus-source.h \
e-dbus-authenticator.h \
$(NULL)
+GENERATED_DBUS_USER_PROMPTER = \
+ e-dbus-user-prompter.c \
+ e-dbus-user-prompter.h \
+ $(NULL)
+
BUILT_SOURCES = \
$(GENERATED_DBUS_SOURCE) \
$(GENERATED_DBUS_SOURCE_MANAGER) \
$(GENERATED_DBUS_AUTHENTICATOR) \
+ $(GENERATED_DBUS_USER_PROMPTER) \
$(NULL)
noinst_LTLIBRARIES = libedbus-private.la
org.gnome.evolution.dataserver.Source.xml \
org.gnome.evolution.dataserver.SourceManager.xml \
org.gnome.evolution.dataserver.Authenticator.xml \
+ org.gnome.evolution.dataserver.UserPrompter.xml \
$(NULL)
CLEANFILES = \
--- /dev/null
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+<!--
+ org.gnome.evolution.dataserver.UserPrompter:
+ @short_description: UserPrompter interface
+
+ Interface for user prompts.
+-->
+<interface name="org.gnome.evolution.dataserver.UserPrompter">
+ <!--
+ Prompt:
+ @type: Type of the prompt, can be one of GtkMessageType enum values
+ @title: Window title
+ @primary_text: The primary text of the prompt
+ @secondary_text: The secondary text of the prompt
+ @use_markup: whether the text uses markup - this applies to both texts
+ @button_captions: Array of button captions, choices for a user
+ @id: Prompt identificator, used in 'Response' signal
+
+ Shows a prompt (#GtkMessageDialog) to a user of given @type with @primary_text
+ and @secondary_text, either as plain text or with markup, according to @use_markup.
+ The @button_captions can be an empty array, in which case only
+ one button will be shown, with "Dismiss" caption.
+ -->
+ <method name="Prompt">
+ <arg name="type" direction="in" type="s"/>
+ <arg name="title" direction="in" type="s"/>
+ <arg name="primary_text" direction="in" type="s"/>
+ <arg name="secondary_text" direction="in" type="s"/>
+ <arg name="use_markup" direction="in" type="b"/>
+ <arg name="button_captions" direction="in" type="as"/>
+ <arg name="id" direction="out" type="i"/>
+ </method>
+
+ <!--
+ Response:
+
+ Emitted when user responded to a Prompt.
+
+ @id: An identificator of the prompt, as returned by Prompt method
+ @response_button: Which button index was used to close the prompt
+
+ Index in the @response_button corresponds to 'button_captions' index
+ from the 'Prompt' call. If none button caption was gived, then 0 is returned.
+ -->
+ <signal name="Response">
+ <arg name="id" type="i"/>
+ <arg name="response_button" type="i"/>
+ </signal>
+
+ <!--
+ ExtensionPrompt:
+ @dialog_name: Dialog name, as defined by an extension, to show
+ @parameter: Optional parameters for the extension
+ @id: Prompt identificator, used in 'ExtensionResponse' signal
+
+ Shows a dialog provided by an extension to a user. Dialog names are
+ case sesitive. Extension can define some parameters, which are passed
+ to it within @parameters. Parameters content is not checked or otherwise
+ interpretted by the UserPrompter, all this is left to the extension itself.
+ -->
+ <method name="ExtensionPrompt">
+ <arg name="dialog_name" direction="in" type="s"/>
+ <arg name="parameters" direction="in" type="as"/>
+ <arg name="id" direction="out" type="i"/>
+ </method>
+
+ <!--
+ ExtensionResponse:
+
+ Emitted when user responded to an ExtensionPrompt.
+
+ @id: An identificator of the prompt, as returned by ExtensionPrompt method
+ @response: Generic response, as defined by the extension
+ @values: Additional values returned by the extension
+
+ Extension can return additional @values, which are not interpretted or
+ otherwise checked by the UserPrompter, all this is left to the extension
+ and its caller.
+ -->
+ <signal name="ExtensionResponse">
+ <arg name="id" type="i"/>
+ <arg name="response" type="i"/>
+ <arg name="values" type="as"/>
+ </signal>
+</interface>
evolution-addressbook-factory \
evolution-calendar-factory \
evolution-source-registry \
+ evolution-user-prompter \
$(NULL)
-include $(top_srcdir)/git.mk
--- /dev/null
+NULL =
+
+service_in_files = org.gnome.evolution.dataserver.UserPrompter.service.in
+servicedir = $(datadir)/dbus-1/services
+service_DATA = $(service_in_files:.service.in=.service)
+@EVO_SUBST_SERVICE_RULE@
+
+CLEANFILES = $(service_DATA)
+EXTRA_DIST = $(service_in_files)
+
+libexec_PROGRAMS = evolution-user-prompter
+
+evolution_user_prompter_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/private \
+ -I$(top_builddir) \
+ -I$(top_builddir)/private \
+ -DG_LOG_DOMAIN=\"evolution-user-prompter\" \
+ -DLOCALEDIR=\"$(localedir)\" \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(E_DATA_SERVER_CFLAGS) \
+ $(NULL)
+
+evolution_user_prompter_SOURCES = \
+ evolution-user-prompter.c \
+ $(NULL)
+
+evolution_user_prompter_LDADD = \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(GNOME_PLATFORM_LIBS) \
+ $(E_DATA_SERVER_LIBS) \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
--- /dev/null
+/*
+ * evolution-user-prompter.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <locale.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <libebackend/libebackend.h>
+
+#define E_USER_PROMPTER_ID_KEY "e-user-prompter-id"
+
+static void
+message_response_cb (GtkWidget *dialog,
+ gint button,
+ EUserPrompterServer *server)
+{
+ gint prompt_id;
+
+ prompt_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), E_USER_PROMPTER_ID_KEY));
+
+ gtk_widget_destroy (dialog);
+
+ g_return_if_fail (E_IS_USER_PROMPTER_SERVER (server));
+
+ e_user_prompter_server_response (server, prompt_id, button, NULL);
+}
+
+static void
+prompt_cb (EUserPrompterServer *server,
+ gint id,
+ const gchar *type,
+ const gchar *title,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ gboolean use_markup,
+ const GSList *button_captions)
+{
+ GtkMessageType ntype = GTK_MESSAGE_OTHER;
+ GtkWidget *message;
+ gint index;
+ const GSList *iter;
+
+ g_return_if_fail (server != NULL);
+
+ if (type) {
+ if (g_ascii_strcasecmp (type, "info") == 0)
+ ntype = GTK_MESSAGE_INFO;
+ else if (g_ascii_strcasecmp (type, "warning") == 0)
+ ntype = GTK_MESSAGE_WARNING;
+ else if (g_ascii_strcasecmp (type, "question") == 0)
+ ntype = GTK_MESSAGE_QUESTION;
+ else if (g_ascii_strcasecmp (type, "error") == 0)
+ ntype = GTK_MESSAGE_ERROR;
+ }
+
+ if (use_markup)
+ message = gtk_message_dialog_new_with_markup (NULL, 0, ntype, GTK_BUTTONS_NONE,
+ "%s", primary_text ? primary_text : "");
+ else
+ message = gtk_message_dialog_new (NULL, 0, ntype, GTK_BUTTONS_NONE,
+ "%s", primary_text ? primary_text : "");
+
+ /* To show dialog on a taskbar */
+ gtk_window_set_skip_taskbar_hint (GTK_WINDOW (message), FALSE);
+ gtk_window_set_title (GTK_WINDOW (message), title ? title : "");
+ gtk_window_set_icon_name (GTK_WINDOW (message), "evolution");
+
+ if (secondary_text && *secondary_text) {
+ if (use_markup)
+ gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (message),
+ "%s", secondary_text);
+ else
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message),
+ "%s", secondary_text);
+ }
+
+ g_object_set (message, "resizable", TRUE, NULL);
+
+ for (index = 0, iter = button_captions; iter; index++, iter = iter->next) {
+ gtk_dialog_add_button (GTK_DIALOG (message), iter->data, index);
+ }
+
+ if (index == 0)
+ gtk_dialog_add_button (GTK_DIALOG (message), _("_Dismiss"), index);
+
+ g_object_set_data (G_OBJECT (message), E_USER_PROMPTER_ID_KEY, GINT_TO_POINTER (id));
+
+ g_signal_connect (message, "response", G_CALLBACK (message_response_cb), server);
+
+ gtk_widget_show (message);
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ EDBusServer *server;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ gtk_init_check (&argc, &argv);
+
+ e_gdbus_templates_init_main_thread ();
+
+ server = e_user_prompter_server_new ();
+ g_signal_connect (server, "prompt", G_CALLBACK (prompt_cb), NULL);
+
+ g_print ("Prompter is up and running...\n");
+
+ e_dbus_server_run (server, TRUE);
+
+ g_object_unref (server);
+
+ g_print ("Bye.\n");
+
+ return 0;
+}
--- /dev/null
+[D-BUS Service]
+Name=@USER_PROMPTER_DBUS_SERVICE_NAME@
+Exec=@libexecdir@/evolution-user-prompter
TESTS = \
e-source-test \
e-source-registry-test \
+ e-user-prompter-test \
$(NULL)
noinst_PROGRAMS = $(TESTS)
e-test-dbus-utils.h \
$(NULL)
+e_user_prompter_test_SOURCES = \
+ e-user-prompter-test.c \
+ $(NULL)
+
e_source_test_CPPFLAGS = $(test_CPPFLAGS)
e_source_test_LDADD = $(test_LDADD)
e_source_registry_test_CPPFLAGS = $(test_CPPFLAGS)
e_source_registry_test_LDADD = $(test_LDADD)
+e_user_prompter_test_CPPFLAGS = $(test_CPPFLAGS)
+e_user_prompter_test_LDADD = $(top_builddir)/libebackend/libebackend-1.2.la $(test_LDADD)
+
-include $(top_srcdir)/git.mk
--- /dev/null
+/*
+ * e-user-prompter-test.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <libebackend/libebackend.h>
+
+typedef struct _TestClosure TestClosure;
+typedef struct _TestFixture TestFixture;
+
+struct _TestClosure {
+};
+
+struct _TestFixture {
+ EUserPrompter *prompter;
+ GMainLoop *main_loop;
+};
+
+static void
+test_fixture_setup_session (TestFixture *fixture,
+ gconstpointer user_data)
+{
+ g_assert (fixture->prompter == NULL);
+ g_assert (fixture->main_loop == NULL);
+
+ fixture->prompter = e_user_prompter_new ();
+ g_assert (fixture->prompter != NULL);
+
+ fixture->main_loop = g_main_loop_new (NULL, FALSE);
+}
+
+static void
+test_fixture_teardown_session (TestFixture *fixture,
+ gconstpointer user_data)
+{
+ g_object_unref (fixture->prompter);
+ fixture->prompter = NULL;
+
+ g_main_loop_unref (fixture->main_loop);
+ fixture->main_loop = NULL;
+}
+
+static void
+test_trust_prompt (EUserPrompter *prompter)
+{
+ const gchar *der_certificate =
+ "MIIIKzCCBxOgAwIBAgIDAMP1MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJTDEWMBQGA1UEChM"
+ "NU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZz"
+ "E4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3MgMiBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwH"
+ "hcNMTIwNDEwMDIyNzU2WhcNMTQwNDExMTQzNjUyWjCBqTEZMBcGA1UEDRMQbGZVMVd1OUVydm9EOW0z"
+ "MDELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcTBkJvc3RvbjEZMBc"
+ "GA1UEChMQR05PTUUgRm91bmRhdGlvbjEWMBQGA1UEAxMNd3d3Lmdub21lLm9yZzEjMCEGCSqGSIb3DQ"
+ "EJARYUaG9zdG1hc3RlckBnbm9tZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIG"
+ "1jjf88ZZw/oUIpXHj75jV8Fn5QrnKkWZzFzs9oIHwIhjJC/A0W1zweUzxZQZuj/r8Pn7RLj86NU8qfX"
+ "UijnXmqpNd71iapUbnmpAbkD7FGDT0Z8ekzzMQmEI1qhZt3emJUgMCqU/OFAO8ooID0oYLw6VeXFZNK"
+ "hOxNifWGSFor77r8GbeDTkIoLdm3YlaIwuiKCzA2ti/SCveFyu/oSrXw0XOJGHc1XSfpHrrQ0xHPKeH"
+ "hzpsOlTBZHZHR0fGpHpyuqhKfCA8Mp0HIJ05IeJwC4Cg41ZnPA2y1sIeos0CbNvrUHPNd+WPdZR/qdR"
+ "phh1OQgeB1bv/AnZ8pUzu9LAgMBAAGjggR1MIIEcTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNV"
+ "HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHQYDVR0OBBYEFDPkRLQJulDChRFa7dLD7DARlxdVMB8"
+ "GA1UdIwQYMBaAFBHbI0X9VMxqcW+EigPXvvcBLyaGMIHlBgNVHREEgd0wgdqCDXd3dy5nbm9tZS5vcm"
+ "eCCWdub21lLm9yZ4IObGl2ZS5nbm9tZS5vcmeCDHJ0Lmdub21lLm9yZ4ISYnVnemlsbGEuZ25vbWUub"
+ "3Jngg52b3RlLmdub21lLm9yZ4IObWFpbC5nbm9tZS5vcmeCDmxkYXAuZ25vbWUub3JnghFtZW51YmFy"
+ "Lmdub21lLm9yZ4IRd2ViYXBwcy5nbm9tZS5vcmeCD2xhYmVsLmdub21lLm9yZ4IPbWFuZ28uZ25vbWU"
+ "ub3JnghRleHRlbnNpb25zLmdub21lLm9yZzCCAiEGA1UdIASCAhgwggIUMIICEAYLKwYBBAGBtTcBAg"
+ "IwggH/MC4GCCsGAQUFBwIBFiJodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMDQGCCsGA"
+ "QUFBwIBFihodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9pbnRlcm1lZGlhdGUucGRmMIH3BggrBgEFBQcC"
+ "AjCB6jAnFiBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTADAgEBGoG+VGhpcyBjZXJ0aWZ"
+ "pY2F0ZSB3YXMgaXNzdWVkIGFjY29yZGluZyB0byB0aGUgQ2xhc3MgMiBWYWxpZGF0aW9uIHJlcXVpcm"
+ "VtZW50cyBvZiB0aGUgU3RhcnRDb20gQ0EgcG9saWN5LCByZWxpYW5jZSBvbmx5IGZvciB0aGUgaW50Z"
+ "W5kZWQgcHVycG9zZSBpbiBjb21wbGlhbmNlIG9mIHRoZSByZWx5aW5nIHBhcnR5IG9ibGlnYXRpb25z"
+ "LjCBnAYIKwYBBQUHAgIwgY8wJxYgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwAwIBAhp"
+ "kTGlhYmlsaXR5IGFuZCB3YXJyYW50aWVzIGFyZSBsaW1pdGVkISBTZWUgc2VjdGlvbiAiTGVnYWwgYW"
+ "5kIExpbWl0YXRpb25zIiBvZiB0aGUgU3RhcnRDb20gQ0EgcG9saWN5LjA1BgNVHR8ELjAsMCqgKKAmh"
+ "iRodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9jcnQyLWNybC5jcmwwgY4GCCsGAQUFBwEBBIGBMH8wOQYI"
+ "KwYBBQUHMAGGLWh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbS9zdWIvY2xhc3MyL3NlcnZlci9jYTBCBgg"
+ "rBgEFBQcwAoY2aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMvc3ViLmNsYXNzMi5zZXJ2ZXIuY2"
+ "EuY3J0MCMGA1UdEgQcMBqGGGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tLzANBgkqhkiG9w0BAQUFAAOCA"
+ "QEAZ+mc7DDWtNBPiWmM62/rWv6c6l77CMLfAfWks8YDf8T69/agJfA+I7tJyP0WoviyVHaoD2LS8FcU"
+ "RQNFErk+8ry50d3NFFZaAfKRMFkob6gBx19YdQ64n0ZvS/0+a+ye6NL/yttjLE0ynei8nPmmgzaf5M3"
+ "3zOMCaTr7Cq6SJqnlrYUYbdBkobjadcfG2eAKfbhOiVGVEOee4O6JJ+nCrqXpqj42EGuQ8mKvl7Kao+"
+ "xerxctag0jzlLRFWJ69l7DZZyyFzY+/I9IWSVj8i0VCz0FkulK9adKeYD4E4BAOQvDFY4ED2FckW3AZ"
+ "zVueeiqTSIKwkDFhSDwTJsIfsOaEQ==";
+ ENamedParameters *parameters;
+ GError *error = NULL;
+ gint result;
+
+ g_return_if_fail (prompter != NULL);
+
+ parameters = e_named_parameters_new ();
+
+ e_named_parameters_set (parameters, "host", "https://bugzilla.gnome.org/");
+ e_named_parameters_set (parameters, "certificate", der_certificate);
+ e_named_parameters_set (parameters, "certificate-errors", "007f");
+
+ result = e_user_prompter_extension_prompt_sync (prompter, "ETrustPrompt::trust-prompt", parameters, NULL, NULL, &error);
+
+ g_print ("Trust prompt result: %s (%d)%s%s\n", result == 0 ? "Reject" :
+ result == 1 ? "Accept permanently" :
+ result == 2 ? "Accept temporarily" : "Unknown",
+ result,
+ error ? ", error: " : "",
+ error ? error->message : "");
+ g_assert_no_error (error);
+
+ e_named_parameters_free (parameters);
+
+ /* test for an unknown dialog prompt */
+ result = e_user_prompter_extension_prompt_sync (prompter, "Uknown-Dialog-Prompt", NULL, NULL, NULL, &error);
+
+ g_print ("Unknown dialog prompt, result:%d, error: %s\n", result, error ? error->message : "None");
+
+ g_assert (result == -1);
+ g_assert (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND));
+
+ g_clear_error (&error);
+}
+
+struct _Prompts {
+ const gchar *type;
+ const gchar *primary;
+ const gchar *secondary;
+ gboolean use_markup;
+ const gchar *buttons;
+} prompts[] = {
+ { "info", "%d) <u>info</u> primary text", "info <i>secondary</i> text\nmarkup", TRUE, NULL },
+ { "warning", "%d) warning primary text", "warning secondary text", FALSE, NULL },
+ { "question", "%d) <u>question</u> primary text", "question <i>secondary</i> text\nmarkup text, but not used as markup", FALSE, NULL },
+ { "error", "%d) error primary text", "error <i>secondary</i> text\nmarkup", TRUE, NULL },
+ { "other", "%d) other primary text", "other <i>secondary</i> text\nmarkup", TRUE, NULL },
+ { "#$%@$#%", "%d) totally unknown type primary text", "totally unknown type secondary text\nmarkup without markup texts", TRUE, NULL },
+ { "", NULL, "%d) a very long secondary text, with no primary text and no icon,"
+ " which should wrap ideally, and be on multiple lines, like one may"
+ " expect for such long messages, even without markup", FALSE, NULL },
+ { "", "%d) a very long primary text, with no secondary text and no icon,"
+ " which should wrap ideally, and be on multiple lines, like one may"
+ " expect for such long messages, even without markup", NULL, FALSE, NULL },
+ { "", "%d) This one has primary text...", "...and secondary text, and 5 buttons", FALSE, "1st button:2nd button:3rd button:4th button:5th button" }
+};
+
+static void
+user_prompt_respond_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ gint result_button;
+ GError *error = NULL;
+
+ result_button = e_user_prompter_prompt_finish (E_USER_PROMPTER (source), result, &error);
+
+ g_print (" Prompt [%d] returned %d%s%s\n", GPOINTER_TO_INT (user_data), result_button,
+ error ? ", error: " : "", error ? error->message : "");
+
+ g_assert_no_error (error);
+}
+
+static gboolean
+quit_main_loop_cb (gpointer main_loop)
+{
+ g_main_loop_quit (main_loop);
+ return FALSE;
+}
+
+static gboolean
+test_user_prompts_idle_cb (gpointer user_data)
+{
+ TestFixture *fixture = user_data;
+ gint ii, sz;
+ GMainContext *main_context = g_main_loop_get_context (fixture->main_loop);
+ GSource *source;
+ GError *error = NULL;
+
+ /* all but the last run asynchronously, to test they will come
+ in the right order and only one at a time, and then run
+ the last synchronously, to wait for the result */
+ sz = G_N_ELEMENTS (prompts);
+ for (ii = 0; ii < sz && !error; ii++) {
+ gchar *title, *primary, *secondary, **buttons = NULL;
+ GSList *captions = NULL;
+
+ title = g_strdup_printf ("Prompt %d...", ii);
+ primary = g_strdup_printf (prompts[ii].primary, ii);
+ secondary = g_strdup_printf (prompts[ii].secondary, ii);
+ if (prompts[ii].buttons) {
+ gint jj;
+
+ buttons = g_strsplit (prompts[ii].buttons, ":", -1);
+ for (jj = 0; buttons[jj]; jj++) {
+ captions = g_slist_append (captions, buttons[jj]);
+ }
+ }
+
+ if (ii + 1 == sz) {
+ gint result_button;
+
+ result_button = e_user_prompter_prompt_sync (fixture->prompter,
+ prompts[ii].type, title, primary, secondary, prompts[ii].use_markup, captions,
+ NULL, &error);
+ g_print (" Prompt [%d] (sync) returned %d%s%s\n", ii, result_button,
+ error ? ", error: " : "", error ? error->message : "");
+ } else {
+ e_user_prompter_prompt (fixture->prompter,
+ prompts[ii].type, title, primary, secondary, prompts[ii].use_markup, captions,
+ NULL, user_prompt_respond_cb, GINT_TO_POINTER (ii));
+
+ /* give it a chance to be delivered in this order */
+ g_main_context_iteration (main_context, FALSE);
+
+ g_usleep (G_USEC_PER_SEC);
+ }
+
+ g_free (title);
+ g_free (primary);
+ g_free (secondary);
+ g_strfreev (buttons);
+ g_slist_free (captions);
+ }
+
+ g_assert_no_error (error);
+
+ test_trust_prompt (fixture->prompter);
+
+ g_print ("Waiting 5 seconds for response deliveries...\n");
+ source = g_timeout_source_new_seconds (5);
+ g_source_set_callback (source, quit_main_loop_cb, fixture->main_loop, NULL);
+ g_source_attach (source, main_context);
+
+ return FALSE;
+}
+
+static void
+test_user_prompts (TestFixture *fixture,
+ gconstpointer user_data)
+{
+ g_idle_add (test_user_prompts_idle_cb, fixture);
+ g_main_loop_run (fixture->main_loop);
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ TestClosure closure;
+ gint retval;
+
+ g_type_init ();
+
+ g_test_init (&argc, &argv, NULL);
+ g_test_bug_base ("http://bugzilla.gnome.org/");
+
+ g_test_add (
+ "/e-user-prompter-test/UserPrompts",
+ TestFixture, &closure,
+ test_fixture_setup_session,
+ test_user_prompts,
+ test_fixture_teardown_session);
+
+ retval = g_test_run ();
+
+ return retval;
+}