]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/accounts.c
Merge with trunk
[sojka/lightdm.git] / src / accounts.c
1 /* -*- Mode: C; indent-tabs-mode: nil; tab-width: 4 -*-
2  *
3  * Copyright (C) 2010-2011 Robert Ancell.
4  * Author: Robert Ancell <robert.ancell@canonical.com>
5  * 
6  * This program is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
10  * license.
11  */
12
13 #include <errno.h>
14 #include <pwd.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "accounts.h"
19 #include "dmrc.h"
20
21 struct UserPrivate
22 {
23     /* Name of user */
24     gchar *name;
25
26     /* Accounts interface proxy */
27     GDBusProxy *proxy;
28
29     /* User ID */
30     uid_t uid;
31
32     /* Group ID */
33     gid_t gid;
34
35     /* GECOS information */
36     gchar *gecos;
37
38     /* Home directory */
39     gchar *home_directory;
40
41     /* Shell */
42     gchar *shell;
43
44     /* Language */
45     gchar *language;
46
47     /* X session */
48     gchar *xsession;
49 };
50
51 G_DEFINE_TYPE (User, user, G_TYPE_OBJECT);
52
53 /* Connection to AccountsService */
54 static GDBusProxy *accounts_service_proxy = NULL;
55 static gboolean have_accounts_service_proxy = FALSE;
56
57 static gboolean
58 call_method (GDBusProxy *proxy, const gchar *method, GVariant *args,
59              const gchar *expected, GVariant **result)
60 {
61     GVariant *answer;
62     GError *error = NULL;
63
64     if (!proxy)
65         return FALSE;
66
67     answer = g_dbus_proxy_call_sync (proxy,
68                                      method,
69                                      args,
70                                      G_DBUS_CALL_FLAGS_NONE,
71                                      -1,
72                                      NULL,
73                                      &error);
74     if (error)
75         g_warning ("Could not call %s: %s", method, error->message);
76     g_clear_error (&error);
77
78     if (!answer)
79         return FALSE;
80
81     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE (expected)))
82     {
83         g_warning ("Unexpected response from %s: %s",
84                    method, g_variant_get_type_string (answer));
85         g_variant_unref (answer);
86         return FALSE;
87     }
88
89     if (result)
90         *result = answer;
91     else
92         g_variant_unref (answer);
93
94     return TRUE;
95 }
96
97 static gboolean
98 get_property (GDBusProxy *proxy, const gchar *property,
99               const gchar *expected, GVariant **result)
100 {
101     GVariant *answer;
102
103     answer = g_dbus_proxy_get_cached_property (proxy, property);
104
105     if (!answer)
106     {
107         g_warning ("Could not get accounts property %s", property);
108         return FALSE;
109     }
110
111     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE (expected)))
112     {
113         g_warning ("Unexpected accounts property type for %s: %s",
114                    property, g_variant_get_type_string (answer));
115         g_variant_unref (answer);
116         return FALSE;
117     }
118
119     if (result)
120         *result = answer;
121     else
122         g_variant_unref (answer);
123     return TRUE;
124 }
125
126 static void
127 save_string_to_dmrc (const gchar *username, const gchar *group,
128                      const gchar *key, const gchar *value)
129 {
130     GKeyFile *dmrc;
131
132     dmrc = dmrc_load (username);
133     g_key_file_set_string (dmrc, group, key, value);
134     dmrc_save (dmrc, username);
135
136     g_key_file_free (dmrc);
137 }
138
139 static gchar *
140 get_string_from_dmrc (const gchar *username, const gchar *group,
141                       const gchar *key)
142 {
143     GKeyFile *dmrc;
144     gchar *value;
145
146     dmrc = dmrc_load (username);
147     value = g_key_file_get_string (dmrc, group, key, NULL);
148
149     g_key_file_free (dmrc);
150     return value;
151 }
152
153 static GDBusProxy *
154 get_accounts_service_proxy (void)
155 {
156     GError *error = NULL;
157
158     if (have_accounts_service_proxy)
159         return accounts_service_proxy;
160
161     have_accounts_service_proxy = TRUE;
162     accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
163                                                             G_DBUS_PROXY_FLAGS_NONE,
164                                                             NULL,
165                                                             "org.freedesktop.Accounts",
166                                                             "/org/freedesktop/Accounts",
167                                                             "org.freedesktop.Accounts",
168                                                             NULL, &error);
169     if (error)
170         g_warning ("Could not get accounts proxy: %s", error->message);
171     g_clear_error (&error);
172
173     if (accounts_service_proxy)
174     {
175         gchar *name;
176         name = g_dbus_proxy_get_name_owner (accounts_service_proxy);
177         if (!name)
178         {
179             g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
180             g_object_unref (accounts_service_proxy);
181             accounts_service_proxy = NULL;
182         }
183         g_free (name);
184     }  
185
186     return accounts_service_proxy;
187 }
188
189 static GDBusProxy *
190 get_accounts_proxy_for_user (const gchar *user)
191 {
192     GDBusProxy *proxy;
193     GError *error = NULL;
194     GVariant *result;
195     gboolean success;
196     gchar *user_path = NULL;
197
198     g_return_val_if_fail (user != NULL, NULL);  
199
200     proxy = get_accounts_service_proxy ();
201     if (!proxy)
202         return NULL;
203
204     success = call_method (proxy, "FindUserByName", g_variant_new ("(s)", user), "(o)", &result);
205
206     if (!success)
207         return NULL;
208
209     g_variant_get (result, "(o)", &user_path);
210     g_variant_unref (result);
211
212     if (!user_path)
213         return NULL;
214   
215     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
216                                            G_DBUS_PROXY_FLAGS_NONE,
217                                            NULL,
218                                            "org.freedesktop.Accounts",
219                                            user_path,
220                                            "org.freedesktop.Accounts.User",
221                                            NULL, &error);
222     if (error)
223         g_warning ("Could not get accounts user proxy: %s", error->message);
224     g_clear_error (&error);
225     g_free (user_path);
226
227     return proxy;
228 }
229
230 static User *
231 user_from_passwd (struct passwd *user_info)
232 {
233     User *user;
234
235     user = g_object_new (USER_TYPE, NULL);
236     user->priv->name = g_strdup (user_info->pw_name);
237     user->priv->uid = user_info->pw_uid;
238     user->priv->gid = user_info->pw_gid;
239     user->priv->gecos = g_strdup (user_info->pw_gecos);
240     user->priv->home_directory = g_strdup (user_info->pw_dir);
241     user->priv->shell = g_strdup (user_info->pw_shell);
242     user->priv->proxy = get_accounts_proxy_for_user (user->priv->name);
243
244     return user;
245 }
246
247 User *
248 accounts_get_user_by_name (const gchar *username)
249 {
250     struct passwd *user_info;
251     User *user = NULL;
252
253     g_return_val_if_fail (username != NULL, NULL);
254
255     errno = 0;
256     user_info = getpwnam (username);
257     if (user_info)
258         user = user_from_passwd (user_info);
259
260     if (!user && errno != 0)
261         g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
262
263     return user;
264 }
265
266 User *
267 accounts_get_user_by_uid (uid_t uid)
268 {
269     User *user = NULL;
270
271     errno = 0;
272     struct passwd *user_info;
273
274     user_info = getpwuid (uid);
275     if (user_info)
276         user = user_from_passwd (user_info);
277
278     if (!user && errno != 0)
279         g_warning ("Unable to get information on user %d: %s", uid, strerror (errno));
280
281     return user;
282 }
283
284 User *
285 accounts_get_current_user ()
286 {
287     return user_from_passwd (getpwuid (getuid ()));
288 }
289
290 const gchar *
291 user_get_name (User *user)
292 {
293     g_return_val_if_fail (user != NULL, NULL);
294     return user->priv->name;
295 }
296
297 uid_t
298 user_get_uid (User *user)
299 {
300     g_return_val_if_fail (user != NULL, 0);
301     return user->priv->uid;
302 }
303
304 gid_t
305 user_get_gid (User *user)
306 {
307     g_return_val_if_fail (user != NULL, 0);
308     return user->priv->gid;
309 }
310
311 const gchar *
312 user_get_gecos (User *user)
313 {
314     g_return_val_if_fail (user != NULL, NULL);
315     return user->priv->gecos;
316 }
317
318 const gchar *
319 user_get_home_directory (User *user)
320 {
321     g_return_val_if_fail (user != NULL, NULL);
322     return user->priv->home_directory;
323 }
324
325 const gchar *
326 user_get_shell (User *user)
327 {
328     g_return_val_if_fail (user != NULL, NULL);
329     return user->priv->shell;
330 }
331
332 void
333 user_set_language (User *user, const gchar *language)
334 {
335     g_return_if_fail (user != NULL);
336
337     call_method (user->priv->proxy, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
338     save_string_to_dmrc (user->priv->name, "Desktop", "Language", language);
339 }
340
341 const gchar *
342 user_get_language (User *user)
343 {
344     GVariant *variant, *inner;
345     gboolean success;
346
347     g_return_val_if_fail (user != NULL, NULL);
348
349     g_free (user->priv->language);
350     if (user->priv->proxy)
351     {
352         /* the "Language" property cannot be retrieved with get_property () here since it
353          * uses g_dbus_proxy_get_cached_property () which would return the previous (cached) value
354          * of the "Language" property
355          */
356         success = call_method (user->priv->proxy, "org.freedesktop.DBus.Properties.Get", g_variant_new ("(ss)", g_dbus_proxy_get_interface_name(user->priv->proxy), "Language"), "(v)", &variant);
357         if (success)
358         {
359             g_variant_get (variant, "(v)", &inner);
360             user->priv->language = g_variant_dup_string (inner, NULL);
361             g_variant_unref (inner);
362             g_variant_unref (variant);
363         }
364         else
365             user->priv->language = NULL;
366     }
367     else
368         user->priv->language = get_string_from_dmrc (user->priv->name, "Desktop", "Language");
369
370     /* Treat a blank language as unset */
371     if (g_strcmp0 (user->priv->language, "") == 0)
372     {
373         g_free (user->priv->language);
374         user->priv->language = NULL;
375     }
376
377     return user->priv->language;
378 }
379
380 void
381 user_set_xsession (User *user, const gchar *xsession)
382 {
383     g_return_if_fail (user != NULL);
384
385     call_method (user->priv->proxy, "SetXSession", g_variant_new ("(s)", xsession), "()", NULL);
386     save_string_to_dmrc (user->priv->name, "Desktop", "Session", xsession);
387 }
388
389 const gchar *
390 user_get_xsession (User *user)
391 {
392     GVariant *result;
393
394     g_return_val_if_fail (user != NULL, NULL);
395
396     g_free (user->priv->xsession);
397     if (user->priv->proxy)
398     {
399         if (get_property (user->priv->proxy, "XSession", "s", &result))
400         {
401             g_variant_get (result, "s", &user->priv->xsession);
402             g_variant_unref (result);
403         }
404         else
405             user->priv->xsession = NULL;
406     }
407     else
408         user->priv->xsession = get_string_from_dmrc (user->priv->name, "Desktop", "Session");
409
410     if (g_strcmp0 (user->priv->xsession, "") == 0)
411     {
412         g_free (user->priv->xsession);
413         user->priv->xsession = NULL;
414     }
415
416     return user->priv->xsession;
417 }
418
419 static void
420 user_init (User *user)
421 {
422     user->priv = G_TYPE_INSTANCE_GET_PRIVATE (user, USER_TYPE, UserPrivate);
423 }
424
425 static void
426 user_dispose (GObject *object)
427 {
428     User *self;
429
430     self = USER (object);
431
432     if (self->priv->proxy)
433     {
434         g_object_unref (self->priv->proxy);
435         self->priv->proxy = NULL;
436     }
437
438     G_OBJECT_CLASS (user_parent_class)->dispose (object);
439 }
440
441 static void
442 user_finalize (GObject *object)
443 {
444     User *self;
445
446     self = USER (object);
447
448     g_free (self->priv->name);
449     g_free (self->priv->gecos);
450     g_free (self->priv->home_directory);
451     g_free (self->priv->shell);
452     g_free (self->priv->language);
453
454     G_OBJECT_CLASS (user_parent_class)->finalize (object);  
455 }
456
457 static void
458 user_class_init (UserClass *klass)
459 {
460     GObjectClass *object_class = G_OBJECT_CLASS (klass);
461
462     object_class->dispose = user_dispose;
463     object_class->finalize = user_finalize;  
464
465     g_type_class_add_private (klass, sizeof (UserPrivate));
466 }