- closes rhbz#901467

Signed-off-by: Jakub Filak <[email protected]>
---
 src/gtk-helpers/secrets.c | 132 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 118 insertions(+), 14 deletions(-)

diff --git a/src/gtk-helpers/secrets.c b/src/gtk-helpers/secrets.c
index 9de2284..c3c1f68 100644
--- a/src/gtk-helpers/secrets.c
+++ b/src/gtk-helpers/secrets.c
@@ -34,6 +34,9 @@
 /* 5s timeout*/
 #define SECRETS_CALL_DEFAULT_TIMEOUT 5000
 
+/* 15s until the timeout dialog will appear  */
+#define PROMPT_TIMEOUT_SECONDS 15
+
 /* Well known errors for which we have workarounds */
 #define NOT_IMPLEMENTED_READ_ALIAS_ERROR 
"org.freedesktop.DBus.Error.UnknownMethod"
 #define INVALID_PROPERTIES_ARGUMENTS_ERROR 
"org.freedesktop.DBus.Error.InvalidArgs"
@@ -377,24 +380,41 @@ static void secrets_service_set_unavailable(void)
 /* http://standards.freedesktop.org/secret-service/re05.html                   
*/
 
/*******************************************************************************/
 
+/*
+ * Holds all data necessary for management of the glib main loop with the
+ * timeout dialog.
+ */
+struct secrets_loop_env
+{
+    GMainContext* gcontext;
+    GMainLoop *gloop;
+    GtkWidget *timeout_dialog;
+};
+
 struct prompt_source
 {
     GSource source;
     GDBusProxy *prompt_proxy;
-    GMainLoop *loop;
+    struct secrets_loop_env *env;
 };
 
 struct prompt_call_back_args
 {
     gboolean dismissed;
     GVariant *result;
-    GMainLoop *loop;
+    struct secrets_loop_env *env;
 };
 
-static void prompt_quit_loop(GMainLoop *loop)
+static void prompt_quit_loop(struct secrets_loop_env *env)
 {
-    if (g_main_loop_is_running(loop))
-        g_main_loop_quit(loop);
+    if (NULL != env->timeout_dialog)
+    {
+        gtk_widget_destroy(env->timeout_dialog);
+        env->timeout_dialog = NULL;
+    }
+
+    if (g_main_loop_is_running(env->gloop))
+        g_main_loop_quit(env->gloop);
 }
 
 static gboolean prompt_g_idle_prepare(GSource *source_data, gint *timeout)
@@ -425,7 +445,7 @@ static gboolean prompt_g_idle_dispatch(GSource *source,
     if (!resp)
         /* We have to kill the loop because a dbus call failed and the signal 
*/
         /* which stops the loop won't be received */
-        prompt_quit_loop(prompt_source->loop);
+        prompt_quit_loop(prompt_source->env);
     else
         g_variant_unref(resp);
 
@@ -454,7 +474,7 @@ static void prompt_call_back(GDBusConnection *connection, 
const gchar *sender_na
         /* if the prompt or operation were dismissed we don't care about 
result */
         g_variant_unref(result);
 
-    prompt_quit_loop(args->loop);
+    prompt_quit_loop(args->env);
 }
 
 /*
@@ -467,6 +487,83 @@ static bool is_prompt_required(const char *prompt_path)
 }
 
 /*
+ * Sets the timeout dialog to be displayed after PROMPT_TIMEOUT_SECONDS.
+ *
+ * @param env Crate holding necessary values.
+ */
+static void prompt_timeout_start(struct secrets_loop_env *env);
+
+/*
+ * A response callback which stops main loop execution or starts a new timeout.
+ *
+ * The main loop is stopped if user replied with "YES"; otherwise a new timeout
+ * is started.
+ */
+static void timeout_dialog_response_cb(GtkDialog *dialog, gint response_id, 
gpointer user_data)
+{
+    struct secrets_loop_env *env = (struct secrets_loop_env *)user_data;
+
+    gtk_widget_hide(env->timeout_dialog);
+
+    /* YES means 'Yes I want to stop wainting for Secret Service' */
+    /* for other responses we have to start a new timeout */
+    if (GTK_RESPONSE_YES == response_id)
+        prompt_quit_loop(env);
+    else
+        prompt_timeout_start(env);
+}
+
+/*
+ * Shows the timeout dialog which should allow user to break infinite wainting
+ * for Completed signal emitted by the Secret Service
+ *
+ * @param user_data Should hold an pointer to struct secrets_loop_env
+ */
+static gboolean prompt_timeout_cb(gpointer user_data)
+{
+    VERB3 log("A timeout was reached while waiting for the DBus Secret Service 
to get prompt result.");
+    struct secrets_loop_env *env = (struct secrets_loop_env *)user_data;
+
+    if (env->timeout_dialog == NULL)
+    {
+        GtkWidget *dialog = gtk_message_dialog_new(/*parent widget*/NULL,
+                                                   GTK_DIALOG_MODAL,
+                                                   GTK_MESSAGE_WARNING,
+                                                   GTK_BUTTONS_YES_NO,
+                _("A timeout was reached while waiting for a prompt result 
from the DBus Secret Service."));
+
+        gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+                _("Do you want to stop waiting and continue in reporting 
without properly loaded configuration?"));
+
+        g_signal_connect(dialog, "response", 
G_CALLBACK(timeout_dialog_response_cb), user_data);
+
+        env->timeout_dialog = dialog;
+    }
+
+    gtk_widget_show_all(env->timeout_dialog);
+
+    return FALSE; /* remove this source */
+}
+
+/*
+ * Sets the timeout dialog to be displayed after PROMPT_TIMEOUT_SECONDS.
+ *
+ * @param env Crate holding necessary values.
+ */
+static void prompt_timeout_start(struct secrets_loop_env *env)
+{
+    GSource *timeout_source= 
g_timeout_source_new_seconds(PROMPT_TIMEOUT_SECONDS);
+
+    g_source_set_callback(timeout_source, prompt_timeout_cb, /*callback 
arg*/env, /*arg destroyer*/NULL);
+    g_source_attach(timeout_source, env->gcontext);
+
+    /* remove local reference, the source will be destroyed by the GMainContext
+     * over taken from glib2/gmain.c: g_timeout_add_full()
+     */
+    g_source_unref(timeout_source);
+}
+
+/*
  * Perform a prompt
  *
  * @param prompt_path An object path pointing to a prompt
@@ -496,12 +593,16 @@ static GVariant *secrets_prompt(const char *prompt_path,
     /* We have to use the thread default main context because a dbus signal 
callback */
     /* will be invoked in the thread default main loop */
     GMainContext *context = g_main_context_get_thread_default();
-    GMainLoop *loop = g_main_loop_new(context, FALSE);
+    struct secrets_loop_env env = {
+        .gcontext=context,
+        .gloop=g_main_loop_new(context, FALSE),
+        .timeout_dialog=NULL,
+    };
 
     struct prompt_call_back_args args = {
         .result=NULL,
         .dismissed=FALSE,
-        .loop=loop
+        .env=&env
     };
 
     /* g_dbus_connection_signal_subscribe() doesn't report any error */
@@ -517,22 +618,25 @@ static GVariant *secrets_prompt(const char *prompt_path,
     /* Idle source simply performs a prompt after the loop is stared. */
     struct prompt_source *const prompt_source =
         (struct prompt_source*)g_source_new(&idle_funcs, 
sizeof(*prompt_source));
-
     prompt_source->prompt_proxy = prompt_proxy;
+    prompt_source->env = &env;
 
     g_source_attach((GSource*)prompt_source, context);
 
-    /* the loop is exited when the Completed signal is received */
     /* the loop may sucks in infinite loop if the signal is never received */
-    /* TODO : if timeout is required use g_timeout_source_new() and 
g_source_attach() */
-    g_main_loop_run(loop);
+    /* thus in order to prevent it we use this timeout */
+    prompt_timeout_start(&env);
+
+    /* the loop is exited when the Completed signal is received */
+    g_main_loop_run(env.gloop);
 
     /* destroy prompt source */
     g_object_unref(prompt_proxy);
     g_source_destroy((GSource*)prompt_source);
+    g_source_unref((GSource*)prompt_source);
 
     g_dbus_connection_signal_unsubscribe(g_connection, signal_ret);
-    g_main_loop_unref(loop);
+    g_main_loop_unref(env.gloop);
 
     *dismissed = args.dismissed;
     return args.result;
-- 
1.8.1.2

Reply via email to