Update to upstream 1.0.1
[profile/ivi/gsignond.git] / src / daemon / plugins / gsignond-plugin-proxy.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2012-2014 Intel Corporation.
7  *
8  * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include "gsignond/gsignond-error.h"
27 #include "gsignond/gsignond-log.h"
28 #include "gsignond/gsignond-config.h"
29 #include "daemon/gsignond-auth-session.h"
30 #include "gsignond-plugin-proxy.h"
31 #include "gsignond-plugin-remote.h"
32
33 #define GSIGNOND_PLUGIN_PROXY_PRIV(obj) \
34     G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
35                                  GSIGNOND_TYPE_PLUGIN_PROXY, \
36                                  GSignondPluginProxyPrivate)
37
38 enum
39 {
40     PROP_0,
41     
42     PROP_TYPE,
43     PROP_MECHANISMS,
44     PROP_LOADERPATH,
45     
46     N_PROPERTIES
47 };
48
49 struct _GSignondPluginProxyPrivate
50 {
51     gchar* loader_path;
52     gchar* plugin_type;
53     GSignondPlugin* plugin;
54     GQueue* session_queue;
55     GSignondAuthSession* active_session;
56     gpointer active_process_userdata;
57     gboolean expecting_request;
58 };
59
60 typedef struct {
61     GSignondAuthSession* auth_session;
62     GSignondSessionData* session_data;
63     GSignondDictionary* identity_method_cache;
64     gchar* mechanism;
65     gpointer userdata;
66 } GSignondProcessData;
67
68 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
69
70 G_DEFINE_TYPE (GSignondPluginProxy, gsignond_plugin_proxy, GSIGNOND_TYPE_DISPOSABLE);
71
72 static GSignondProcessData*
73 gsignond_process_data_new (
74         GSignondAuthSession* auth_session,
75         GSignondSessionData *session_data,
76         GSignondDictionary *identity_method_cache,
77         const gchar* mechanism,
78         gpointer userdata)
79 {
80     GSignondProcessData* data = g_slice_new0 (GSignondProcessData);
81     data->auth_session = g_object_ref (auth_session);
82     data->session_data = gsignond_dictionary_copy (session_data);
83     if (identity_method_cache)
84         data->identity_method_cache = gsignond_dictionary_copy (identity_method_cache);
85     data->mechanism = g_strdup (mechanism);
86     data->userdata = userdata;
87     return data;
88 }
89
90 static void
91 gsignond_process_data_free (
92         GSignondProcessData* data)
93 {
94     g_object_unref (data->auth_session);
95     gsignond_dictionary_unref (data->session_data);
96     if (data->identity_method_cache)
97         gsignond_dictionary_unref (data->identity_method_cache);
98     g_free (data->mechanism);
99     g_slice_free (GSignondProcessData, data);
100 }
101
102 static void
103 gsignond_plugin_proxy_process_queue (
104         GSignondPluginProxy *self)
105 {
106     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
107
108     GSignondPluginProxyPrivate *priv = self->priv;
109     GSignondProcessData* next_data = g_queue_pop_head (priv->session_queue);
110     if (next_data) {
111         priv->expecting_request = FALSE;
112         priv->active_process_userdata = next_data->userdata;
113         priv->active_session = next_data->auth_session;
114         g_object_ref (priv->active_session);
115         gsignond_auth_session_notify_state_changed (
116                 priv->active_session, GSIGNOND_PLUGIN_STATE_STARTED,
117                 "The request is being processed.",
118                 priv->active_process_userdata);
119         gsignond_plugin_request_initial (priv->plugin,
120                                          next_data->session_data,
121                                          next_data->identity_method_cache,
122                                          next_data->mechanism);
123         gsignond_process_data_free (next_data);
124     }
125 }
126
127 static void
128 gsignond_plugin_proxy_response_final_callback (
129         GSignondPlugin *plugin,
130         GSignondSessionData *result,
131         gpointer user_data)
132 {
133     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
134     GSignondPluginProxyPrivate *priv = self->priv;
135
136     DBG ("");
137     if (priv->active_session == NULL) {
138         WARN("Error: plugin %s reported 'response_final', but no active session"
139                 " in plugin proxy", priv->plugin_type);
140         return;
141     }
142     gsignond_auth_session_notify_process_result (priv->active_session, result,
143             priv->active_process_userdata);
144     g_object_unref (priv->active_session);
145     priv->active_session = NULL;
146     priv->active_process_userdata = NULL;
147     gsignond_plugin_proxy_process_queue (self);
148 }
149
150 static void
151 gsignond_plugin_proxy_response_callback(
152         GSignondPlugin *plugin,
153         GSignondSessionData *result,
154         gpointer user_data)
155 {
156     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
157     GSignondPluginProxyPrivate *priv = self->priv;
158
159     if (priv->active_session == NULL) {
160         WARN("Error: plugin %s reported 'response', but no active session "
161                 "in plugin proxy", priv->plugin_type);
162         return;
163     }
164     priv->expecting_request = TRUE;
165     gsignond_auth_session_notify_process_result (priv->active_session,
166             result, priv->active_process_userdata);
167 }
168
169 static void
170 gsignond_plugin_proxy_store_callback (
171         GSignondPlugin *plugin,
172         GSignondSessionData *result,
173         gpointer user_data)
174 {
175     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
176     GSignondPluginProxyPrivate *priv = self->priv;
177
178     if (priv->active_session == NULL) {
179         WARN("Error: plugin %s reported 'store', but no active session "
180                 "in plugin proxy", priv->plugin_type);
181         return;
182     }
183     gsignond_auth_session_notify_store (priv->active_session, result);
184 }
185
186 static void
187 gsignond_plugin_proxy_refreshed_callback (
188         GSignondPlugin *plugin,
189         GSignondSignonuiData *ui_result,
190         gpointer user_data)
191 {
192     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
193     GSignondPluginProxyPrivate *priv = self->priv;
194
195     if (priv->active_session == NULL) {
196         WARN("Error: plugin %s reported 'refreshed', but no active session"
197                 " in plugin proxy", priv->plugin_type);
198         return;
199     }
200     gsignond_auth_session_notify_refreshed (priv->active_session, ui_result);
201 }
202
203 static void
204 gsignond_plugin_proxy_user_action_required_callback (
205         GSignondPlugin *plugin,
206         GSignondSignonuiData *ui_request,
207         gpointer user_data)
208 {
209     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
210     GSignondPluginProxyPrivate *priv = self->priv;
211
212     if (priv->active_session == NULL) {
213         WARN("Error: plugin %s reported 'user_action_required', but no active"
214                 " session in plugin proxy", priv->plugin_type);
215         return;
216     }
217     gsignond_auth_session_notify_user_action_required(
218         priv->active_session, ui_request);
219 }
220
221 static void
222 gsignond_plugin_proxy_error_callback (
223         GSignondPlugin* plugin,
224         GError* error,
225         gpointer user_data)
226 {
227     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
228     GSignondPluginProxyPrivate *priv = self->priv;
229
230     if (priv->active_session == NULL) {
231         WARN("Error: plugin %s reported error %s, but no active session "
232                 "in plugin proxy", priv->plugin_type, error->message);
233         return;
234     }
235     gsignond_auth_session_notify_process_error (priv->active_session, error,
236             priv->active_process_userdata);
237     g_object_unref (priv->active_session);
238     priv->active_session = NULL;
239     priv->active_process_userdata = NULL;
240     gsignond_plugin_proxy_process_queue (self);
241 }
242
243 static void
244 gsignond_plugin_proxy_status_changed_callback (
245         GSignondPlugin *plugin,
246         GSignondPluginState state,
247         const gchar *message,
248         gpointer user_data)
249 {
250     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
251     GSignondPluginProxyPrivate *priv = self->priv;
252
253     if (priv->active_session == NULL) {
254         WARN("Error: plugin %s reported change in state %d with message %s, "
255                 "but no active session in plugin proxy", priv->plugin_type,
256                 state, message);
257         return;
258     }
259     gsignond_auth_session_notify_state_changed (priv->active_session,
260                                                 (gint) state, message,
261                                                 priv->active_process_userdata);
262 }
263
264 static void
265 _on_remote_plugin_dead (gpointer data, GObject *dead_obj)
266 {
267 DBG("{");
268     GSignondPluginProxy *proxy = NULL;
269     if (data && (proxy = GSIGNOND_PLUGIN_PROXY(data))) {
270         proxy->priv->plugin = NULL;
271         g_object_unref (G_OBJECT(data));
272     }
273 DBG("}");
274 }
275
276 static GObject *
277 gsignond_plugin_proxy_constructor (
278         GType                  gtype,
279         guint                  n_properties,
280         GObjectConstructParam *properties)
281 {
282     GObject *obj;
283
284     /* Always chain up to the parent constructor */
285     obj = G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->
286         constructor (gtype, n_properties, properties);
287   
288     /* update the object state depending on constructor properties */
289     GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY (obj);
290     GSignondPluginProxyPrivate *priv = self->priv;
291     priv->plugin = GSIGNOND_PLUGIN (gsignond_plugin_remote_new (priv->loader_path,
292             priv->plugin_type));
293
294     if (priv->plugin == NULL) {
295         g_free (priv->plugin_type);
296         priv->plugin_type = NULL;
297
298     } else {
299         gchar *type = NULL;
300
301         g_signal_connect(priv->plugin, "response", G_CALLBACK(
302             gsignond_plugin_proxy_response_callback), self);
303         g_signal_connect(priv->plugin, "response-final", G_CALLBACK(
304             gsignond_plugin_proxy_response_final_callback), self);
305         g_signal_connect(priv->plugin, "user-action-required", G_CALLBACK(
306             gsignond_plugin_proxy_user_action_required_callback), self);
307         g_signal_connect(priv->plugin, "error", G_CALLBACK(
308             gsignond_plugin_proxy_error_callback), self);
309         g_signal_connect(priv->plugin, "store", G_CALLBACK(
310             gsignond_plugin_proxy_store_callback), self);
311         g_signal_connect(priv->plugin, "refreshed", G_CALLBACK(
312             gsignond_plugin_proxy_refreshed_callback), self);
313         g_signal_connect(priv->plugin, "status-changed", G_CALLBACK(
314             gsignond_plugin_proxy_status_changed_callback), self);
315
316         g_object_get (priv->plugin, "type", &type, NULL);
317         if (g_strcmp0 (type, priv->plugin_type) != 0) {
318             g_free (priv->plugin_type);
319             priv->plugin_type = NULL;
320         }
321         g_free (type);
322
323         g_object_weak_ref (G_OBJECT(priv->plugin), _on_remote_plugin_dead, obj);
324
325     }
326     return obj;
327 }
328
329 static void
330 gsignond_plugin_proxy_set_property (
331         GObject      *object,
332         guint         property_id,
333         const GValue *value,
334         GParamSpec   *pspec)
335 {
336     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (object);
337     GSignondPluginProxyPrivate *priv = self->priv;
338
339     switch (property_id)
340     {
341         case PROP_TYPE:
342             g_free (self->priv->plugin_type);
343             priv->plugin_type = g_value_dup_string (value);
344             break;
345         case PROP_LOADERPATH:
346             g_assert (self->priv->loader_path == NULL);
347             priv->loader_path = g_value_dup_string (value);
348             break;
349         default:
350             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
351             break;
352     }
353 }
354
355 static void
356 gsignond_plugin_proxy_get_property (
357         GObject    *object,
358         guint       prop_id,
359         GValue     *value,
360         GParamSpec *pspec)
361 {
362     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (object);
363     GSignondPluginProxyPrivate *priv = self->priv;
364     
365     switch (prop_id)
366     {
367         case PROP_TYPE:
368             g_value_set_string (value, priv->plugin_type);
369             break;
370         case PROP_MECHANISMS:
371             g_object_get_property (G_OBJECT(priv->plugin),
372                                    "mechanisms", value);
373             break;
374         case PROP_LOADERPATH:
375             g_value_set_string (value, priv->loader_path);
376             break;
377         default:
378             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
379             break;
380     }
381 }
382
383 static void
384 gsignond_plugin_proxy_dispose (
385         GObject *gobject)
386 {
387     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (gobject);
388     GSignondPluginProxyPrivate *priv = self->priv;
389
390     if (priv->active_session) {
391         g_object_unref (priv->active_session);
392         priv->active_session = NULL;
393     }
394     if (priv->plugin) {
395         g_object_weak_unref (G_OBJECT(priv->plugin), _on_remote_plugin_dead, self);
396         g_object_unref (priv->plugin);
397         priv->plugin = NULL;
398     }
399
400   /* Chain up to the parent class */
401   G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->dispose (gobject);
402 }
403
404 static void
405 gsignond_plugin_proxy_finalize (GObject *gobject)
406 {
407     GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (gobject);
408     GSignondPluginProxyPrivate *priv = self->priv;
409
410     if (priv->plugin_type) {
411         g_free (priv->plugin_type);
412         priv->plugin_type = NULL;
413     }
414     if (priv->loader_path) {
415         g_free (priv->loader_path);
416         priv->loader_path = NULL;
417     }
418     if (priv->session_queue)
419     {
420         g_queue_free_full (priv->session_queue,
421                            (GDestroyNotify) gsignond_process_data_free);
422         priv->session_queue = NULL;
423     }
424
425     /* Chain up to the parent class */
426     G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->finalize (gobject);
427 }
428
429
430 static void
431 gsignond_plugin_proxy_class_init (
432         GSignondPluginProxyClass *klass)
433 {
434     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
435
436     g_type_class_add_private (gobject_class,
437                               sizeof (GSignondPluginProxyPrivate)); 
438
439     gobject_class->constructor = gsignond_plugin_proxy_constructor;
440     gobject_class->set_property = gsignond_plugin_proxy_set_property;
441     gobject_class->get_property = gsignond_plugin_proxy_get_property;
442     gobject_class->dispose = gsignond_plugin_proxy_dispose;
443     gobject_class->finalize = gsignond_plugin_proxy_finalize;
444
445     obj_properties[PROP_TYPE] =
446     g_param_spec_string ("type",
447                          "Plugin type",
448                          "Set the plugin type for the proxy",
449                          "" /* default value */,
450                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
451                          G_PARAM_STATIC_STRINGS);
452     
453     obj_properties[PROP_MECHANISMS] = g_param_spec_boxed ("mechanisms", 
454                             "Mechanisms", 
455                             "List of plugin mechanisms", 
456                             G_TYPE_STRV, G_PARAM_READABLE);
457
458     obj_properties[PROP_LOADERPATH] = g_param_spec_string ("loaderpath",
459                                                    "Path to loader",
460                                                    "Path to plugin loader for this plugin",
461                                                    "" /* default value */,
462                                                    G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
463                                                    G_PARAM_STATIC_STRINGS);
464
465     g_object_class_install_properties (gobject_class,
466                                        N_PROPERTIES,
467                                        obj_properties);
468
469 }
470
471 static void
472 gsignond_plugin_proxy_init (
473         GSignondPluginProxy *self)
474 {
475     GSignondPluginProxyPrivate *priv = GSIGNOND_PLUGIN_PROXY_PRIV (self);
476     self->priv = priv;
477
478     priv->loader_path = NULL;
479     priv->plugin_type = NULL;
480     priv->plugin = NULL;
481     priv->session_queue = g_queue_new ();
482     priv->active_session = NULL;
483     priv->active_process_userdata = NULL;
484     priv->expecting_request = FALSE;
485 }
486
487 static const gchar *
488 gsignond_plugin_proxy_get_plugin_type (
489         GSignondPluginProxy *self)
490 {
491     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
492
493     return self->priv->plugin_type;
494 }
495
496 GSignondPluginProxy* 
497 gsignond_plugin_proxy_new (
498         const gchar *loader_path,
499         const gchar *plugin_type,
500         gint timeout)
501 {
502     g_return_val_if_fail (loader_path && plugin_type, NULL);
503
504     GSignondPluginProxy* proxy = g_object_new (GSIGNOND_TYPE_PLUGIN_PROXY,
505                                                "loaderpath", loader_path,
506                                                "type", plugin_type,
507                                                "auto-dispose", FALSE,
508                                                "timeout", timeout,
509                                                NULL);
510     if (g_strcmp0 (plugin_type,
511                    gsignond_plugin_proxy_get_plugin_type (proxy)) == 0)
512         return proxy;
513
514     g_object_unref (proxy);
515     return NULL;
516 }
517
518 void
519 gsignond_plugin_proxy_process (
520         GSignondPluginProxy *self,
521         GSignondAuthSession *session,
522         GSignondSessionData *session_data,
523         GSignondDictionary *identity_method_cache,
524         const gchar *mechanism,
525         gpointer userdata)
526 {
527     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
528     g_assert (GSIGNOND_IS_AUTH_SESSION (session));
529
530     GSignondPluginProxyPrivate *priv = self->priv;
531
532     if (session == priv->active_session && priv->expecting_request == TRUE) {
533         priv->expecting_request = FALSE;
534         // mechanism and identity_method_cache are discarded if this is not an initial request
535         gsignond_plugin_request (priv->plugin, session_data);
536         return;
537     }
538
539     g_queue_push_tail (priv->session_queue,
540                        gsignond_process_data_new (session,
541                                                   session_data, 
542                                                   identity_method_cache,
543                                                   mechanism, userdata));
544     gsignond_auth_session_notify_state_changed (
545             session, GSIGNOND_PLUGIN_STATE_PROCESS_PENDING,
546             "The request has been queued.", userdata);
547     if (priv->active_session == NULL) {
548         gsignond_plugin_proxy_process_queue (self);
549     }
550 }
551
552 static gint
553 gsignond_plugin_proxy_compare_process_data (
554         gconstpointer process_data,
555         gconstpointer auth_session)
556 {
557     g_return_val_if_fail (process_data && auth_session, 0);
558
559     if (auth_session == ((GSignondProcessData*) process_data)->auth_session)
560         return 0;
561     else
562         return 1;
563 }
564
565 static GSignondProcessData*
566 gsignond_plugin_proxy_find_by_session_iface (
567         GSignondPluginProxy *self,
568         GSignondAuthSession *session)
569 {
570     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
571
572     return (GSignondProcessData*) g_queue_find_custom (
573                                     self->priv->session_queue, 
574                                     session,
575                                     gsignond_plugin_proxy_compare_process_data);
576 }
577
578 void 
579 gsignond_plugin_proxy_cancel (
580         GSignondPluginProxy *self,
581         GSignondAuthSession *session)
582 {
583     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
584     g_assert (GSIGNOND_IS_AUTH_SESSION (session));
585
586     GSignondPluginProxyPrivate *priv = self->priv;
587
588     /* cancel active session */
589     if (session == priv->active_session) {
590         gsignond_plugin_cancel (priv->plugin);
591     } else { /* cancel by de-queue */
592         GSignondProcessData* data =
593             gsignond_plugin_proxy_find_by_session_iface (self, session);
594         if (!data) {
595             GError* error = g_error_new (GSIGNOND_ERROR, 
596                                          GSIGNOND_ERROR_WRONG_STATE,
597                                          "Canceling an unknown session");
598             gsignond_auth_session_notify_process_error (session, error, NULL);
599             g_error_free (error);
600             return;
601         }
602         g_queue_remove (priv->session_queue, data);
603         gsignond_process_data_free (data);
604     }
605 }
606
607 void
608 gsignond_plugin_proxy_user_action_finished (
609         GSignondPluginProxy *self,
610         GSignondSignonuiData *ui_data)
611 {
612     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
613
614     if (self->priv->active_session == NULL) {
615         WARN("Error: 'user_action_finished' requested for plugin %s but no "
616                 "active session", self->priv->plugin_type);
617         return;
618     }
619     gsignond_plugin_user_action_finished (self->priv->plugin, ui_data);
620 }
621
622 void
623 gsignond_plugin_proxy_refresh (
624         GSignondPluginProxy *self,
625         GSignondSignonuiData *ui_data)
626 {
627     g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
628
629     if (self->priv->active_session == NULL) {
630         WARN("Error: 'refresh' requested for plugin %s but no active session",
631                 self->priv->plugin_type);
632         return;
633     }
634     gsignond_plugin_refresh (self->priv->plugin, ui_data);
635 }
636
637