fix .desktop file
[gconf-editor] / src / gconf-policykit.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2008 Vincent Untz <vuntz@gnome.org>
4  *
5  * Based on set-timezone.c from gnome-panel which was GPLv2+ with this
6  * copyright:
7  *    Copyright (C) 2007 David Zeuthen <david@fubar.dk>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <sys/wait.h>
35
36 #include <dbus/dbus-glib.h>
37 #include <dbus/dbus-glib-lowlevel.h>
38
39 #include "gconf-policykit.h"
40
41 #define CACHE_VALIDITY_SEC 10
42
43 static DBusGConnection *
44 get_system_bus (void)
45 {
46         GError          *error;
47         static DBusGConnection *bus = NULL;
48
49         if (bus == NULL) {
50                 error = NULL;
51                 bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
52                 if (bus == NULL) {
53                         g_warning ("Couldn't connect to system bus: %s",
54                                    error->message);
55                         g_error_free (error);
56                 }
57         }
58
59         return bus;
60 }
61
62 static guint
63 can_set (const gchar *key, gboolean mandatory)
64 {
65         DBusGConnection *bus;
66         DBusGProxy *proxy = NULL;
67         const gchar *keys[2];
68         const gchar *func;
69         GError *error = NULL;
70         guint res = 0;
71
72         bus = get_system_bus ();
73         if (bus == NULL)
74                 goto out;
75
76         proxy = dbus_g_proxy_new_for_name (bus,
77                                            "org.gnome.GConf.Defaults",
78                                            "/",
79                                            "org.gnome.GConf.Defaults");
80         if (proxy == NULL)
81                 goto out;
82
83         keys[0] = key;
84         keys[1] = NULL;
85         func = mandatory ? "CanSetMandatory" : "CanSetSystem";
86
87         if (!dbus_g_proxy_call (proxy, func,
88                                 &error,
89                                 G_TYPE_STRV, keys,
90                                 G_TYPE_INVALID,
91                                 G_TYPE_UINT, &res,
92                                 G_TYPE_INVALID)) {
93                 g_warning ("error calling %s: %s\n", func, error->message);
94                 g_error_free (error);
95         }
96
97 out:
98         if (proxy)
99                 g_object_unref (proxy);
100
101         return res;
102 }
103
104 typedef struct
105 {
106         time_t last_refreshed;
107         gint can_set;
108 } CacheEntry;
109
110 static GHashTable *defaults_cache = NULL;
111 static GHashTable *mandatory_cache = NULL;
112
113 gint
114 gconf_pk_can_set (const gchar *key, gboolean mandatory)
115 {
116         time_t now;
117         GHashTable **cache;
118         CacheEntry *entry;
119
120         time (&now);
121         cache = mandatory ? &mandatory_cache : &defaults_cache;
122         if (!*cache)
123                 *cache = g_hash_table_new (g_str_hash, g_str_equal);
124         entry = (CacheEntry *) g_hash_table_lookup (*cache, key);
125         if (!entry) {
126                 entry = g_new0 (CacheEntry, 1);
127                 g_hash_table_insert (*cache, (char *) key, entry);
128         }
129         if (ABS (now - entry->last_refreshed) > CACHE_VALIDITY_SEC) {
130                 entry->can_set = can_set (key, mandatory);
131                 entry->last_refreshed = now;
132         }
133
134         return entry->can_set;
135 }
136
137 gint
138 gconf_pk_can_set_default (const gchar *key)
139 {
140         return gconf_pk_can_set (key, FALSE);
141 }
142
143 gint
144 gconf_pk_can_set_mandatory (const gchar *key)
145 {
146         return gconf_pk_can_set (key, TRUE);
147 }
148
149 static void
150 gconf_pk_update_can_set_cache (const gchar *key,
151                                gboolean     mandatory)
152 {
153         time_t          now;
154         GHashTable **cache;
155         CacheEntry *entry;
156
157         time (&now);
158         cache = mandatory ? &mandatory_cache : &defaults_cache;
159         if (!*cache)
160                 *cache = g_hash_table_new (g_str_hash, g_str_equal);
161         entry = (CacheEntry *) g_hash_table_lookup (*cache, key);
162         if (!entry) {
163                 entry = g_new0 (CacheEntry, 1);
164                 g_hash_table_insert (*cache, (char *) key, entry);
165         }
166         entry->can_set = 2;
167         entry->last_refreshed = now;
168 }
169
170 typedef struct {
171         gint            ref_count;
172         gboolean        mandatory;
173         gchar          *key;
174         GFunc           callback;
175         gpointer        data;
176         GDestroyNotify  notify;
177 } GConfPKCallbackData;
178
179 static gpointer
180 _gconf_pk_data_ref (gpointer d)
181 {
182         GConfPKCallbackData *data = d;
183
184         data->ref_count++;
185
186         return data;
187 }
188
189 static void
190 _gconf_pk_data_unref (gpointer d)
191 {
192         GConfPKCallbackData *data = d;
193
194         data->ref_count--;
195         if (data->ref_count == 0) {
196                 if (data->notify)
197                         data->notify (data->data);
198                 g_free (data->key);
199                 g_slice_free (GConfPKCallbackData, data);
200         }
201 }
202
203 static void set_key_async (GConfPKCallbackData *data);
204
205 static void
206 gconf_pk_update_can_set_cache (const gchar *key,
207                                gboolean     mandatory);
208
209 static void
210 set_key_notify (DBusGProxy     *proxy,
211                 DBusGProxyCall *call,
212                 void           *user_data)
213 {
214         GConfPKCallbackData *data = user_data;
215         GError *error = NULL;
216
217         if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
218                 gconf_pk_update_can_set_cache (data->key, data->mandatory);
219                 if (data->callback)
220                         data->callback (data->data, NULL);
221         }
222         else {
223                 if (error->domain == DBUS_GERROR &&
224                     error->code == DBUS_GERROR_NO_REPLY) {
225                         /* these errors happen because dbus doesn't
226                          * use monotonic clocks
227                          */
228                         g_warning ("ignoring no-reply error when setting key");
229                         g_error_free (error);
230                         if (data->callback)
231                                 data->callback (data->data, NULL);
232                 }
233                 else {
234                         if (data->callback)
235                                 data->callback (data->data, error);
236                         else
237                                 g_error_free (error);
238                 }
239         }
240 }
241
242 static void
243 set_key_async (GConfPKCallbackData *data)
244 {
245         DBusGConnection *bus;
246         DBusGProxy      *proxy;
247         const gchar     *call;
248         gchar           *keys[2] = { data->key, NULL };
249
250         bus = get_system_bus ();
251         if (bus == NULL)
252                 return;
253
254         proxy = dbus_g_proxy_new_for_name (bus,
255                                            "org.gnome.GConf.Defaults",
256                                            "/",
257                                            "org.gnome.GConf.Defaults");
258
259         call = data->mandatory ? "SetMandatory" : "SetSystem";
260         dbus_g_proxy_begin_call_with_timeout (proxy,
261                                               call,
262                                               set_key_notify,
263                                               _gconf_pk_data_ref (data),
264                                               _gconf_pk_data_unref,
265                                               INT_MAX,
266                                               /* parameters: */
267                                               G_TYPE_STRV, keys,
268                                               G_TYPE_STRV, NULL,
269                                               G_TYPE_INVALID,
270                                               /* return values: */
271                                               G_TYPE_INVALID);
272 }
273
274 void
275 gconf_pk_set_default_async (const gchar    *key,
276                             GFunc           callback,
277                             gpointer        d,
278                             GDestroyNotify  notify)
279 {
280         GConfPKCallbackData *data;
281
282         if (key == NULL)
283                 return;
284
285         data = g_slice_new0 (GConfPKCallbackData);
286         data->ref_count = 1;
287         data->mandatory = FALSE;
288         data->key = g_strdup (key);
289         data->callback = callback;
290         data->data = d;
291         data->notify = notify;
292
293         set_key_async (data);
294         _gconf_pk_data_unref (data);
295 }
296
297 void
298 gconf_pk_set_mandatory_async (const gchar    *key,
299                               GFunc           callback,
300                               gpointer        d,
301                               GDestroyNotify  notify)
302 {
303         GConfPKCallbackData *data;
304
305         if (key == NULL)
306                 return;
307
308         data = g_slice_new0 (GConfPKCallbackData);
309         data->ref_count = 1;
310         data->mandatory = TRUE;
311         data->key = g_strdup (key);
312         data->callback = callback;
313         data->data = d;
314         data->notify = notify;
315
316         set_key_async (data);
317         _gconf_pk_data_unref (data);
318 }