Release 0.0.4
[platform/upstream/gumd.git] / src / lib / gum-group.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 gum
5  *
6  * Copyright (C) 2013 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@intel.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, but
16  * 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 "common/gum-defines.h"
27 #include "common/gum-log.h"
28 #include "common/gum-error.h"
29 #include "common/gum-group-types.h"
30 #include "common/dbus/gum-dbus-group-service-gen.h"
31 #include "common/dbus/gum-dbus-group-gen.h"
32
33 #include "gum-group.h"
34 #include "gum-group-service.h"
35 #include "gum-internals.h"
36
37 /**
38  * SECTION:gum-group
39  * @short_description: provides interface for managing group's data
40  * @include: gum/gum-group.h
41  *
42  * #GumGroup provides interface for adding, removing and updating group.
43  * Group's information can also be retrieved using this interface. Only
44  * privileged user can access the interface when system-bus is used for
45  * communication with the user management daemon.
46  *
47  * Following code snippet demonstrates how to create a new remote group object:
48  *
49  * |[
50  *  GumGroup *group = NULL;
51  *
52  *  group = gum_group_create_sync ();
53  *
54  *  // use the object
55  *
56  *  // destroy the object
57  *  g_object_unref (group);
58  * ]|
59  *
60  * Similarly, new group can be added as:
61  * |[
62  *  GumGroup *group = NULL;
63  *  gboolean rval = FALSE;
64  *
65  *  group = gum_group_create_sync ();
66  *
67  *  // set group properties
68  *  g_object_set (G_OBJECT (group), "groupname", "group1", "secret", "123456",
69  *   "grouptype", GUM_GROUPTYPE_USER, NULL);
70  *
71  *  // add group
72  *  rval = gum_group_add_sync (user);
73  *
74  *  // destroy the object
75  *  g_object_unref (group);
76  * ]|
77  *
78  * For more details, see command-line utility implementation here:
79  *<ulink url="https://github.com/01org/gumd/blob/master/src/utils/gum-utils.c">
80  *          gum-utils</ulink>
81  */
82
83 /**
84  * GumGroupCb:
85  * @group: (transfer none): #GumGroup object which is used in the request
86  * @error: (transfer none): #GError object. In case of error, error will be
87  * non-NULL
88  * @user_data: user data passed onto the request
89  *
90  * #GumGroupCb defines the callback which is used when group object is created,
91  * added, deleted or updated or new members are added to the group.
92  */
93
94 /**
95  * GumGroup:
96  *
97  * Opaque structure for the object.
98  */
99
100 /**
101  * GumGroupClass:
102  * @parent_class: parent class object
103  *
104  * Opaque structure for the class.
105  */
106
107 typedef struct {
108     GumGroupCb callback;
109     gpointer user_data;
110     GError *error;
111     guint cb_id;
112 } GumGroupOp;
113
114 struct _GumGroupPrivate
115 {
116     GumDbusGroupService *dbus_service;
117     GumDbusGroup *dbus_group;
118     GCancellable *cancellable;
119     GumGroupOp *op;
120 };
121
122 G_DEFINE_TYPE (GumGroup, gum_group, G_TYPE_OBJECT)
123
124 #define GUM_GROUP_PRIV(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
125         GUM_TYPE_GROUP, GumGroupPrivate)
126 enum
127 {
128     PROP_0,
129
130     PROP_GROUPTYPE,
131     PROP_GID,
132     PROP_GROUPNAME,
133     PROP_SECRET,
134
135     N_PROPERTIES
136 };
137
138 static GParamSpec *properties[N_PROPERTIES];
139
140
141 static void
142 _free_op (
143         GumGroup *self)
144 {
145     if (self &&
146         self->priv->op) {
147         if (self->priv->op->error) g_error_free (self->priv->op->error);
148         g_free (self->priv->op);
149         self->priv->op = NULL;
150     }
151 }
152
153 static void
154 _create_op (
155         GumGroup *self,
156         GumGroupCb callback,
157         gpointer user_data)
158 {
159     GumGroupOp *op = g_malloc0 (sizeof (GumGroupOp));
160     op->callback = callback;
161     op->user_data = user_data;
162     _free_op (self);
163     self->priv->op = op;
164 }
165
166 static gboolean
167 _trigger_callback (
168         gpointer user_data)
169 {
170     g_return_val_if_fail (user_data && GUM_IS_GROUP (user_data), FALSE);
171
172     GumGroup *group = GUM_GROUP (user_data);
173     if (group->priv->op->callback) {
174         (group->priv->op->callback)(group, group->priv->op->error,
175                 group->priv->op->user_data);
176     }
177     group->priv->op->cb_id = 0;
178     return FALSE;
179 }
180
181 static void
182 _setup_idle_callback (
183         GumGroup *self,
184         const GError *error)
185 {
186     if (!self->priv->op->callback) return;
187     if (error) {
188         if (self->priv->op->error) g_clear_error (&self->priv->op->error);
189         self->priv->op->error = g_error_copy (error);
190     }
191     self->priv->op->cb_id = g_idle_add (_trigger_callback, self);
192 }
193
194 static void
195 _on_group_remote_object_destroyed (
196         GDBusProxy *proxy,
197         gpointer user_data)
198 {
199     g_return_if_fail (GUM_IS_GROUP (user_data));
200     GumGroup *self = GUM_GROUP (user_data);
201
202     DBG("");
203
204     GUM_OBJECT_UNREF (self->priv->dbus_group);
205 }
206
207 static void
208 _set_property (
209         GObject *object,
210         guint property_id,
211         const GValue *value,
212         GParamSpec *pspec)
213 {
214     GumGroup *self = GUM_GROUP (object);
215     if (self->priv->dbus_group) {
216         g_object_set_property (G_OBJECT(self->priv->dbus_group), pspec->name,
217                 value);
218     }
219 }
220
221 static void
222 _get_property (
223         GObject *object,
224         guint property_id,
225         GValue *value,
226         GParamSpec *pspec)
227 {
228     GumGroup *self = GUM_GROUP (object);
229     if (self->priv->dbus_group) {
230         g_object_get_property (G_OBJECT(self->priv->dbus_group), pspec->name,
231                 value);
232     }
233 }
234
235 static void
236 _dispose (GObject *object)
237 {
238     GumGroup *self = GUM_GROUP (object);
239
240     if (self->priv->op &&
241         self->priv->op->cb_id > 0) {
242         g_source_remove (self->priv->op->cb_id);
243         self->priv->op->cb_id = 0;
244     }
245
246     if (self->priv->cancellable) {
247         g_cancellable_cancel (self->priv->cancellable);
248         g_object_unref (self->priv->cancellable);
249         self->priv->cancellable = NULL;
250     }
251
252     if (self->priv->dbus_group) {
253         g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->dbus_group),
254                 _on_group_remote_object_destroyed, self);
255     }
256
257     GUM_OBJECT_UNREF (self->priv->dbus_group);
258
259     GUM_OBJECT_UNREF (self->priv->dbus_service);
260
261     G_OBJECT_CLASS (gum_group_parent_class)->dispose (object);
262 }
263
264 static void
265 _finalize (GObject *object)
266 {
267     GumGroup *self = GUM_GROUP (object);
268
269     _free_op (self);
270
271     G_OBJECT_CLASS (gum_group_parent_class)->finalize (object);
272 }
273
274 static void
275 gum_group_init (
276         GumGroup *self)
277 {
278     self->priv = GUM_GROUP_PRIV (self);
279     self->priv->dbus_group = NULL;
280     self->priv->cancellable = g_cancellable_new ();
281     self->priv->dbus_service = gum_group_service_get_instance ();
282     self->priv->op = NULL;
283 }
284
285 static void
286 gum_group_class_init (
287         GumGroupClass *klass)
288 {
289     GObjectClass* object_class = G_OBJECT_CLASS (klass);
290
291     g_type_class_add_private (object_class, sizeof (GumGroupPrivate));
292
293     object_class->get_property = _get_property;
294     object_class->set_property = _set_property;
295     object_class->dispose = _dispose;
296     object_class->finalize = _finalize;
297
298     /**
299      * GumGroup:grouptype:
300      *
301      * This property holds a group type that the object corresponds to. Valid
302      * values of group types are as specified in #GumGroupType.
303      * #GumGroup:grouptype must be specified when adding a new group.
304      */
305     properties[PROP_GROUPTYPE] =  g_param_spec_uint ("grouptype",
306             "GroupType",
307             "Type of the group",
308             0,
309             G_MAXUINT16,
310             GUM_GROUPTYPE_NONE /* default value */,
311             G_PARAM_READWRITE |
312             G_PARAM_STATIC_STRINGS);
313
314     /**
315      * GumGroup:gid:
316      *
317      * This property holds a unique group identity for the group as assigned by
318      * the underlying framework, which is always be in range [0, MAXUINT].
319      */
320     properties[PROP_GID] =  g_param_spec_uint ("gid",
321             "Gid",
322             "Unique identifier of the group of the group",
323             0,
324             G_MAXUINT,
325             GUM_GROUP_INVALID_GID /* default value */,
326             G_PARAM_READABLE |
327             G_PARAM_STATIC_STRINGS);
328
329     /**
330      * GumGroup:groupname:
331      *
332      * This property holds the name of given to the group when the group is
333      * added. Allowed pattern for groupname is:
334      * "^[A-Za-z_][A-Za-z0-9_.-]*[A-Za-z0-9_.$-]\\?$".
335      */
336     properties[PROP_GROUPNAME] = g_param_spec_string ("groupname",
337             "Groupname",
338             "System name of the group",
339             "" /* default value */,
340             G_PARAM_READWRITE |
341             G_PARAM_STATIC_STRINGS);
342
343     /**
344      * GumGroup:secret:
345      *
346      * This property holds the secret as chosen. Secret should not
347      * contain any control chars (0x00-0x1F,0x7F) or colon (':' 0x3A).
348      */
349     properties[PROP_SECRET] = g_param_spec_string ("secret",
350             "Secret",
351             "Group secret",
352             "" /* default value */,
353             G_PARAM_READWRITE |
354             G_PARAM_STATIC_STRINGS);
355
356     g_object_class_install_properties (object_class, N_PROPERTIES,
357             properties);
358 }
359
360
361 static void
362 _create_dbus_group (
363         GumGroup *group,
364         gchar *object_path,
365         GError *error)
366 {
367     group->priv->dbus_group = gum_dbus_group_proxy_new_sync (
368             g_dbus_proxy_get_connection (G_DBUS_PROXY (
369                     group->priv->dbus_service)),
370             G_DBUS_PROXY_FLAGS_NONE, g_dbus_proxy_get_name (
371             G_DBUS_PROXY (group->priv->dbus_service)), object_path,
372             group->priv->cancellable, &error);
373     if (!error) {
374         g_signal_connect (G_OBJECT (group->priv->dbus_group), "unregistered",
375             G_CALLBACK (_on_group_remote_object_destroyed),  group);
376     }
377 }
378
379 static GVariant *
380 _get_prop_value (
381         GVariant *props,
382         const gchar *prop)
383 {
384     GVariantIter *iter = NULL;
385     GVariant *item = NULL;
386     g_variant_get (props, "(a{sv})",  &iter);
387     while ((item = g_variant_iter_next_value (iter)))  {
388         gchar *key;
389         GVariant *value;
390         g_variant_get (item, "{sv}", &key, &value);
391         if (g_strcmp0 (key, prop) == 0) {
392             g_free (key);
393             return g_variant_ref (value);
394         }
395         g_free (key); key = NULL;
396         g_variant_unref (value); value = NULL;
397     }
398     return NULL;
399 }
400
401 static gboolean
402 _sync_properties (
403         GumGroup *group)
404 {
405     GError *error = NULL;
406     GVariant *result = NULL;
407
408     /* load all properties synchronously */
409     result = g_dbus_connection_call_sync (
410             g_dbus_proxy_get_connection (
411                     G_DBUS_PROXY (group->priv->dbus_service)),
412             g_dbus_proxy_get_name (G_DBUS_PROXY (group->priv->dbus_service)),
413             g_dbus_proxy_get_object_path (G_DBUS_PROXY (group->priv->dbus_group)),
414             "org.freedesktop.DBus.Properties",
415             "GetAll",
416             g_variant_new ("(s)",
417                     g_dbus_proxy_get_interface_name (
418                             G_DBUS_PROXY (group->priv->dbus_group))),
419             G_VARIANT_TYPE ("(a{sv})"),
420             G_DBUS_CALL_FLAGS_NONE,
421             -1,
422             group->priv->cancellable,
423             &error);
424
425     if (error) {
426         WARN ("Failed with error %d:%s", error->code, error->message);
427         g_error_free (error);
428         error = NULL;
429         return FALSE;
430     }
431
432     if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})"))) {
433         guint n_properties = 0, ind = 0;
434         GParamSpec **properties = g_object_class_list_properties (
435                 G_OBJECT_GET_CLASS(group), &n_properties);
436         for (ind=0; ind < n_properties; ind++) {
437             GParamSpec *pspec = properties[ind];
438             GVariant *prop = _get_prop_value (result,  pspec->name);
439             if (prop != NULL) {
440                 g_dbus_proxy_set_cached_property (
441                         G_DBUS_PROXY (group->priv->dbus_group), pspec->name,
442                         prop);
443             }
444         }
445         g_free (properties);
446     }
447     g_variant_unref (result);
448     return TRUE;
449 }
450
451 static void
452 _on_new_group_cb (
453         GObject *object,
454         GAsyncResult *res,
455         gpointer user_data)
456 {
457     GumGroup *group = (GumGroup*)user_data;
458     GumDbusGroupService *proxy = GUM_DBUS_GROUP_SERVICE (object);
459     gchar *object_path = NULL;
460     GError *error = NULL;
461
462     g_return_if_fail (group != NULL);
463
464     DBG ("");
465
466     gum_dbus_group_service_call_create_new_group_finish (proxy, &object_path,
467             res, &error);
468
469     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
470         if (!error) {
471             _create_dbus_group (group, object_path, error);
472         }
473         _setup_idle_callback (group, error);
474     }
475     g_free (object_path);
476     g_clear_error (&error);
477 }
478
479 static void
480 _on_get_group_cb (
481         GObject *object,
482         GAsyncResult *res,
483         gpointer user_data)
484 {
485     GumGroup *group = (GumGroup*)user_data;
486     GumDbusGroupService *proxy = GUM_DBUS_GROUP_SERVICE (object);
487     gchar *object_path = NULL;
488     GError *error = NULL;
489
490     g_return_if_fail (group != NULL);
491
492     DBG ("");
493
494     gum_dbus_group_service_call_get_group_finish (proxy, &object_path,
495             res, &error);
496
497     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
498         if (!error) {
499             _create_dbus_group (group, object_path, error);
500         }
501         _setup_idle_callback (group, error);
502     }
503     g_free (object_path);
504     g_clear_error (&error);
505 }
506
507 static void
508 _on_get_group_by_name_cb (
509         GObject *object,
510         GAsyncResult *res,
511         gpointer user_data)
512 {
513     GumGroup *group = (GumGroup*)user_data;
514     GumDbusGroupService *proxy = GUM_DBUS_GROUP_SERVICE (object);
515     gchar *object_path = NULL;
516     GError *error = NULL;
517
518     g_return_if_fail (group != NULL);
519
520     DBG ("");
521
522     gum_dbus_group_service_call_get_group_by_name_finish (proxy, &object_path,
523             res, &error);
524
525     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
526         if (!error) {
527             _create_dbus_group (group, object_path, error);
528         }
529         _setup_idle_callback (group, error);
530     }
531     g_free (object_path);
532     g_clear_error (&error);
533 }
534
535 static void
536 _on_group_add_cb (
537         GObject *object,
538         GAsyncResult *res,
539         gpointer user_data)
540 {
541     GumGroup *group = (GumGroup*)user_data;
542     GumDbusGroup *proxy = GUM_DBUS_GROUP (object);
543     GError *error = NULL;
544     gid_t gid = GUM_GROUP_INVALID_GID;
545
546     g_return_if_fail (group != NULL);
547
548     DBG ("");
549
550     gum_dbus_group_call_add_group_finish (proxy, &gid, res, &error);
551
552     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
553         _setup_idle_callback (group, error);
554     }
555     g_clear_error (&error);
556 }
557
558 static void
559 _on_group_delete_cb (
560         GObject *object,
561         GAsyncResult *res,
562         gpointer user_data)
563 {
564     GumGroup *group = (GumGroup*)user_data;
565     GumDbusGroup *proxy = GUM_DBUS_GROUP (object);
566     GError *error = NULL;
567
568     g_return_if_fail (group != NULL);
569
570     DBG ("");
571
572     gum_dbus_group_call_delete_group_finish (proxy, res, &error);
573
574     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
575         _setup_idle_callback (group, error);
576     }
577     g_clear_error (&error);
578 }
579
580 static void
581 _on_group_update_cb (
582         GObject *object,
583         GAsyncResult *res,
584         gpointer user_data)
585 {
586     GumGroup *group = (GumGroup*)user_data;
587     GumDbusGroup *proxy = GUM_DBUS_GROUP (object);
588     GError *error = NULL;
589
590     g_return_if_fail (group != NULL);
591
592     DBG ("");
593
594     gum_dbus_group_call_update_group_finish (proxy, res, &error);
595
596     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
597         _setup_idle_callback (group, error);
598     }
599     g_clear_error (&error);
600 }
601
602 static void
603 _on_group_add_member_cb (
604         GObject *object,
605         GAsyncResult *res,
606         gpointer user_data)
607 {
608     GumGroup *group = (GumGroup*)user_data;
609     GumDbusGroup *proxy = GUM_DBUS_GROUP (object);
610     GError *error = NULL;
611
612     g_return_if_fail (group != NULL);
613
614     DBG ("");
615
616     gum_dbus_group_call_add_member_finish (proxy, res, &error);
617
618     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
619         _setup_idle_callback (group, error);
620     }
621     g_clear_error (&error);
622 }
623
624 static void
625 _on_group_delete_member_cb (
626         GObject *object,
627         GAsyncResult *res,
628         gpointer user_data)
629 {
630     GumGroup *group = (GumGroup*)user_data;
631     GumDbusGroup *proxy = GUM_DBUS_GROUP (object);
632     GError *error = NULL;
633
634     g_return_if_fail (group != NULL);
635
636     DBG ("");
637
638     gum_dbus_group_call_delete_member_finish (proxy, res, &error);
639
640     if (GUM_OPERATION_IS_NOT_CANCELLED (error)) {
641         _setup_idle_callback (group, error);
642     }
643     g_clear_error (&error);
644 }
645
646 /**
647  * gum_group_create:
648  * @callback: #GumGroupCb to be invoked when new group object is created
649  * @user_data: user data
650  *
651  * This method creates a new remote group object over the DBus asynchronously.
652  * Callback is used to notify when the remote object is fully created and
653  * accessible.
654  *
655  * Returns: (transfer full): #GumGroup newly created object
656  */
657 GumGroup *
658 gum_group_create (
659         GumGroupCb callback,
660         gpointer user_data)
661 {
662     GumGroup *group = GUM_GROUP (g_object_new (GUM_TYPE_GROUP, NULL));
663     g_return_val_if_fail (group->priv->dbus_service != NULL, NULL);
664
665     _create_op (group, callback, user_data);
666     gum_dbus_group_service_call_create_new_group (group->priv->dbus_service,
667             group->priv->cancellable, _on_new_group_cb, group);
668     return group;
669 }
670
671 /**
672  * gum_group_create_sync:
673  *
674  * This method creates a new remote group object over the DBus synchronously.
675  *
676  * Returns: (transfer full): #GumGroup newly created object
677  */
678 GumGroup *
679 gum_group_create_sync ()
680 {
681     GError *error = NULL;
682     gchar *object_path = NULL;
683
684     GumGroup *group = GUM_GROUP (g_object_new (GUM_TYPE_GROUP, NULL));
685     g_return_val_if_fail (group->priv->dbus_service != NULL, NULL);
686
687     if (gum_dbus_group_service_call_create_new_group_sync (
688             group->priv->dbus_service, &object_path, group->priv->cancellable,
689                 &error)) {
690         _create_dbus_group (group, object_path, error);
691     }
692
693     g_free (object_path);
694
695     if (error) {
696         WARN ("Failed with error %d:%s", error->code, error->message);
697         g_error_free (error);
698         error = NULL;
699         g_object_unref (group);
700         group = NULL;
701     }
702
703     return group;
704 }
705
706 /**
707  * gum_group_get:
708  * @gid: group id for the group
709  * @callback: #GumGroupCb to be invoked when group object is fetched
710  * @user_data: user data
711  *
712  * This method gets the group object attached to gid over the DBus
713  * asynchronously. Callback is used to notify when the remote object is fully
714  * created and accessible.
715  *
716  * Returns: (transfer full): #GumGroup object
717  */
718 GumGroup *
719 gum_group_get (
720         gid_t gid,
721         GumGroupCb callback,
722         gpointer user_data)
723 {
724     GumGroup *group = GUM_GROUP (g_object_new (GUM_TYPE_GROUP, NULL));
725     g_return_val_if_fail (group->priv->dbus_service != NULL, NULL);
726
727     _create_op (group, callback, user_data);
728     gum_dbus_group_service_call_get_group (group->priv->dbus_service, gid,
729             group->priv->cancellable, _on_get_group_cb, group);
730     return group;
731 }
732
733
734 /**
735  * gum_group_get_sync:
736  * @gid: group id for the group
737  *
738  * This method gets the group object attached to gid over the DBus
739  * synchronously.
740  *
741  * Returns: (transfer full): #GumGroup object
742  */
743 GumGroup *
744 gum_group_get_sync (
745         gid_t gid)
746 {
747     GError *error = NULL;
748     gchar *object_path = NULL;
749
750     GumGroup *group = GUM_GROUP (g_object_new (GUM_TYPE_GROUP, NULL));
751     g_return_val_if_fail (group->priv->dbus_service != NULL, NULL);
752
753     if (gum_dbus_group_service_call_get_group_sync (
754             group->priv->dbus_service, gid, &object_path,
755             group->priv->cancellable, &error)) {
756         _create_dbus_group (group, object_path, error);
757     }
758
759     g_free (object_path);
760
761     if (error) {
762         WARN ("Failed with error %d:%s", error->code, error->message);
763         g_error_free (error);
764         error = NULL;
765         g_object_unref (group);
766         group = NULL;
767     }
768     return group;
769 }
770
771 /**
772  * gum_group_get_by_name:
773  * @groupname: name of the group
774  * @callback: #GumGroupCb to be invoked when group object is fetched
775  * @user_data: user data
776  *
777  * This method gets the group object attached to groupname over the DBus
778  * asynchronously. Callback is used to notify when the remote object is fully
779  * created and accessible.
780  *
781  * Returns: (transfer full): #GumGroup object
782  */
783 GumGroup *
784 gum_group_get_by_name (
785         const gchar *groupname,
786         GumGroupCb callback,
787         gpointer user_data)
788 {
789     GumGroup *group = GUM_GROUP (g_object_new (GUM_TYPE_GROUP, NULL));
790     g_return_val_if_fail (group->priv->dbus_service != NULL, NULL);
791
792     if (!groupname) {
793         WARN ("groupname not specified");
794         return NULL;
795     }
796     _create_op (group, callback, user_data);
797     gum_dbus_group_service_call_get_group_by_name (group->priv->dbus_service,
798             groupname, group->priv->cancellable, _on_get_group_by_name_cb,
799             group);
800     return group;
801 }
802
803 /**
804  * gum_group_get_by_name_sync:
805  * @groupname: name of the group
806  *
807  * This method gets the group object attached to groupname over the DBus
808  * synchronously.
809  *
810  * Returns: (transfer full): #GumGroup object
811  */
812 GumGroup *
813 gum_group_get_by_name_sync (
814         const gchar *groupname)
815 {
816     GError *error = NULL;
817     gchar *object_path = NULL;
818
819     GumGroup *group = GUM_GROUP (g_object_new (GUM_TYPE_GROUP, NULL));
820     g_return_val_if_fail (group->priv->dbus_service != NULL, NULL);
821
822     if (!groupname) {
823         WARN ("groupname not specified");
824         return NULL;
825     }
826
827     if (gum_dbus_group_service_call_get_group_by_name_sync (
828             group->priv->dbus_service, groupname, &object_path,
829             group->priv->cancellable, &error)) {
830         _create_dbus_group (group, object_path, error);
831     }
832
833     g_free (object_path);
834
835     if (error) {
836         WARN ("Failed with error %d:%s", error->code, error->message);
837         g_error_free (error);
838         error = NULL;
839         g_object_unref (group);
840         group = NULL;
841     }
842     return group;
843 }
844
845 /**
846  * gum_group_add:
847  * @self: #GumGroup object to be added; object should have valid
848  * #GumGroup:groupname and #GumGroup:grouptype properties.
849  * @callback: #GumGroupCb to be invoked when group is added
850  * @user_data: user data
851  *
852  * This method adds the group over the DBus asynchronously. Callback is used to
853  * notify when the group is added.
854  *
855  * Returns: returns TRUE if the request has been pushed and is waiting for
856  * the response, FALSE otherwise. No callback is triggered, in case the
857  * function returns FALSE.
858  */
859 gboolean
860 gum_group_add (
861         GumGroup *self,
862         GumGroupCb callback,
863         gpointer user_data)
864 {
865     DBG ("");
866     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
867
868     if (!self->priv->dbus_group) {
869         WARN ("Remote dbus object not valid");
870         return FALSE;
871     }
872     _create_op (self, callback, user_data);
873     gum_dbus_group_call_add_group (self->priv->dbus_group,
874             GUM_GROUP_INVALID_GID, self->priv->cancellable, _on_group_add_cb,
875             self);
876     return TRUE;
877 }
878
879 /**
880  * gum_group_add_sync:
881  * @self: #GumGroup object to be added; object should have valid
882  * #GumGroup:groupname and #GumGroup:grouptype properties.
883  *
884  * This method adds the group over the DBus synchronously.
885  *
886  * Returns: returns TRUE if successful, FALSE otherwise.
887  */
888 gboolean
889 gum_group_add_sync (
890         GumGroup *self)
891 {
892     GError *error = NULL;
893     gid_t gid = GUM_GROUP_INVALID_GID;
894
895     DBG ("");
896     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
897
898     if (!self->priv->dbus_group) {
899         WARN ("Remote dbus object not valid");
900         return FALSE;
901     }
902
903     if (!gum_dbus_group_call_add_group_sync (self->priv->dbus_group,
904             GUM_GROUP_INVALID_GID, &gid, self->priv->cancellable, &error)) {
905         if (error) {
906             WARN ("Failed with error %d:%s", error->code, error->message);
907             g_error_free (error);
908             error = NULL;
909         }
910         return FALSE;
911     }
912
913     return _sync_properties (self);
914 }
915
916 /**
917  * gum_group_delete:
918  * @self: #GumGroup object to be deleted; object should have valid
919  * #GumGroup:gid property.
920  * @callback: #GumGroupCb to be invoked when group is deleted
921  * @user_data: user data
922  *
923  * This method deletes the group over the DBus asynchronously. Callback is used
924  * to notify when the group is deleted.
925  *
926  * Returns: returns TRUE if the request has been pushed and is waiting for
927  * the response, FALSE otherwise. No callback is triggered, in case the
928  * function returns FALSE.
929  */
930 gboolean
931 gum_group_delete (
932         GumGroup *self,
933         GumGroupCb callback,
934         gpointer user_data)
935 {
936     DBG ("");
937     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
938
939     if (!self->priv->dbus_group) {
940         WARN ("Remote dbus object not valid");
941         return FALSE;
942     }
943     _create_op (self, callback, user_data);
944     gum_dbus_group_call_delete_group (self->priv->dbus_group,
945             self->priv->cancellable, _on_group_delete_cb, self);
946     return TRUE;
947 }
948
949 /**
950  * gum_group_delete_sync:
951  * @self: #GumGroup object to be deleted; object should have valid
952  * #GumGroup:gid property.
953  *
954  * This method deletes the group over the DBus synchronously.
955  *
956  * Returns: returns TRUE if successful, FALSE otherwise.
957  */
958 gboolean
959 gum_group_delete_sync (
960         GumGroup *self)
961 {
962     GError *error = NULL;
963
964     DBG ("");
965     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
966
967     if (!self->priv->dbus_group) {
968         WARN ("Remote dbus object not valid");
969         return FALSE;
970     }
971
972     if (!gum_dbus_group_call_delete_group_sync (self->priv->dbus_group,
973             self->priv->cancellable, &error)) {
974         if (error) {
975             WARN ("Failed with error %d:%s", error->code, error->message);
976             g_error_free (error);
977             error = NULL;
978         }
979         return FALSE;
980     }
981
982     return TRUE;
983 }
984
985 /**
986  * gum_group_update:
987  * @self: #GumGroup object to be updated; object should have valid
988  * #GumGroup:gid property.
989  * @callback: #GumGroupCb to be invoked when group is updated
990  * @user_data: user data
991  *
992  * This method updates the group over the DBus asynchronously. Callback is used
993  * to notify when the group is updated. The properties which can be updated
994  * are: secret.
995  *
996  * Returns: returns TRUE if the request has been pushed and is waiting for
997  * the response, FALSE otherwise. No callback is triggered, in case the
998  * function returns FALSE.
999  */
1000 gboolean
1001 gum_group_update (
1002         GumGroup *self,
1003         GumGroupCb callback,
1004         gpointer user_data)
1005 {
1006     DBG ("Update Group");
1007     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
1008
1009     if (!self->priv->dbus_group) {
1010         WARN ("Remote dbus object not valid");
1011         return FALSE;
1012     }
1013     _create_op (self, callback, user_data);
1014     gum_dbus_group_call_update_group (self->priv->dbus_group,
1015             self->priv->cancellable, _on_group_update_cb, self);
1016     return TRUE;
1017 }
1018
1019 /**
1020  * gum_group_update_sync:
1021  * @self: #GumGroup object to be updated; object should have valid
1022  * #GumGroup:gid property.
1023  *
1024  * This method updates the group over the DBus synchronously. The properties
1025  * which can be updated are: secret.
1026  *
1027  * Returns: returns TRUE if successful, FALSE otherwise.
1028  */
1029 gboolean
1030 gum_group_update_sync (
1031         GumGroup *self)
1032 {
1033     GError *error = NULL;
1034
1035     DBG ("");
1036     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
1037
1038     if (!self->priv->dbus_group) {
1039         WARN ("Remote dbus object not valid");
1040         return FALSE;
1041     }
1042
1043     if (!gum_dbus_group_call_update_group_sync (self->priv->dbus_group,
1044             self->priv->cancellable, &error)) {
1045         if (error) {
1046             WARN ("Failed with error %d:%s", error->code, error->message);
1047             g_error_free (error);
1048             error = NULL;
1049         }
1050         return FALSE;
1051     }
1052
1053     return _sync_properties (self);
1054 }
1055
1056 /**
1057  * gum_group_add_member:
1058  * @self: #GumGroup object where new member is to be added; object should have
1059  * valid #GumGroup:gid property.
1060  * @uid: user id of the member to be added to the group
1061  * @add_as_admin: user will be added with admin privileges for the group if set
1062  * to TRUE
1063  * @callback: #GumGroupCb to be invoked when member is added
1064  * @user_data: user data
1065  *
1066  * This method adds new member to the group over the DBus asynchronously.
1067  * Callback is used to notify when the member is added.
1068  *
1069  * Returns: returns TRUE if the request has been pushed and is waiting for
1070  * the response, FALSE otherwise. No callback is triggered, in case the
1071  * function returns FALSE.
1072  */
1073 gboolean
1074 gum_group_add_member (
1075         GumGroup *self,
1076         uid_t uid,
1077         gboolean add_as_admin,
1078         GumGroupCb callback,
1079         gpointer user_data)
1080 {
1081     DBG ("");
1082     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
1083
1084     if (!self->priv->dbus_group) {
1085         WARN ("Remote dbus object not valid");
1086         return FALSE;
1087     }
1088     _create_op (self, callback, user_data);
1089     gum_dbus_group_call_add_member (self->priv->dbus_group, uid, add_as_admin,
1090             self->priv->cancellable, _on_group_add_member_cb,
1091             self);
1092     return TRUE;
1093 }
1094
1095 /**
1096  * gum_group_add_member_sync:
1097  * @self: #GumGroup object where new member is to be added; object should have
1098  * valid #GumGroup:gid property.
1099  * @uid: user id of the member to be added to the group
1100  * @add_as_admin: user will be added with admin privileges for the group if set
1101  * to TRUE
1102  *
1103  * This method adds new member to the group over the DBus synchronously.
1104  *
1105  * Returns: returns TRUE if successful, FALSE otherwise.
1106  */
1107 gboolean
1108 gum_group_add_member_sync (
1109         GumGroup *self,
1110         uid_t uid,
1111         gboolean add_as_admin)
1112 {
1113     GError *error = NULL;
1114
1115     DBG ("");
1116     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
1117
1118     if (!self->priv->dbus_group) {
1119         WARN ("Remote dbus object not valid");
1120         return FALSE;
1121     }
1122
1123     if (!gum_dbus_group_call_add_member_sync (self->priv->dbus_group, uid,
1124             add_as_admin, self->priv->cancellable, &error)) {
1125         if (error) {
1126             WARN ("Failed with error %d:%s", error->code, error->message);
1127             g_error_free (error);
1128             error = NULL;
1129         }
1130         return FALSE;
1131     }
1132
1133     return TRUE;
1134 }
1135
1136 /**
1137  * gum_group_delete_member:
1138  * @self: #GumGroup object where member is to be deleted from; object should
1139  * have valid #GumGroup:gid property.
1140  * @uid: user id of the member to be deleted from the group
1141  * @callback: #GumGroupCb to be invoked when member is deleted
1142  * @user_data: user data
1143  *
1144  * This method deletes new member from the group over the DBus asynchronously.
1145  * Callback is used to notify when the member is deleted.
1146  *
1147  * Returns: returns TRUE if the request has been pushed and is waiting for
1148  * the response, FALSE otherwise. No callback is triggered, in case the
1149  * function returns FALSE.
1150  */
1151 gboolean
1152 gum_group_delete_member (
1153         GumGroup *self,
1154         uid_t uid,
1155         GumGroupCb callback,
1156         gpointer user_data)
1157 {
1158     DBG ("");
1159     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
1160
1161     if (!self->priv->dbus_group) {
1162         WARN ("Remote dbus object not valid");
1163         return FALSE;
1164     }
1165     _create_op (self, callback, user_data);
1166     gum_dbus_group_call_delete_member (self->priv->dbus_group, uid,
1167             self->priv->cancellable, _on_group_delete_member_cb, self);
1168     return TRUE;
1169 }
1170
1171 /**
1172  * gum_group_delete_member_sync:
1173  * @self: #GumGroup object where member is to be deleted from; object should
1174  * have valid #GumGroup:gid property.
1175  * @uid: user id of the member to be deleted from the group
1176  *
1177  * This method deletes new member from the group over the DBus synchronously.
1178  *
1179  * Returns: returns TRUE if successful, FALSE otherwise.
1180  */
1181 gboolean
1182 gum_group_delete_member_sync (
1183         GumGroup *self,
1184         uid_t uid)
1185 {
1186     GError *error = NULL;
1187
1188     DBG ("");
1189     g_return_val_if_fail (GUM_IS_GROUP (self), FALSE);
1190
1191     if (!self->priv->dbus_group) {
1192         WARN ("Remote dbus object not valid");
1193         return FALSE;
1194     }
1195
1196     if (!gum_dbus_group_call_delete_member_sync (self->priv->dbus_group, uid,
1197             self->priv->cancellable, &error)) {
1198         if (error) {
1199             WARN ("Failed with error %d:%s", error->code, error->message);
1200             g_error_free (error);
1201             error = NULL;
1202         }
1203         return FALSE;
1204     }
1205
1206     return TRUE;
1207 }