Added fronted for setting user roles.
[accounts-service-plus:accounts-service-plus.git] / src / user.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2   *
3   * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
4   * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
5   * Copyright (C) 2009-2010 Red Hat, Inc.
6   *
7   * This program is free software; you can redistribute it and/or modify
8   * it under the terms of the GNU General Public License as published by
9   * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */
21
22 #define _BSD_SOURCE
23
24 #include "config.h"
25
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <unistd.h>
31 #include <grp.h>
32 #ifdef HAVE_SHADOW_H
33 #include <shadow.h>
34 #endif
35
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 #include <glib-object.h>
39 #include <glib/gstdio.h>
40 #include <gio/gio.h>
41 #include <gio/gunixinputstream.h>
42 #include <polkit/polkit.h>
43
44 #include "daemon.h"
45 #include "user.h"
46 #include "accounts-user-generated.h"
47 #include "util.h"
48
49 enum {
50         PROP_0,
51         PROP_UID,
52         PROP_USER_NAME,
53         PROP_REAL_NAME,
54         PROP_ACCOUNT_TYPE,
55         PROP_HOME_DIR,
56         PROP_SHELL,
57         PROP_EMAIL,
58         PROP_LANGUAGE,
59         PROP_X_SESSION,
60         PROP_LOCATION,
61         PROP_LOGIN_FREQUENCY,
62         PROP_ICON_FILE,
63         PROP_LOCKED,
64         PROP_PASSWORD_MODE,
65         PROP_PASSWORD_HINT,
66         PROP_AUTOMATIC_LOGIN,
67         PROP_SYSTEM_ACCOUNT
68 };
69
70 struct User {
71         AccountsUserSkeleton parent;
72
73         GDBusConnection *system_bus_connection;
74         gchar *object_path;
75
76         Daemon       *daemon;
77
78         uid_t         uid;
79         gid_t         gid;
80         gchar        *user_name;
81         gchar        *real_name;
82         AccountType   account_type;
83         PasswordMode  password_mode;
84         gchar        *password_hint;
85         gchar        *home_dir;
86         gchar        *shell;
87         gchar        *email;
88         gchar        *language;
89         gchar        *x_session;
90         gchar        *location;
91         guint64       login_frequency;
92         gchar        *icon_file;
93         gchar        *default_icon_file;
94         gboolean      locked;
95         gboolean      automatic_login;
96         gboolean      system_account;
97 };
98
99 typedef struct UserClass
100 {
101         AccountsUserSkeletonClass parent_class;
102 } UserClass;
103
104 static void user_accounts_user_iface_init (AccountsUserIface *iface);
105
106 G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init));
107
108 static gint
109 account_type_from_pwent (struct passwd *pwent)
110 {
111         struct group *grp;
112         gid_t wheel;
113         gid_t *groups;
114         gint ngroups;
115         gint i;
116
117         if (pwent->pw_uid == 0) {
118                 g_debug ("user is root so account type is administrator");
119                 return ACCOUNT_TYPE_ADMINISTRATOR;
120         }
121
122         grp = getgrnam ("wheel");
123         if (grp == NULL) {
124                 g_debug ("wheel group not found");
125                 return ACCOUNT_TYPE_STANDARD;
126         }
127         wheel = grp->gr_gid;
128
129         ngroups = get_user_groups (pwent->pw_name, pwent->pw_gid, &groups);
130
131         for (i = 0; i < ngroups; i++) {
132                 if (groups[i] == wheel) {
133                         g_free (groups);
134                         return ACCOUNT_TYPE_ADMINISTRATOR;
135                 }
136         }
137
138         g_free (groups);
139
140         return ACCOUNT_TYPE_STANDARD;
141 }
142
143 void
144 user_local_update_from_pwent (User          *user,
145                               struct passwd *pwent)
146 {
147 #ifdef HAVE_SHADOW_H
148         struct spwd *spent;
149 #endif
150         gchar *real_name;
151         gboolean changed;
152         const gchar *passwd;
153         gboolean locked;
154         PasswordMode mode;
155
156         g_object_freeze_notify (G_OBJECT (user));
157
158         changed = FALSE;
159
160         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
161                 gchar *first_comma = NULL;
162                 gchar *valid_utf8_name = NULL;
163
164                 if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
165                         valid_utf8_name = pwent->pw_gecos;
166                         first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
167                 }
168                 else {
169                         g_warning ("User %s has invalid UTF-8 in GECOS field. "
170                                    "It would be a good thing to check /etc/passwd.",
171                                    pwent->pw_name ? pwent->pw_name : "");
172                 }
173
174                 if (first_comma) {
175                         real_name = g_strndup (valid_utf8_name,
176                                                   (first_comma - valid_utf8_name));
177                 }
178                 else if (valid_utf8_name) {
179                         real_name = g_strdup (valid_utf8_name);
180                 }
181                 else {
182                         real_name = NULL;
183                 }
184
185                 if (real_name && real_name[0] == '\0') {
186                         g_free (real_name);
187                         real_name = NULL;
188                 }
189         }
190         else {
191                 real_name = NULL;
192         }
193         if (g_strcmp0 (real_name, user->real_name) != 0) {
194                 g_free (user->real_name);
195                 user->real_name = real_name;
196                 changed = TRUE;
197                 g_object_notify (G_OBJECT (user), "real-name");
198         }
199         else {
200                 g_free (real_name);
201         }
202
203         /* UID */
204         if (pwent->pw_uid != user->uid) {
205                 user->uid = pwent->pw_uid;
206                 changed = TRUE;
207                 g_object_notify (G_OBJECT (user), "uid");
208         }
209
210         /* GID */
211         user->gid = pwent->pw_gid;
212
213         user->account_type = account_type_from_pwent (pwent);
214
215         /* Username */
216         if (g_strcmp0 (user->user_name, pwent->pw_name) != 0) {
217                 g_free (user->user_name);
218                 user->user_name = g_strdup (pwent->pw_name);
219                 changed = TRUE;
220                 g_object_notify (G_OBJECT (user), "user-name");
221         }
222
223         /* Home Directory */
224         if (g_strcmp0 (user->home_dir, pwent->pw_dir) != 0) {
225                 g_free (user->home_dir);
226                 user->home_dir = g_strdup (pwent->pw_dir);
227                 g_free (user->default_icon_file);
228                 user->default_icon_file = g_build_filename (user->home_dir, ".face", NULL);
229                 changed = TRUE;
230                 g_object_notify (G_OBJECT (user), "home-directory");
231         }
232
233         /* Shell */
234         if (g_strcmp0 (user->shell, pwent->pw_shell) != 0) {
235                 g_free (user->shell);
236                 user->shell = g_strdup (pwent->pw_shell);
237                 changed = TRUE;
238                 g_object_notify (G_OBJECT (user), "shell");
239         }
240
241         passwd = pwent->pw_passwd;
242 #ifdef HAVE_SHADOW_H
243         spent = getspnam (pwent->pw_name);
244         if (spent)
245                 passwd = spent->sp_pwdp;
246 #endif
247
248         if (passwd && passwd[0] == '!') {
249                 locked = TRUE;
250         }
251         else {
252                 locked = FALSE;
253         }
254
255         if (user->locked != locked) {
256                 user->locked = locked;
257                 changed = TRUE;
258                 g_object_notify (G_OBJECT (user), "locked");
259         }
260
261         if (passwd && passwd[0] != 0) {
262                 mode = PASSWORD_MODE_REGULAR;
263         }
264         else {
265                 mode = PASSWORD_MODE_NONE;
266         }
267
268 #ifdef HAVE_SHADOW_H
269         if (spent) {
270                 if (spent->sp_lstchg == 0) {
271                         mode = PASSWORD_MODE_SET_AT_LOGIN;
272                 }
273         }
274 #endif
275
276         if (user->password_mode != mode) {
277                 user->password_mode = mode;
278                 changed = TRUE;
279                 g_object_notify (G_OBJECT (user), "password-mode");
280         }
281
282         user->system_account = daemon_local_user_is_excluded (user->daemon,
283                                                               user->user_name,
284                                                               pwent->pw_shell);
285
286         g_object_thaw_notify (G_OBJECT (user));
287
288         if (changed)
289                 accounts_user_emit_changed (ACCOUNTS_USER (user));
290 }
291
292 void
293 user_local_update_from_keyfile (User     *user,
294                                 GKeyFile *keyfile)
295 {
296         gchar *s;
297
298         g_object_freeze_notify (G_OBJECT (user));
299
300         s = g_key_file_get_string (keyfile, "User", "Language", NULL);
301         if (s != NULL) {
302                 /* TODO: validate / normalize */
303                 g_free (user->language);
304                 user->language = s;
305         }
306
307         s = g_key_file_get_string (keyfile, "User", "XSession", NULL);
308         if (s != NULL) {
309                 g_free (user->x_session);
310                 user->x_session = s;
311         }
312
313         s = g_key_file_get_string (keyfile, "User", "Email", NULL);
314         if (s != NULL) {
315                 g_free (user->email);
316                 user->email = s;
317         }
318
319         s = g_key_file_get_string (keyfile, "User", "Location", NULL);
320         if (s != NULL) {
321                 g_free (user->location);
322                 user->location = s;
323         }
324
325         s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL);
326         if (s != NULL) {
327                 g_free (user->password_hint);
328                 user->password_hint = s;
329         }
330
331         s = g_key_file_get_string (keyfile, "User", "Icon", NULL);
332         if (s != NULL) {
333                 g_free (user->icon_file);
334                 user->icon_file = s;
335         }
336
337         g_object_thaw_notify (G_OBJECT (user));
338 }
339
340 static void
341 user_local_save_to_keyfile (User     *user,
342                             GKeyFile *keyfile)
343 {
344         if (user->email)
345                 g_key_file_set_string (keyfile, "User", "Email", user->email);
346
347         if (user->language)
348                 g_key_file_set_string (keyfile, "User", "Language", user->language);
349
350         if (user->x_session)
351                 g_key_file_set_string (keyfile, "User", "XSession", user->x_session);
352
353         if (user->location)
354                 g_key_file_set_string (keyfile, "User", "Location", user->location);
355
356         if (user->password_hint)
357                 g_key_file_set_string (keyfile, "User", "PasswordHint", user->password_hint);
358
359         if (user->icon_file)
360                 g_key_file_set_string (keyfile, "User", "Icon", user->icon_file);
361 }
362
363 static void
364 save_extra_data (User *user)
365 {
366         gchar *filename;
367         GKeyFile *keyfile;
368         gchar *data;
369         GError *error;
370
371         keyfile = g_key_file_new ();
372         user_local_save_to_keyfile (user, keyfile);
373
374         error = NULL;
375         data = g_key_file_to_data (keyfile, NULL, &error);
376         if (error == NULL) {
377                 filename = g_build_filename ("/var/lib/AccountsService/users",
378                                              user->user_name,
379                                              NULL);
380                 g_file_set_contents (filename, data, -1, &error);
381                 g_free (filename);
382         }
383         if (error) {
384                 g_warning ("Saving data for user %s failed: %s",
385                            user->user_name, error->message);
386                 g_error_free (error);
387         }
388         g_key_file_free (keyfile);
389 }
390
391 static void
392 move_extra_data (const gchar *old_name,
393                  const gchar *new_name)
394 {
395         gchar *old_filename;
396         gchar *new_filename;
397
398         old_filename = g_build_filename ("/var/lib/AccountsService/users",
399                                          old_name, NULL);
400         new_filename = g_build_filename ("/var/lib/AccountsService/users",
401                                          new_name, NULL);
402
403         g_rename (old_filename, new_filename);
404
405         g_free (old_filename);
406         g_free (new_filename);
407 }
408
409 static gchar *
410 compute_object_path (User *user)
411 {
412         gchar *object_path;
413
414         object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%ld",
415                                        (long) user->uid);
416
417         return object_path;
418 }
419
420 void
421 user_local_register (User *user)
422 {
423         GError *error = NULL;
424
425         user->system_bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
426         if (user->system_bus_connection == NULL) {
427                 if (error != NULL) {
428                         g_critical ("error getting system bus: %s", error->message);
429                         g_error_free (error);
430                 }
431                 return;
432         }
433
434         user->object_path = compute_object_path (user);
435
436         if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (user),
437                                                user->system_bus_connection,
438                                                user->object_path,
439                                                &error)) {
440                 if (error != NULL) {
441                         g_critical ("error exporting user object: %s", error->message);
442                         g_error_free (error);
443                 }
444                 return;
445         }
446 }
447
448 void
449 user_local_unregister (User *user)
450 {
451         g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (user));
452 }
453
454 User *
455 user_local_new (Daemon *daemon, uid_t uid)
456 {
457         User *user;
458
459         user = g_object_new (TYPE_USER, NULL);
460         user->daemon = daemon;
461         user->uid = uid;
462
463         return user;
464 }
465
466 const gchar *
467 user_local_get_user_name (User *user)
468 {
469         return user->user_name;
470 }
471
472 gboolean
473 user_local_get_system_account (User *user)
474 {
475         return user->system_account;
476 }
477
478 const gchar *
479 user_local_get_object_path (User *user)
480 {
481         return user->object_path;
482 }
483
484 uid_t
485 user_local_get_uid (User *user)
486 {
487         return user->uid;
488 }
489
490 const gchar *
491 user_local_get_shell(User *user)
492 {
493         return user->shell;
494 }
495
496 static void
497 throw_error (GDBusMethodInvocation *context,
498              gint                   error_code,
499              const gchar           *format,
500              ...)
501 {
502         va_list args;
503         gchar *message;
504
505         va_start (args, format);
506         message = g_strdup_vprintf (format, args);
507         va_end (args);
508
509         g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message);
510
511         g_free (message);
512 }
513
514 static void
515 user_change_real_name_authorized_cb (Daemon                *daemon,
516                                      User                  *user,
517                                      GDBusMethodInvocation *context,
518                                      gpointer               data)
519
520 {
521         gchar *name = data;
522         GError *error;
523         const gchar *argv[6];
524
525         if (g_strcmp0 (user->real_name, name) != 0) {
526                 sys_log (context,
527                          "change real name of user '%s' (%d) to '%s'",
528                          user->user_name, user->uid, name);
529
530                 argv[0] = "/usr/sbin/usermod";
531                 argv[1] = "-c";
532                 argv[2] = name;
533                 argv[3] = "--";
534                 argv[4] = user->user_name;
535                 argv[5] = NULL;
536
537                 error = NULL;
538                 if (!spawn_with_login_uid (context, argv, &error)) {
539                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
540                         g_error_free (error);
541                         return;
542                 }
543
544                 g_free (user->real_name);
545                 user->real_name = g_strdup (name);
546
547                 accounts_user_emit_changed (ACCOUNTS_USER (user));
548
549                 g_object_notify (G_OBJECT (user), "real-name");
550         }
551
552         accounts_user_complete_set_real_name (ACCOUNTS_USER (user), context);
553 }
554
555 static gboolean
556 user_set_real_name (AccountsUser          *auser,
557                     GDBusMethodInvocation *context,
558                     const gchar           *real_name)
559 {
560         User *user = (User*)auser;
561         int uid;
562         const gchar *action_id;
563
564         if (!get_caller_uid (context, &uid)) {
565                 throw_error (context, ERROR_FAILED, "identifying caller failed");
566                 return FALSE;
567         }
568
569         if (user->uid == (uid_t) uid)
570                 action_id = "org.freedesktop.accounts.change-own-user-data";
571         else
572                 action_id = "org.freedesktop.accounts.user-administration";
573
574         daemon_local_check_auth (user->daemon,
575                                  user,
576                                  action_id,
577                                  TRUE,
578                                  user_change_real_name_authorized_cb,
579                                  context,
580                                  g_strdup (real_name),
581                                  (GDestroyNotify)g_free);
582
583         return TRUE;
584 }
585
586 static void
587 user_change_user_name_authorized_cb (Daemon                *daemon,
588                                      User                  *user,
589                                      GDBusMethodInvocation *context,
590                                      gpointer               data)
591
592 {
593         gchar *name = data;
594         gchar *old_name;
595         GError *error;
596         const gchar *argv[6];
597
598         if (g_strcmp0 (user->user_name, name) != 0) {
599                 old_name = g_strdup (user->user_name);
600                 sys_log (context,
601                          "change name of user '%s' (%d) to '%s'",
602                          old_name, user->uid, name);
603
604                 argv[0] = "/usr/sbin/usermod";
605                 argv[1] = "-l";
606                 argv[2] = name;
607                 argv[3] = "--";
608                 argv[4] = user->user_name;
609                 argv[5] = NULL;
610
611                 error = NULL;
612                 if (!spawn_with_login_uid (context, argv, &error)) {
613                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
614                         g_error_free (error);
615                         return;
616                 }
617
618                 g_free (user->user_name);
619                 user->user_name = g_strdup (name);
620
621                 move_extra_data (old_name, name);
622
623                 accounts_user_emit_changed (ACCOUNTS_USER (user));
624
625                 g_object_notify (G_OBJECT (user), "user-name");
626         }
627
628         accounts_user_complete_set_user_name (ACCOUNTS_USER (user), context);
629 }
630
631
632 static gboolean
633 user_set_user_name (AccountsUser          *auser,
634                     GDBusMethodInvocation *context,
635                     const gchar           *user_name)
636 {
637         User *user = (User*)auser;
638         daemon_local_check_auth (user->daemon,
639                                  user,
640                                  "org.freedesktop.accounts.user-administration",
641                                  TRUE,
642                                  user_change_user_name_authorized_cb,
643                                  context,
644                                  g_strdup (user_name),
645                                  (GDestroyNotify)g_free);
646
647         return TRUE;
648 }
649
650 static void
651 user_change_email_authorized_cb (Daemon                *daemon,
652                                  User                  *user,
653                                  GDBusMethodInvocation *context,
654                                  gpointer               data)
655
656 {
657         gchar *email = data;
658
659         if (g_strcmp0 (user->email, email) != 0) {
660                 g_free (user->email);
661                 user->email = g_strdup (email);
662
663                 save_extra_data (user);
664
665                 accounts_user_emit_changed (ACCOUNTS_USER (user));
666
667                 g_object_notify (G_OBJECT (user), "email");
668         }
669
670         accounts_user_complete_set_email (ACCOUNTS_USER (user), context);  
671 }
672
673
674
675 static gboolean
676 user_set_email (AccountsUser          *auser,
677                 GDBusMethodInvocation *context,
678                 const gchar           *email)
679 {
680         User *user = (User*)auser;
681         int uid;
682         const gchar *action_id;
683
684         if (!get_caller_uid (context, &uid)) {
685                 throw_error (context, ERROR_FAILED, "identifying caller failed");
686                 return FALSE;
687         }
688
689         if (user->uid == (uid_t) uid)
690                 action_id = "org.freedesktop.accounts.change-own-user-data";
691         else
692                 action_id = "org.freedesktop.accounts.user-administration";
693
694         daemon_local_check_auth (user->daemon,
695                                  user,
696                                  action_id,
697                                  TRUE,
698                                  user_change_email_authorized_cb,
699                                  context,
700                                  g_strdup (email),
701                                  (GDestroyNotify)g_free);
702
703         return TRUE;
704 }
705
706 static void
707 user_change_language_authorized_cb (Daemon                *daemon,
708                                     User                  *user,
709                                     GDBusMethodInvocation *context,
710                                     gpointer               data)
711
712 {
713         gchar *language = data;
714
715         if (g_strcmp0 (user->language, language) != 0) {
716                 g_free (user->language);
717                 user->language = g_strdup (language);
718
719                 save_extra_data (user);
720
721                 accounts_user_emit_changed (ACCOUNTS_USER (user));
722
723                 g_object_notify (G_OBJECT (user), "language");
724         }
725
726         accounts_user_complete_set_language (ACCOUNTS_USER (user), context);
727 }
728
729
730
731 static gboolean
732 user_set_language (AccountsUser          *auser,
733                    GDBusMethodInvocation *context,
734                    const gchar           *language)
735 {
736         User *user = (User*)auser;
737         int uid;
738         const gchar *action_id;
739
740         if (!get_caller_uid (context, &uid)) {
741                 throw_error (context, ERROR_FAILED, "identifying caller failed");
742                 return FALSE;
743         }
744
745         if (user->uid == (uid_t) uid)
746                 action_id = "org.freedesktop.accounts.change-own-user-data";
747         else
748                 action_id = "org.freedesktop.accounts.user-administration";
749
750         daemon_local_check_auth (user->daemon,
751                                  user,
752                                  action_id,
753                                  TRUE,
754                                  user_change_language_authorized_cb,
755                                  context,
756                                  g_strdup (language),
757                                  (GDestroyNotify)g_free);
758
759         return TRUE;
760 }
761
762 static void
763 user_change_x_session_authorized_cb (Daemon                *daemon,
764                                      User                  *user,
765                                      GDBusMethodInvocation *context,
766                                      gpointer               data)
767
768 {
769         gchar *x_session = data;
770
771         if (g_strcmp0 (user->x_session, x_session) != 0) {
772                 g_free (user->x_session);
773                 user->x_session = g_strdup (x_session);
774
775                 save_extra_data (user);
776
777                 accounts_user_emit_changed (ACCOUNTS_USER (user));
778
779                 g_object_notify (G_OBJECT (user), "xsession");
780         }
781
782         accounts_user_complete_set_xsession (ACCOUNTS_USER (user), context);
783 }
784
785 static gboolean
786 user_set_x_session (AccountsUser          *auser,
787                     GDBusMethodInvocation *context,
788                     const gchar           *x_session)
789 {
790         User *user = (User*)auser;
791         int uid;
792         const gchar *action_id;
793
794         if (!get_caller_uid (context, &uid)) {
795                 throw_error (context, ERROR_FAILED, "identifying caller failed");
796                 return FALSE;
797         }
798
799         if (user->uid == (uid_t) uid)
800                 action_id = "org.freedesktop.accounts.change-own-user-data";
801         else
802                 action_id = "org.freedesktop.accounts.user-administration";
803
804         daemon_local_check_auth (user->daemon,
805                                  user,
806                                  action_id,
807                                  TRUE,
808                                  user_change_x_session_authorized_cb,
809                                  context,
810                                  g_strdup (x_session),
811                                  (GDestroyNotify) g_free);
812
813         return TRUE;
814 }
815
816 static void
817 user_change_location_authorized_cb (Daemon                *daemon,
818                                     User                  *user,
819                                     GDBusMethodInvocation *context,
820                                     gpointer               data)
821
822 {
823         gchar *location = data;
824
825         if (g_strcmp0 (user->location, location) != 0) {
826                 g_free (user->location);
827                 user->location = g_strdup (location);
828
829                 save_extra_data (user);
830
831                 accounts_user_emit_changed (ACCOUNTS_USER (user));
832
833                 g_object_notify (G_OBJECT (user), "location");
834         }
835
836         accounts_user_complete_set_location (ACCOUNTS_USER (user), context);
837 }
838
839 static gboolean
840 user_set_location (AccountsUser          *auser,
841                    GDBusMethodInvocation *context,
842                    const gchar           *location)
843 {
844         User *user = (User*)auser;
845         int uid;
846         const gchar *action_id;
847
848         if (!get_caller_uid (context, &uid)) {
849                 throw_error (context, ERROR_FAILED, "identifying caller failed");
850                 return FALSE;
851         }
852
853         if (user->uid == (uid_t) uid)
854                 action_id = "org.freedesktop.accounts.change-own-user-data";
855         else
856                 action_id = "org.freedesktop.accounts.user-administration";
857
858         daemon_local_check_auth (user->daemon,
859                                  user,
860                                  action_id,
861                                  TRUE,
862                                  user_change_location_authorized_cb,
863                                  context,
864                                  g_strdup (location),
865                                  (GDestroyNotify)g_free);
866
867         return TRUE;
868 }
869
870 static void
871 user_change_home_dir_authorized_cb (Daemon                *daemon,
872                                     User                  *user,
873                                     GDBusMethodInvocation *context,
874                                     gpointer               data)
875
876 {
877         gchar *home_dir = data;
878         GError *error;
879         const gchar *argv[7];
880
881         if (g_strcmp0 (user->home_dir, home_dir) != 0) {
882                 sys_log (context,
883                          "change home directory of user '%s' (%d) to '%s'",
884                          user->user_name, user->uid, home_dir);
885
886                 argv[0] = "/usr/sbin/usermod";
887                 argv[1] = "-m";
888                 argv[2] = "-d";
889                 argv[3] = home_dir;
890                 argv[4] = "--";
891                 argv[5] = user->user_name;
892                 argv[6] = NULL;
893
894                 error = NULL;
895                 if (!spawn_with_login_uid (context, argv, &error)) {
896                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
897                         g_error_free (error);
898                         return;
899                 }
900
901                 g_free (user->home_dir);
902                 user->home_dir = g_strdup (home_dir);
903                 g_free (user->default_icon_file);
904                 user->default_icon_file = g_build_filename (user->home_dir, ".face", NULL);
905
906                 accounts_user_emit_changed (ACCOUNTS_USER (user));
907
908                 g_object_notify (G_OBJECT (user), "home-directory");
909         }
910
911         accounts_user_complete_set_home_directory (ACCOUNTS_USER (user), context);
912 }
913
914 static gboolean
915 user_set_home_directory (AccountsUser          *auser,
916                          GDBusMethodInvocation *context,
917                          const gchar           *home_dir)
918 {
919         User *user = (User*)auser;
920         daemon_local_check_auth (user->daemon,
921                                  user,
922                                  "org.freedesktop.accounts.user-administration",
923                                  TRUE,
924                                  user_change_home_dir_authorized_cb,
925                                  context,
926                                  g_strdup (home_dir),
927                                  (GDestroyNotify)g_free);
928
929         return TRUE;
930 }
931
932 static void
933 user_change_shell_authorized_cb (Daemon                *daemon,
934                                  User                  *user,
935                                  GDBusMethodInvocation *context,
936                                  gpointer               data)
937
938 {
939         gchar *shell = data;
940         GError *error;
941         const gchar *argv[6];
942
943         if (g_strcmp0 (user->shell, shell) != 0) {
944                 sys_log (context,
945                          "change shell of user '%s' (%d) to '%s'",
946                          user->user_name, user->uid, shell);
947
948                 argv[0] = "/usr/sbin/usermod";
949                 argv[1] = "-s";
950                 argv[2] = shell;
951                 argv[3] = "--";
952                 argv[4] = user->user_name;
953                 argv[5] = NULL;
954
955                 error = NULL;
956                 if (!spawn_with_login_uid (context, argv, &error)) {
957                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
958                         g_error_free (error);
959                         return;
960                 }
961
962                 g_free (user->shell);
963                 user->shell = g_strdup (shell);
964
965                 accounts_user_emit_changed (ACCOUNTS_USER (user));
966
967                 g_object_notify (G_OBJECT (user), "shell");
968         }
969
970         accounts_user_complete_set_shell (ACCOUNTS_USER (user), context);
971 }
972
973 static gboolean
974 user_set_shell (AccountsUser          *auser,
975                 GDBusMethodInvocation *context,
976                 const gchar           *shell)
977 {
978         User *user = (User*)auser;
979         daemon_local_check_auth (user->daemon,
980                                  user,
981                                  "org.freedesktop.accounts.user-administration",
982                                  TRUE,
983                                  user_change_shell_authorized_cb,
984                                  context,
985                                  g_strdup (shell),
986                                  (GDestroyNotify)g_free);
987
988         return TRUE;
989 }
990
991 static void
992 become_user (gpointer data)
993 {
994         struct passwd *pw = data;
995
996         if (pw == NULL ||
997             initgroups (pw->pw_name, pw->pw_gid) != 0 ||
998             setgid (pw->pw_gid) != 0 ||
999             setuid (pw->pw_uid) != 0) {
1000                 exit (1);
1001         }
1002 }
1003
1004 static void
1005 user_change_icon_file_authorized_cb (Daemon                *daemon,
1006                                      User                  *user,
1007                                      GDBusMethodInvocation *context,
1008                                      gpointer               data)
1009
1010 {
1011         gchar *filename;
1012         GFile *file;
1013         GFileInfo *info;
1014         guint32 mode;
1015         GFileType type;
1016         guint64 size;
1017
1018         filename = g_strdup (data);
1019
1020         if (filename == NULL ||
1021             *filename == '\0') {
1022                 char *dest_path;
1023                 GFile *dest;
1024                 GError *error;
1025
1026                 g_free (filename);
1027                 filename = NULL;
1028
1029                 dest_path = g_build_filename (ICONDIR, user->user_name, NULL);
1030                 dest = g_file_new_for_path (dest_path);
1031                 g_free (dest_path);
1032
1033                 error = NULL;
1034                 if (!g_file_delete (dest, NULL, &error) &&
1035                     !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1036                         g_object_unref (dest);
1037                         throw_error (context, ERROR_FAILED, "failed to remove user icon, %s", error->message);
1038                         g_error_free (error);
1039                         return;
1040                 }
1041                 g_object_unref (dest);
1042                 goto icon_saved;
1043         }
1044
1045         file = g_file_new_for_path (filename);
1046         info = g_file_query_info (file, G_FILE_ATTRIBUTE_UNIX_MODE ","
1047                                         G_FILE_ATTRIBUTE_STANDARD_TYPE ","
1048                                         G_FILE_ATTRIBUTE_STANDARD_SIZE,
1049                                   0, NULL, NULL);
1050         mode = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
1051         type = g_file_info_get_file_type (info);
1052         size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
1053
1054         g_object_unref (info);
1055         g_object_unref (file);
1056
1057         if (type != G_FILE_TYPE_REGULAR) {
1058                 g_debug ("not a regular file\n");
1059                 throw_error (context, ERROR_FAILED, "file '%s' is not a regular file", filename);
1060                 g_free (filename);
1061                 return;
1062         }
1063
1064         if (size > 1048576) {
1065                 g_debug ("file too large\n");
1066                 /* 1MB ought to be enough for everybody */
1067                 throw_error (context, ERROR_FAILED, "file '%s' is too large to be used as an icon", filename);
1068                 g_free (filename);
1069                 return;
1070         }
1071
1072         if ((mode & S_IROTH) == 0 ||
1073             (!g_str_has_prefix (filename, DATADIR) &&
1074              !g_str_has_prefix (filename, ICONDIR))) {
1075                 gchar *dest_path;
1076                 GFile *dest;
1077                 const gchar *argv[3];
1078                 gint std_out;
1079                 GError *error;
1080                 GInputStream *input;
1081                 GOutputStream *output;
1082                 gint uid;
1083                 gssize bytes;
1084                 struct passwd *pw;
1085
1086                 if (!get_caller_uid (context, &uid)) {
1087                         throw_error (context, ERROR_FAILED, "failed to copy file, could not determine caller UID");
1088                         g_free (filename);
1089                         return;
1090                 }
1091
1092                 dest_path = g_build_filename (ICONDIR, user->user_name, NULL);
1093                 dest = g_file_new_for_path (dest_path);
1094
1095                 error = NULL;
1096                 output = G_OUTPUT_STREAM (g_file_replace (dest, NULL, FALSE, 0, NULL, &error));
1097                 if (!output) {
1098                         throw_error (context, ERROR_FAILED, "creating file '%s' failed: %s", dest_path, error->message);
1099                         g_error_free (error);
1100                         g_free (filename);
1101                         g_free (dest_path);
1102                         g_object_unref (dest);
1103                         return;
1104                 }
1105
1106                 argv[0] = "/bin/cat";
1107                 argv[1] = filename;
1108                 argv[2] = NULL;
1109
1110                 pw = getpwuid (uid);
1111
1112                 error = NULL;
1113                 if (!g_spawn_async_with_pipes (NULL, (gchar**)argv, NULL, 0, become_user, pw, NULL, NULL, &std_out, NULL, &error)) {
1114                         throw_error (context, ERROR_FAILED, "reading file '%s' failed: %s", filename, error->message);
1115                         g_error_free (error);
1116                         g_free (filename);
1117                         g_free (dest_path);
1118                         g_object_unref (dest);
1119                         return;
1120                 }
1121
1122                 input = g_unix_input_stream_new (std_out, FALSE);
1123
1124                 error = NULL;
1125                 bytes = g_output_stream_splice (output, input, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
1126                 if (bytes < 0 || (gsize)bytes != size) {
1127                         throw_error (context, ERROR_FAILED, "copying file '%s' to '%s' failed: %s", filename, dest_path, error ? error->message : "unknown reason");
1128                         if (error)
1129                                 g_error_free (error);
1130
1131                         g_file_delete (dest, NULL, NULL);
1132
1133                         g_free (filename);
1134                         g_free (dest_path);
1135                         g_object_unref (dest);
1136                         g_object_unref (input);
1137                         g_object_unref (output);
1138                         return;
1139                 }
1140
1141                 g_object_unref (dest);
1142                 g_object_unref (input);
1143                 g_object_unref (output);
1144
1145                 g_free (filename);
1146                 filename = dest_path;
1147         }
1148
1149 icon_saved:
1150         g_free (user->icon_file);
1151         user->icon_file = filename;
1152
1153         save_extra_data (user);
1154
1155         accounts_user_emit_changed (ACCOUNTS_USER (user));
1156
1157         g_object_notify (G_OBJECT (user), "icon-file");
1158
1159         accounts_user_complete_set_icon_file (ACCOUNTS_USER (user), context);
1160 }
1161
1162 static gboolean
1163 user_set_icon_file (AccountsUser          *auser,
1164                     GDBusMethodInvocation *context,
1165                     const gchar           *filename)
1166 {
1167         User *user = (User*)auser;
1168         int uid;
1169         const gchar *action_id;
1170
1171         if (!get_caller_uid (context, &uid)) {
1172                 throw_error (context, ERROR_FAILED, "identifying caller failed");
1173                 return FALSE;
1174         }
1175
1176         if (user->uid == (uid_t) uid)
1177                 action_id = "org.freedesktop.accounts.change-own-user-data";
1178         else
1179                 action_id = "org.freedesktop.accounts.user-administration";
1180
1181         daemon_local_check_auth (user->daemon,
1182                                  user,
1183                                  action_id,
1184                                  TRUE,
1185                                  user_change_icon_file_authorized_cb,
1186                                  context,
1187                                  g_strdup (filename),
1188                                  (GDestroyNotify)g_free);
1189
1190         return TRUE;
1191 }
1192
1193 static void
1194 user_change_locked_authorized_cb (Daemon                *daemon,
1195                                   User                  *user,
1196                                   GDBusMethodInvocation *context,
1197                                   gpointer               data)
1198
1199 {
1200         gboolean locked = GPOINTER_TO_INT (data);
1201         GError *error;
1202         const gchar *argv[5];
1203
1204         if (user->locked != locked) {
1205                 sys_log (context,
1206                          "%s account of user '%s' (%d)",
1207                          locked ? "locking" : "unlocking", user->user_name, user->uid);
1208                 argv[0] = "/usr/sbin/usermod";
1209                 argv[1] = locked ? "-L" : "-U";
1210                 argv[2] = "--";
1211                 argv[3] = user->user_name;
1212                 argv[4] = NULL;
1213
1214                 error = NULL;
1215                 if (!spawn_with_login_uid (context, argv, &error)) {
1216                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
1217                         g_error_free (error);
1218                         return;
1219                 }
1220
1221                 user->locked = locked;
1222
1223                 accounts_user_emit_changed (ACCOUNTS_USER (user));
1224
1225                 g_object_notify (G_OBJECT (user), "locked");
1226         }
1227
1228         accounts_user_complete_set_locked (ACCOUNTS_USER (user), context);
1229 }
1230
1231 static gboolean
1232 user_set_locked (AccountsUser          *auser,
1233                  GDBusMethodInvocation *context,
1234                  gboolean               locked)
1235 {
1236         User *user = (User*)auser;
1237         daemon_local_check_auth (user->daemon,
1238                                  user,
1239                                  "org.freedesktop.accounts.user-administration",
1240                                  TRUE,
1241                                  user_change_locked_authorized_cb,
1242                                  context,
1243                                  GINT_TO_POINTER (locked),
1244                                  NULL);
1245
1246         return TRUE;
1247 }
1248
1249 static void
1250 user_change_account_type_authorized_cb (Daemon                *daemon,
1251                                         User                  *user,
1252                                         GDBusMethodInvocation *context,
1253                                         gpointer               data)
1254
1255 {
1256         AccountType account_type = GPOINTER_TO_INT (data);
1257         GError *error;
1258         gid_t *groups;
1259         gint ngroups;
1260         GString *str;
1261         gid_t wheel;
1262         struct group *grp;
1263         gint i;
1264         const gchar *argv[6];
1265
1266         if (user->account_type != account_type) {
1267                 sys_log (context,
1268                          "change account type of user '%s' (%d) to %d",
1269                          user->user_name, user->uid, account_type);
1270
1271                 grp = getgrnam ("wheel");
1272                 if (grp == NULL) {
1273                         throw_error (context, ERROR_FAILED, "failed to set account type: wheel group not found");
1274                         return;
1275                 }
1276                 wheel = grp->gr_gid;
1277
1278                 ngroups = get_user_groups (user->user_name, user->gid, &groups);
1279
1280                 str = g_string_new ("");
1281                 for (i = 0; i < ngroups; i++) {
1282                         if (groups[i] == wheel)
1283                                 continue;
1284                         g_string_append_printf (str, "%d,", groups[i]);
1285                 }
1286                 switch (account_type) {
1287                 case ACCOUNT_TYPE_ADMINISTRATOR:
1288                         g_string_append_printf (str, "%d", wheel);
1289                         break;
1290                 case ACCOUNT_TYPE_STANDARD:
1291                 default:
1292                         /* remove excess comma */
1293                         g_string_truncate (str, str->len - 1);
1294                         break;
1295                 }
1296
1297                 g_free (groups);
1298
1299                 argv[0] = "/usr/sbin/usermod";
1300                 argv[1] = "-G";
1301                 argv[2] = str->str;
1302                 argv[3] = "--";
1303                 argv[4] = user->user_name;
1304                 argv[5] = NULL;
1305
1306                 g_string_free (str, FALSE);
1307
1308                 error = NULL;
1309                 if (!spawn_with_login_uid (context, argv, &error)) {
1310                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
1311                         g_error_free (error);
1312                         return;
1313                 }
1314
1315                 user->account_type = account_type;
1316
1317                 accounts_user_emit_changed (ACCOUNTS_USER (user));
1318
1319                 g_object_notify (G_OBJECT (user), "account-type");
1320         }
1321
1322         accounts_user_complete_set_account_type (ACCOUNTS_USER (user), context);
1323 }
1324
1325 static void
1326 user_change_role_authorized_cb (Daemon                *daemon,
1327                                 User                  *user,
1328                                 GDBusMethodInvocation *context,
1329                                 gchar                 *role)
1330 {
1331         g_free(role);
1332
1333         accounts_user_complete_set_role (ACCOUNTS_USER (user), context);
1334
1335         return;
1336 }
1337
1338 static gboolean
1339 user_set_account_type (AccountsUser          *auser,
1340                        GDBusMethodInvocation *context,
1341                        gint                   account_type)
1342 {
1343         User *user = (User*)auser;
1344         if (account_type < 0 || account_type > ACCOUNT_TYPE_LAST) {
1345                 throw_error (context, ERROR_FAILED, "unknown account type: %d", account_type);
1346                 return FALSE;
1347         }
1348
1349         daemon_local_check_auth (user->daemon,
1350                                  user,
1351                                  "org.freedesktop.accounts.user-administration",
1352                                  TRUE,
1353                                  user_change_account_type_authorized_cb,
1354                                  context,
1355                                  GINT_TO_POINTER (account_type),
1356                                  NULL);
1357
1358         return TRUE;
1359 }
1360
1361 static gboolean
1362 user_set_role         (AccountsUser          *auser,
1363                        GDBusMethodInvocation *context,
1364                        const gchar           *role)
1365 {
1366
1367         gchar * role_dup;
1368         role_dup = g_strdup (role);
1369
1370         User *user = (User*)auser;
1371
1372         daemon_local_check_auth (user->daemon,
1373                                  user,
1374                                  "org.freedesktop.accounts.user-administration",
1375                                  TRUE,
1376                                  user_change_role_authorized_cb,
1377                                  context,
1378                                  role_dup,
1379                                  NULL);
1380
1381         return TRUE;
1382 }
1383
1384
1385
1386 static void
1387 user_change_password_mode_authorized_cb (Daemon                *daemon,
1388                                          User                  *user,
1389                                          GDBusMethodInvocation *context,
1390                                          gpointer               data)
1391
1392 {
1393         PasswordMode mode = GPOINTER_TO_INT (data);
1394         GError *error;
1395         const gchar *argv[6];
1396
1397         if (user->password_mode != mode) {
1398                 sys_log (context,
1399                          "change password mode of user '%s' (%d) to %d",
1400                          user->user_name, user->uid, mode);
1401
1402                 g_object_freeze_notify (G_OBJECT (user));
1403
1404                 if (mode == PASSWORD_MODE_SET_AT_LOGIN ||
1405                     mode == PASSWORD_MODE_NONE) {
1406
1407                         argv[0] = "/usr/bin/passwd";
1408                         argv[1] = "-d";
1409                         argv[2] = "--";
1410                         argv[3] = user->user_name;
1411                         argv[4] = NULL;
1412
1413                         error = NULL;
1414                         if (!spawn_with_login_uid (context, argv, &error)) {
1415                                 throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
1416                                 g_error_free (error);
1417                                 return;
1418                         }
1419
1420                         if (mode == PASSWORD_MODE_SET_AT_LOGIN) {
1421                                 argv[0] = "/usr/bin/chage";
1422                                 argv[1] = "-d";
1423                                 argv[2] = "0";
1424                                 argv[3] = "--";
1425                                 argv[4] = user->user_name;
1426                                 argv[5] = NULL;
1427
1428                                 error = NULL;
1429                                 if (!spawn_with_login_uid (context, argv, &error)) {
1430                                         throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
1431                                         g_error_free (error);
1432                                         return;
1433                                 }
1434                         }
1435
1436                         g_free (user->password_hint);
1437                         user->password_hint = NULL;
1438
1439                         g_object_notify (G_OBJECT (user), "password-hint");
1440
1441                         /* removing the password has the side-effect of
1442                          * unlocking the account
1443                          */
1444                         if (user->locked) {
1445                                 user->locked = FALSE;
1446                                 g_object_notify (G_OBJECT (user), "locked");
1447                         }
1448                 }
1449                 else if (user->locked) {
1450                         argv[0] = "/usr/sbin/usermod";
1451                         argv[1] = "-U";
1452                         argv[2] = "--";
1453                         argv[3] = user->user_name;
1454                         argv[4] = NULL;
1455
1456                         error = NULL;
1457                         if (!spawn_with_login_uid (context, argv, &error)) {
1458                                 throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
1459                                 g_error_free (error);
1460                                 return;
1461                         }
1462
1463                         user->locked = FALSE;
1464                         g_object_notify (G_OBJECT (user), "locked");
1465                 }
1466
1467                 user->password_mode = mode;
1468
1469                 g_object_notify (G_OBJECT (user), "password-mode");
1470
1471                 save_extra_data (user);
1472
1473                 g_object_thaw_notify (G_OBJECT (user));
1474
1475                 accounts_user_emit_changed (ACCOUNTS_USER (user));
1476         }
1477
1478         accounts_user_complete_set_password_mode (ACCOUNTS_USER (user), context);
1479 }
1480
1481 static gboolean
1482 user_set_password_mode (AccountsUser          *auser,
1483                         GDBusMethodInvocation *context,
1484                         gint                   mode)
1485 {
1486         User *user = (User*)auser;
1487         const gchar *action_id;
1488
1489         if (mode < 0 || mode > PASSWORD_MODE_LAST) {
1490                 throw_error (context, ERROR_FAILED, "unknown password mode: %d", mode);
1491                 return FALSE;
1492         }
1493
1494         action_id = "org.freedesktop.accounts.user-administration";
1495
1496         daemon_local_check_auth (user->daemon,
1497                                  user,
1498                                  action_id,
1499                                  TRUE,
1500                                  user_change_password_mode_authorized_cb,
1501                                  context,
1502                                  GINT_TO_POINTER (mode),
1503                                  NULL);
1504
1505         return TRUE;
1506 }
1507
1508 static void
1509 user_change_password_authorized_cb (Daemon                *daemon,
1510                                     User                  *user,
1511                                     GDBusMethodInvocation *context,
1512                                     gpointer               data)
1513
1514 {
1515         gchar **strings = data;
1516         GError *error;
1517         const gchar *argv[6];
1518
1519         sys_log (context,
1520                  "set password and hint of user '%s' (%d)",
1521                  user->user_name, user->uid);
1522
1523         g_object_freeze_notify (G_OBJECT (user));
1524
1525         argv[0] = "/usr/sbin/usermod";
1526         argv[1] = "-p";
1527         argv[2] = strings[0];
1528         argv[3] = "--";
1529         argv[4] = user->user_name;
1530         argv[5] = NULL;
1531
1532         error = NULL;
1533         if (!spawn_with_login_uid (context, argv, &error)) {
1534                 throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
1535                 g_error_free (error);
1536                 return;
1537         }
1538
1539         if (user->password_mode != PASSWORD_MODE_REGULAR) {
1540                 user->password_mode = PASSWORD_MODE_REGULAR;
1541                 g_object_notify (G_OBJECT (user), "password-mode");
1542         }
1543
1544         if (user->locked) {
1545                 user->locked = FALSE;
1546                 g_object_notify (G_OBJECT (user), "locked");
1547         }
1548
1549         if (g_strcmp0 (user->password_hint, strings[1]) != 0) {
1550                 g_free (user->password_hint);
1551                 user->password_hint = g_strdup (strings[1]);
1552                 g_object_notify (G_OBJECT (user), "password-hint");
1553         }
1554
1555         save_extra_data (user);
1556
1557         g_object_thaw_notify (G_OBJECT (user));
1558
1559         accounts_user_emit_changed (ACCOUNTS_USER (user));
1560
1561         accounts_user_complete_set_password (ACCOUNTS_USER (user), context);
1562 }
1563
1564 static void
1565 free_passwords (gchar **strings)
1566 {
1567         memset (strings[0], 0, strlen (strings[0]));
1568         g_strfreev (strings);
1569 }
1570
1571 static gboolean
1572 user_set_password (AccountsUser          *auser,
1573                    GDBusMethodInvocation *context,
1574                    const gchar           *password,
1575                    const gchar           *hint)
1576 {
1577         User *user = (User*)auser;
1578         gchar **data;
1579
1580         data = g_new (gchar *, 3);
1581         data[0] = g_strdup (password);
1582         data[1] = g_strdup (hint);
1583         data[2] = NULL;
1584
1585         daemon_local_check_auth (user->daemon,
1586                                  user,
1587                                  "org.freedesktop.accounts.user-administration",
1588                                  TRUE,
1589                                  user_change_password_authorized_cb,
1590                                  context,
1591                                  data,
1592                                  (GDestroyNotify)free_passwords);
1593
1594         memset ((char*)password, 0, strlen (password));
1595
1596         return TRUE;
1597 }
1598
1599 static void
1600 user_change_automatic_login_authorized_cb (Daemon                *daemon,
1601                                            User                  *user,
1602                                            GDBusMethodInvocation *context,
1603                                            gpointer               data)
1604 {
1605         gboolean enabled = GPOINTER_TO_INT (data);
1606         GError *error = NULL;
1607
1608         sys_log (context,
1609                  "%s automatic login for user '%s' (%d)",
1610                  enabled ? "enable" : "disable", user->user_name, user->uid);
1611
1612         if (!daemon_local_set_automatic_login (daemon, user, enabled, &error)) {
1613                 throw_error (context, ERROR_FAILED, "failed to change automatic login: %s", error->message);
1614                 g_error_free (error);
1615                 return;
1616         }
1617
1618         accounts_user_complete_set_automatic_login (ACCOUNTS_USER (user), context);
1619 }
1620
1621 static gboolean
1622 user_set_automatic_login (AccountsUser          *auser,
1623                           GDBusMethodInvocation *context,
1624                           gboolean               enabled)
1625 {
1626         User *user = (User*)auser;
1627         daemon_local_check_auth (user->daemon,
1628                                  user,
1629                                  "org.freedesktop.accounts.user-administration",
1630                                  TRUE,
1631                                  user_change_automatic_login_authorized_cb,
1632                                  context,
1633                                  GINT_TO_POINTER (enabled),
1634                                  NULL);
1635
1636         return TRUE;
1637 }
1638
1639 static guint64
1640 user_get_uid (AccountsUser *user)
1641 {
1642         return (guint64) USER (user)->uid;
1643 }
1644
1645 static const gchar *
1646 user_get_user_name (AccountsUser *user)
1647 {
1648         return USER (user)->user_name;
1649 }
1650
1651 static const gchar *
1652 user_get_real_name (AccountsUser *user)
1653 {
1654         return USER (user)->real_name;
1655 }
1656
1657 static gint
1658 user_get_account_type (AccountsUser *user)
1659 {
1660         return (gint) USER (user)->account_type;
1661 }
1662
1663 static const gchar *
1664 user_get_home_directory (AccountsUser *user)
1665 {
1666         return USER (user)->home_dir;
1667 }
1668
1669 static const gchar *
1670 user_get_shell (AccountsUser *user)
1671 {
1672         return USER (user)->shell;
1673 }
1674
1675 static const gchar *
1676 user_get_email (AccountsUser *user)
1677 {
1678         return USER (user)->email;
1679 }
1680
1681 static const gchar *
1682 user_get_language (AccountsUser *user)
1683 {
1684         return USER (user)->language;
1685 }
1686
1687 static const gchar *
1688 user_get_xsession (AccountsUser *user)
1689 {
1690         return USER (user)->x_session;
1691 }
1692
1693 static const gchar *
1694 user_get_location (AccountsUser *user)
1695 {
1696         return USER (user)->location;
1697 }
1698
1699 static guint64
1700 user_get_login_frequency (AccountsUser *user)
1701 {
1702         return USER (user)->login_frequency;
1703 }
1704
1705 static const gchar *
1706 user_get_icon_file (AccountsUser *user)
1707 {
1708         if (USER (user)->icon_file)
1709                 return USER (user)->icon_file;
1710         else
1711                 return USER (user)->default_icon_file;
1712 }
1713
1714 static gboolean
1715 user_get_locked (AccountsUser *user)
1716 {
1717         return USER (user)->locked;
1718 }
1719
1720 static gint
1721 user_get_password_mode (AccountsUser *user)
1722 {
1723         return USER (user)->password_mode;
1724 }
1725
1726 static const gchar *
1727 user_get_password_hint (AccountsUser *user)
1728 {
1729         return USER (user)->password_hint;
1730 }
1731
1732 static gboolean
1733 user_get_automatic_login (AccountsUser *user)
1734 {
1735         return USER (user)->automatic_login;
1736 }
1737
1738 static gboolean
1739 user_get_system_account (AccountsUser *user)
1740 {
1741         return USER (user)->system_account;
1742 }
1743
1744 static void
1745 user_finalize (GObject *object)
1746 {
1747         User *user;
1748
1749         user = USER (object);
1750
1751         g_free (user->object_path);
1752         g_free (user->user_name);
1753         g_free (user->real_name);
1754         g_free (user->home_dir);
1755         g_free (user->shell);
1756         g_free (user->icon_file);
1757         g_free (user->default_icon_file);
1758         g_free (user->email);
1759         g_free (user->language);
1760         g_free (user->x_session);
1761         g_free (user->location);
1762         g_free (user->password_hint);
1763
1764         if (G_OBJECT_CLASS (user_parent_class)->finalize)
1765                 (*G_OBJECT_CLASS (user_parent_class)->finalize) (object);
1766 }
1767
1768 static void
1769 user_set_property (GObject      *object,
1770                    guint         param_id,
1771                    const GValue *value,
1772                    GParamSpec   *pspec)
1773 {
1774         User *user = USER (object);
1775
1776         switch (param_id) {
1777         case PROP_ACCOUNT_TYPE:
1778                 user->account_type = g_value_get_int (value);
1779                 break;
1780         case PROP_LANGUAGE:
1781                 user->language = g_value_dup_string (value);
1782                 break;
1783         case PROP_X_SESSION:
1784                 user->x_session = g_value_dup_string (value);
1785                 break;
1786         case PROP_EMAIL:
1787                 user->email = g_value_dup_string (value);
1788                 break;
1789         case PROP_LOGIN_FREQUENCY:
1790                 user->login_frequency = g_value_get_uint64 (value);
1791                 break;
1792         case PROP_AUTOMATIC_LOGIN:
1793                 user->automatic_login = g_value_get_boolean (value);
1794                 break;
1795         case PROP_SYSTEM_ACCOUNT:
1796                 user->system_account = g_value_get_boolean (value);
1797                 break;
1798         default:
1799                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1800                 break;
1801         }
1802 }
1803
1804 static void
1805 user_get_property (GObject    *object,
1806                    guint       param_id,
1807                    GValue     *value,
1808                    GParamSpec *pspec)
1809 {
1810         User *user = USER (object);
1811
1812         switch (param_id) {
1813         case PROP_UID:
1814                 g_value_set_uint64 (value, user->uid);
1815                 break;
1816         case PROP_USER_NAME:
1817                 g_value_set_string (value, user->user_name);
1818                 break;
1819         case PROP_REAL_NAME:
1820                 g_value_set_string (value, user->real_name);
1821                 break;
1822         case PROP_ACCOUNT_TYPE:
1823                 g_value_set_int (value, user->account_type);
1824                 break;
1825         case PROP_PASSWORD_MODE:
1826                 g_value_set_int (value, user->password_mode);
1827                 break;
1828         case PROP_PASSWORD_HINT:
1829                 g_value_set_string (value, user->password_hint);
1830                 break;
1831         case PROP_HOME_DIR:
1832                 g_value_set_string (value, user->home_dir);
1833                 break;
1834         case PROP_SHELL:
1835                 g_value_set_string (value, user->shell);
1836                 break;
1837         case PROP_EMAIL:
1838                 g_value_set_string (value, user->email);
1839                 break;
1840         case PROP_LANGUAGE:
1841                 g_value_set_string (value, user->language);
1842                 break;
1843         case PROP_X_SESSION:
1844                 g_value_set_string (value, user->x_session);
1845                 break;
1846         case PROP_LOCATION:
1847                 g_value_set_string (value, user->location);
1848                 break;
1849         case PROP_ICON_FILE:
1850                 if (user->icon_file)
1851                         g_value_set_string (value, user->icon_file);
1852                 else {
1853                         gchar *icon_file;
1854
1855                         icon_file = g_build_filename (user->home_dir, ".face", NULL);
1856                         g_value_take_string (value, icon_file);
1857                 }
1858                 break;
1859         case PROP_LOGIN_FREQUENCY:
1860                 g_value_set_uint64 (value, user->login_frequency);
1861                 break;
1862         case PROP_LOCKED:
1863                 g_value_set_boolean (value, user->locked);
1864                 break;
1865         case PROP_AUTOMATIC_LOGIN:
1866                 g_value_set_boolean (value, user->automatic_login);
1867                 break;
1868         case PROP_SYSTEM_ACCOUNT:
1869                 g_value_set_boolean (value, user->system_account);
1870                 break;
1871         default:
1872                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1873                 break;
1874         }
1875 }
1876
1877 static void
1878 user_class_init (UserClass *class)
1879 {
1880         GObjectClass *gobject_class;
1881
1882         gobject_class = G_OBJECT_CLASS (class);
1883
1884         gobject_class->get_property = user_get_property;
1885         gobject_class->set_property = user_set_property;
1886         gobject_class->finalize = user_finalize;
1887
1888         accounts_user_override_properties (gobject_class, 1);
1889 }
1890
1891 static void
1892 user_accounts_user_iface_init (AccountsUserIface *iface)
1893 {
1894         iface->handle_set_account_type = user_set_account_type;
1895         iface->handle_set_role = user_set_role;
1896         iface->handle_set_automatic_login = user_set_automatic_login;
1897         iface->handle_set_email = user_set_email;
1898         iface->handle_set_home_directory = user_set_home_directory;
1899         iface->handle_set_icon_file = user_set_icon_file;
1900         iface->handle_set_language = user_set_language;
1901         iface->handle_set_location = user_set_location;
1902         iface->handle_set_locked = user_set_locked;
1903         iface->handle_set_password = user_set_password;
1904         iface->handle_set_password_mode = user_set_password_mode;
1905         iface->handle_set_real_name = user_set_real_name;
1906         iface->handle_set_shell = user_set_shell;
1907         iface->handle_set_user_name = user_set_user_name;
1908         iface->handle_set_xsession = user_set_x_session;
1909         iface->get_uid = user_get_uid;
1910         iface->get_user_name = user_get_user_name;
1911         iface->get_real_name = user_get_real_name;
1912         iface->get_account_type = user_get_account_type;
1913         iface->get_home_directory = user_get_home_directory;
1914         iface->get_shell = user_get_shell;
1915         iface->get_email = user_get_email;
1916         iface->get_language = user_get_language;
1917         iface->get_xsession = user_get_xsession;
1918         iface->get_location = user_get_location;
1919         iface->get_login_frequency = user_get_login_frequency;
1920         iface->get_icon_file = user_get_icon_file;
1921         iface->get_locked = user_get_locked;
1922         iface->get_password_mode = user_get_password_mode;
1923         iface->get_password_hint = user_get_password_hint;
1924         iface->get_automatic_login = user_get_automatic_login;
1925         iface->get_system_account = user_get_system_account;
1926 }
1927
1928 static void
1929 user_init (User *user)
1930 {
1931         user->system_bus_connection = NULL;
1932         user->object_path = NULL;
1933         user->user_name = NULL;
1934         user->real_name = NULL;
1935         user->account_type = ACCOUNT_TYPE_STANDARD;
1936         user->home_dir = NULL;
1937         user->shell = NULL;
1938         user->icon_file = NULL;
1939         user->default_icon_file = NULL;
1940         user->email = NULL;
1941         user->language = NULL;
1942         user->x_session = NULL;
1943         user->location = NULL;
1944         user->password_mode = PASSWORD_MODE_REGULAR;
1945         user->password_hint = NULL;
1946         user->locked = FALSE;
1947         user->automatic_login = FALSE;
1948         user->system_account = FALSE;
1949 }