~ubuntu-branches/ubuntu/raring/accountsservice/raring

« back to all changes in this revision

Viewing changes to .pc/0012-add-keyboard-layout-support.patch/src/user.c

  • Committer: Package Import Robot
  • Author(s): Michael Terry
  • Date: 2012-02-10 22:48:39 UTC
  • Revision ID: package-import@ubuntu.com-20120210224839-741j7fovc3uws9p1
Tags: 0.6.15-2ubuntu5
* debian/patches/0012-add-keyboard-layout-support.patch:
  - Add XKeyboardLayouts property to report a user's keyboard layouts,
    largely for LightDM's benefit.  LP: #915468

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
43
#include <dbus/dbus-glib.h>
 
44
#include <dbus/dbus-glib-lowlevel.h>
 
45
 
 
46
#include "daemon.h"
 
47
#include "user.h"
 
48
#include "user-glue.h"
 
49
#include "util.h"
 
50
 
 
51
#define ICONDIR LOCALSTATEDIR "/lib/AccountsService/icons"
 
52
 
 
53
enum {
 
54
        PROP_0,
 
55
        PROP_UID,
 
56
        PROP_USER_NAME,
 
57
        PROP_REAL_NAME,
 
58
        PROP_HOME_DIR,
 
59
        PROP_SHELL,
 
60
        PROP_ACCOUNT_TYPE,
 
61
        PROP_EMAIL,
 
62
        PROP_LANGUAGE,
 
63
        PROP_FORMATS_LOCALE,
 
64
        PROP_X_SESSION,
 
65
        PROP_LOCATION,
 
66
        PROP_PASSWORD_MODE,
 
67
        PROP_PASSWORD_HINT,
 
68
        PROP_LOGIN_FREQUENCY,
 
69
        PROP_BACKGROUND_FILE,
 
70
        PROP_ICON_FILE,
 
71
        PROP_LOCKED,
 
72
        PROP_AUTOMATIC_LOGIN,
 
73
        PROP_SYSTEM_ACCOUNT
 
74
};
 
75
 
 
76
enum {
 
77
        CHANGED,
 
78
        LAST_SIGNAL
 
79
};
 
80
 
 
81
static guint signals[LAST_SIGNAL] = { 0 };
 
82
 
 
83
struct User {
 
84
        GObject       parent;
 
85
 
 
86
        DBusGConnection *system_bus_connection;
 
87
        gchar *object_path;
 
88
 
 
89
        Daemon       *daemon;
 
90
 
 
91
        uid_t         uid;
 
92
        gid_t         gid;
 
93
        gchar        *user_name;
 
94
        gchar        *real_name;
 
95
        AccountType   account_type;
 
96
        PasswordMode  password_mode;
 
97
        gchar        *password_hint;
 
98
        gchar        *home_dir;
 
99
        gchar        *shell;
 
100
        gchar        *email;
 
101
        gchar        *language;
 
102
        gchar        *formats_locale;
 
103
        gchar        *x_session;
 
104
        gchar        *location;
 
105
        guint64       login_frequency;
 
106
        gchar        *background_file;
 
107
        gchar        *icon_file;
 
108
        gboolean      locked;
 
109
        gboolean      automatic_login;
 
110
        gboolean      system_account;
 
111
};
 
112
 
 
113
typedef struct UserClass
 
114
{
 
115
        GObjectClass parent_class;
 
116
} UserClass;
 
117
 
 
118
static void user_finalize (GObject *object);
 
119
static gchar *user_get_fallback_value (User        *user,
 
120
                                       const gchar *property);
 
121
 
 
122
G_DEFINE_TYPE (User, user, G_TYPE_OBJECT)
 
123
 
 
124
static void
 
125
user_set_property (GObject      *object,
 
126
                   guint         param_id,
 
127
                   const GValue *value,
 
128
                   GParamSpec   *pspec)
 
129
{
 
130
        User *user = USER (object);
 
131
 
 
132
        switch (param_id) {
 
133
        case PROP_ACCOUNT_TYPE:
 
134
                user->account_type = g_value_get_int (value);
 
135
                break;
 
136
        case PROP_LANGUAGE:
 
137
                user->language = g_value_dup_string (value);
 
138
                break;
 
139
        case PROP_FORMATS_LOCALE:
 
140
                user->formats_locale = g_value_dup_string (value);
 
141
                break;
 
142
        case PROP_X_SESSION:
 
143
                user->x_session = g_value_dup_string (value);
 
144
                break;
 
145
        case PROP_EMAIL:
 
146
                user->email = g_value_dup_string (value);
 
147
                break;
 
148
        case PROP_LOGIN_FREQUENCY:
 
149
                user->login_frequency = g_value_get_uint64 (value);
 
150
                break;
 
151
        case PROP_AUTOMATIC_LOGIN:
 
152
                user->automatic_login = g_value_get_boolean (value);
 
153
                break;
 
154
        case PROP_SYSTEM_ACCOUNT:
 
155
                user->system_account = g_value_get_boolean (value);
 
156
                break;
 
157
        default:
 
158
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
159
                break;
 
160
        }
 
161
}
 
162
 
 
163
static void
 
164
user_get_property (GObject    *object,
 
165
                   guint       param_id,
 
166
                   GValue     *value,
 
167
                   GParamSpec *pspec)
 
168
{
 
169
        User *user = USER (object);
 
170
 
 
171
        switch (param_id) {
 
172
        case PROP_UID:
 
173
                g_value_set_uint64 (value, user->uid);
 
174
                break;
 
175
        case PROP_USER_NAME:
 
176
                g_value_set_string (value, user->user_name);
 
177
                break;
 
178
        case PROP_REAL_NAME:
 
179
                g_value_set_string (value, user->real_name);
 
180
                break;
 
181
        case PROP_ACCOUNT_TYPE:
 
182
                g_value_set_int (value, user->account_type);
 
183
                break;
 
184
        case PROP_PASSWORD_MODE:
 
185
                g_value_set_int (value, user->password_mode);
 
186
                break;
 
187
        case PROP_PASSWORD_HINT:
 
188
                g_value_set_string (value, user->password_hint);
 
189
                break;
 
190
        case PROP_HOME_DIR:
 
191
                g_value_set_string (value, user->home_dir);
 
192
                break;
 
193
        case PROP_SHELL:
 
194
                g_value_set_string (value, user->shell);
 
195
                break;
 
196
        case PROP_EMAIL:
 
197
                g_value_set_string (value, user->email);
 
198
                break;
 
199
        case PROP_LANGUAGE:
 
200
                if (user->language)
 
201
                        g_value_set_string (value, user->language);
 
202
                else
 
203
                        g_value_set_string (value, user_get_fallback_value (user, "Language"));
 
204
                break;
 
205
        case PROP_FORMATS_LOCALE:
 
206
                if (user->formats_locale)
 
207
                        g_value_set_string (value, user->formats_locale);
 
208
                else
 
209
                        g_value_set_string (value, user_get_fallback_value (user, "FormatsLocale"));
 
210
                break;
 
211
        case PROP_X_SESSION:
 
212
                g_value_set_string (value, user->x_session);
 
213
                break;
 
214
        case PROP_LOCATION:
 
215
                g_value_set_string (value, user->location);
 
216
                break;
 
217
        case PROP_BACKGROUND_FILE:
 
218
                g_value_set_string (value, user->background_file);
 
219
                break;
 
220
        case PROP_ICON_FILE:
 
221
                if (user->icon_file)
 
222
                        g_value_set_string (value, user->icon_file);
 
223
                else {
 
224
                        gchar *icon_file;
 
225
 
 
226
                        icon_file = g_build_filename (user->home_dir, ".face", NULL);
 
227
                        g_value_take_string (value, icon_file);
 
228
                }
 
229
                break;
 
230
        case PROP_LOGIN_FREQUENCY:
 
231
                g_value_set_uint64 (value, user->login_frequency);
 
232
                break;
 
233
        case PROP_LOCKED:
 
234
                g_value_set_boolean (value, user->locked);
 
235
                break;
 
236
        case PROP_AUTOMATIC_LOGIN:
 
237
                g_value_set_boolean (value, user->automatic_login);
 
238
                break;
 
239
        case PROP_SYSTEM_ACCOUNT:
 
240
                g_value_set_boolean (value, user->system_account);
 
241
                break;
 
242
        default:
 
243
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
244
                break;
 
245
        }
 
246
}
 
247
 
 
248
static void
 
249
user_class_init (UserClass *class)
 
250
{
 
251
        GObjectClass *gobject_class;
 
252
 
 
253
        gobject_class = G_OBJECT_CLASS (class);
 
254
 
 
255
        gobject_class->get_property = user_get_property;
 
256
        gobject_class->set_property = user_set_property;
 
257
        gobject_class->finalize = user_finalize;
 
258
 
 
259
        dbus_g_object_type_install_info (TYPE_USER,
 
260
                                         &dbus_glib_user_object_info);
 
261
 
 
262
        signals[CHANGED] = g_signal_new ("changed",
 
263
                                         G_OBJECT_CLASS_TYPE (class),
 
264
                                         G_SIGNAL_RUN_LAST,
 
265
                                         0,
 
266
                                         NULL,
 
267
                                         NULL,
 
268
                                         g_cclosure_marshal_VOID__VOID,
 
269
                                         G_TYPE_NONE,
 
270
                                         0);
 
271
 
 
272
        g_object_class_install_property (gobject_class,
 
273
                                         PROP_REAL_NAME,
 
274
                                         g_param_spec_string ("real-name",
 
275
                                                              "Real Name",
 
276
                                                              "The real name to display for this user.",
 
277
                                                              NULL,
 
278
                                                              G_PARAM_READABLE));
 
279
 
 
280
        g_object_class_install_property (gobject_class,
 
281
                                         PROP_ACCOUNT_TYPE,
 
282
                                         g_param_spec_int ("account-type",
 
283
                                                           "Account Type",
 
284
                                                           "The account type for this user.",
 
285
                                                           0, ACCOUNT_TYPE_LAST,
 
286
                                                           0,
 
287
                                                           G_PARAM_READWRITE));
 
288
 
 
289
        g_object_class_install_property (gobject_class,
 
290
                                         PROP_PASSWORD_MODE,
 
291
                                         g_param_spec_int ("password-mode",
 
292
                                                           "Password Mode",
 
293
                                                           "The password mode for this user.",
 
294
                                                           0, PASSWORD_MODE_LAST,
 
295
                                                           PASSWORD_MODE_REGULAR,
 
296
                                                           G_PARAM_READABLE));
 
297
 
 
298
        g_object_class_install_property (gobject_class,
 
299
                                         PROP_PASSWORD_HINT,
 
300
                                         g_param_spec_string ("password-hint",
 
301
                                                              "Password Hint",
 
302
                                                              "Hint to help this user remember his password",
 
303
                                                              NULL,
 
304
                                                              G_PARAM_READABLE));
 
305
 
 
306
        g_object_class_install_property (gobject_class,
 
307
                                         PROP_UID,
 
308
                                         g_param_spec_uint64 ("uid",
 
309
                                                              "User ID",
 
310
                                                              "The UID for this user.",
 
311
                                                              0, G_MAXUINT64, 0,
 
312
                                                              G_PARAM_READABLE));
 
313
        g_object_class_install_property (gobject_class,
 
314
                                         PROP_USER_NAME,
 
315
                                         g_param_spec_string ("user-name",
 
316
                                                              "User Name",
 
317
                                                              "The login name for this user.",
 
318
                                                              NULL,
 
319
                                                              G_PARAM_READABLE));
 
320
 
 
321
        g_object_class_install_property (gobject_class,
 
322
                                         PROP_HOME_DIR,
 
323
                                         g_param_spec_string ("home-directory",
 
324
                                                              "Home Directory",
 
325
                                                              "The home directory for this user.",
 
326
                                                              NULL,
 
327
                                                              G_PARAM_READABLE));
 
328
        g_object_class_install_property (gobject_class,
 
329
                                         PROP_SHELL,
 
330
                                         g_param_spec_string ("shell",
 
331
                                                              "Shell",
 
332
                                                              "The shell for this user.",
 
333
                                                              NULL,
 
334
                                                              G_PARAM_READABLE));
 
335
        g_object_class_install_property (gobject_class,
 
336
                                         PROP_EMAIL,
 
337
                                         g_param_spec_string ("email",
 
338
                                                              "Email",
 
339
                                                              "The email address for this user.",
 
340
                                                              NULL,
 
341
                                                              G_PARAM_READABLE));
 
342
        g_object_class_install_property (gobject_class,
 
343
                                         PROP_LANGUAGE,
 
344
                                         g_param_spec_string ("language",
 
345
                                                              "Language",
 
346
                                                              "The language for this user.",
 
347
                                                              NULL,
 
348
                                                              G_PARAM_READABLE));
 
349
        g_object_class_install_property (gobject_class,
 
350
                                         PROP_FORMATS_LOCALE,
 
351
                                         g_param_spec_string ("formats_locale",
 
352
                                                              "Regional Formats",
 
353
                                                              "The regional formats for this user.",
 
354
                                                              NULL,
 
355
                                                              G_PARAM_READABLE));
 
356
        g_object_class_install_property (gobject_class,
 
357
                                         PROP_X_SESSION,
 
358
                                         g_param_spec_string ("x-session",
 
359
                                                              "X Session",
 
360
                                                              "The session this user logs into.",
 
361
                                                              NULL,
 
362
                                                              G_PARAM_READABLE));
 
363
        g_object_class_install_property (gobject_class,
 
364
                                         PROP_LOCATION,
 
365
                                         g_param_spec_string ("location",
 
366
                                                              "Location",
 
367
                                                              "The location of this user.",
 
368
                                                              NULL,
 
369
                                                              G_PARAM_READABLE));
 
370
        g_object_class_install_property (gobject_class,
 
371
                                         PROP_LOGIN_FREQUENCY,
 
372
                                         g_param_spec_uint64 ("login-frequency",
 
373
                                                              "login frequency",
 
374
                                                              "login frequency",
 
375
                                                              0,
 
376
                                                              G_MAXUINT64,
 
377
                                                              0,
 
378
                                                              G_PARAM_READWRITE));
 
379
        g_object_class_install_property (gobject_class,
 
380
                                         PROP_BACKGROUND_FILE,
 
381
                                         g_param_spec_string ("background-file",
 
382
                                                              "Background file",
 
383
                                                              "The background file to use for this user.",
 
384
                                                              NULL,
 
385
                                                              G_PARAM_READABLE));
 
386
        g_object_class_install_property (gobject_class,
 
387
                                         PROP_ICON_FILE,
 
388
                                         g_param_spec_string ("icon-file",
 
389
                                                              "Icon file",
 
390
                                                              "The icon file to use for this user.",
 
391
                                                              NULL,
 
392
                                                              G_PARAM_READABLE));
 
393
 
 
394
        g_object_class_install_property (gobject_class,
 
395
                                         PROP_LOCKED,
 
396
                                         g_param_spec_boolean ("locked",
 
397
                                                               "Locked",
 
398
                                                               "Locked",
 
399
                                                               FALSE,
 
400
                                                              G_PARAM_READABLE));
 
401
 
 
402
        g_object_class_install_property (gobject_class,
 
403
                                         PROP_AUTOMATIC_LOGIN,
 
404
                                         g_param_spec_boolean ("automatic-login",
 
405
                                                               "Automatic Login",
 
406
                                                               "Automatic Login",
 
407
                                                               FALSE,
 
408
                                                               G_PARAM_READWRITE));
 
409
 
 
410
        g_object_class_install_property (gobject_class,
 
411
                                         PROP_SYSTEM_ACCOUNT,
 
412
                                         g_param_spec_boolean ("system-account",
 
413
                                                               "System Account",
 
414
                                                               "System Account",
 
415
                                                               FALSE,
 
416
                                                               G_PARAM_READWRITE));
 
417
}
 
418
 
 
419
 
 
420
static void
 
421
user_init (User *user)
 
422
{
 
423
        user->system_bus_connection = NULL;
 
424
        user->object_path = NULL;
 
425
        user->user_name = NULL;
 
426
        user->real_name = NULL;
 
427
        user->account_type = ACCOUNT_TYPE_STANDARD;
 
428
        user->home_dir = NULL;
 
429
        user->shell = NULL;
 
430
        user->background_file = NULL;
 
431
        user->icon_file = NULL;
 
432
        user->email = NULL;
 
433
        user->language = NULL;
 
434
        user->formats_locale = NULL;
 
435
        user->x_session = NULL;
 
436
        user->location = NULL;
 
437
        user->password_mode = PASSWORD_MODE_REGULAR;
 
438
        user->password_hint = NULL;
 
439
        user->locked = FALSE;
 
440
        user->automatic_login = FALSE;
 
441
        user->system_account = FALSE;
 
442
}
 
443
 
 
444
static void
 
445
user_finalize (GObject *object)
 
446
{
 
447
        User *user;
 
448
 
 
449
        user = USER (object);
 
450
 
 
451
        g_free (user->object_path);
 
452
        g_free (user->user_name);
 
453
        g_free (user->real_name);
 
454
        g_free (user->home_dir);
 
455
        g_free (user->shell);
 
456
        g_free (user->background_file);
 
457
        g_free (user->icon_file);
 
458
        g_free (user->email);
 
459
        g_free (user->language);
 
460
        g_free (user->formats_locale);
 
461
        g_free (user->x_session);
 
462
        g_free (user->location);
 
463
        g_free (user->password_hint);
 
464
 
 
465
        if (G_OBJECT_CLASS (user_parent_class)->finalize)
 
466
                (*G_OBJECT_CLASS (user_parent_class)->finalize) (object);
 
467
}
 
468
 
 
469
static gint
 
470
account_type_from_pwent (struct passwd *pwent)
 
471
{
 
472
        struct group *grp;
 
473
        gid_t sudo;
 
474
        gid_t admin;
 
475
        gid_t *groups;
 
476
        gint ngroups;
 
477
        gint i;
 
478
 
 
479
        if (pwent->pw_uid == 0) {
 
480
                g_debug ("user is root so account type is administrator");
 
481
                return ACCOUNT_TYPE_ADMINISTRATOR;
 
482
        }
 
483
 
 
484
        grp = getgrnam ("sudo");
 
485
        if (grp == NULL) {
 
486
                g_debug ("sudo group not found");
 
487
                return ACCOUNT_TYPE_STANDARD;
 
488
        }
 
489
        sudo = grp->gr_gid;
 
490
 
 
491
        /* Ubuntu prior to 12.04 used "admin" */
 
492
        grp = getgrnam ("admin");
 
493
        if (grp == NULL) {
 
494
                g_debug ("admin group not found");
 
495
                admin = -1;
 
496
        } else {
 
497
                admin = grp->gr_gid;
 
498
        }
 
499
 
 
500
        ngroups = get_user_groups (pwent->pw_name, pwent->pw_gid, &groups);
 
501
 
 
502
        for (i = 0; i < ngroups; i++) {
 
503
                if (groups[i] == sudo || groups[i] == admin) {
 
504
                        g_free (groups);
 
505
                        return ACCOUNT_TYPE_ADMINISTRATOR;
 
506
                }
 
507
        }
 
508
 
 
509
        g_free (groups);
 
510
 
 
511
        return ACCOUNT_TYPE_STANDARD;
 
512
}
 
513
 
 
514
void
 
515
user_local_update_from_pwent (User          *user,
 
516
                              struct passwd *pwent)
 
517
{
 
518
#ifdef HAVE_SHADOW_H
 
519
        struct spwd *spent;
 
520
#endif
 
521
        gchar *real_name;
 
522
        gboolean changed;
 
523
        const gchar *passwd;
 
524
        gboolean locked;
 
525
        PasswordMode mode;
 
526
 
 
527
        g_object_freeze_notify (G_OBJECT (user));
 
528
 
 
529
        changed = FALSE;
 
530
 
 
531
        if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
 
532
                gchar *first_comma = NULL;
 
533
                gchar *valid_utf8_name = NULL;
 
534
 
 
535
                if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
 
536
                        valid_utf8_name = pwent->pw_gecos;
 
537
                        first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
 
538
                }
 
539
                else {
 
540
                        g_warning ("User %s has invalid UTF-8 in GECOS field. "
 
541
                                   "It would be a good thing to check /etc/passwd.",
 
542
                                   pwent->pw_name ? pwent->pw_name : "");
 
543
                }
 
544
 
 
545
                if (first_comma) {
 
546
                        real_name = g_strndup (valid_utf8_name,
 
547
                                                  (first_comma - valid_utf8_name));
 
548
                }
 
549
                else if (valid_utf8_name) {
 
550
                        real_name = g_strdup (valid_utf8_name);
 
551
                }
 
552
                else {
 
553
                        real_name = NULL;
 
554
                }
 
555
 
 
556
                if (real_name && real_name[0] == '\0') {
 
557
                        g_free (real_name);
 
558
                        real_name = NULL;
 
559
                }
 
560
        }
 
561
        else {
 
562
                real_name = NULL;
 
563
        }
 
564
        if (g_strcmp0 (real_name, user->real_name) != 0) {
 
565
                g_free (user->real_name);
 
566
                user->real_name = real_name;
 
567
                changed = TRUE;
 
568
                g_object_notify (G_OBJECT (user), "real-name");
 
569
        }
 
570
        else {
 
571
                g_free (real_name);
 
572
        }
 
573
 
 
574
        /* UID */
 
575
        if (pwent->pw_uid != user->uid) {
 
576
                user->uid = pwent->pw_uid;
 
577
                changed = TRUE;
 
578
                g_object_notify (G_OBJECT (user), "uid");
 
579
        }
 
580
 
 
581
        /* GID */
 
582
        user->gid = pwent->pw_gid;
 
583
 
 
584
        user->account_type = account_type_from_pwent (pwent);
 
585
 
 
586
        /* Username */
 
587
        if (g_strcmp0 (user->user_name, pwent->pw_name) != 0) {
 
588
                g_free (user->user_name);
 
589
                user->user_name = g_strdup (pwent->pw_name);
 
590
                changed = TRUE;
 
591
                g_object_notify (G_OBJECT (user), "user-name");
 
592
        }
 
593
 
 
594
        /* Home Directory */
 
595
        if (g_strcmp0 (user->home_dir, pwent->pw_dir) != 0) {
 
596
                g_free (user->home_dir);
 
597
                user->home_dir = g_strdup (pwent->pw_dir);
 
598
                changed = TRUE;
 
599
                g_object_notify (G_OBJECT (user), "home-directory");
 
600
        }
 
601
 
 
602
        /* Shell */
 
603
        if (g_strcmp0 (user->shell, pwent->pw_shell) != 0) {
 
604
                g_free (user->shell);
 
605
                user->shell = g_strdup (pwent->pw_shell);
 
606
                changed = TRUE;
 
607
                g_object_notify (G_OBJECT (user), "shell");
 
608
        }
 
609
 
 
610
        passwd = pwent->pw_passwd;
 
611
#ifdef HAVE_SHADOW_H
 
612
        spent = getspnam (pwent->pw_name);
 
613
        if (spent)
 
614
                passwd = spent->sp_pwdp;
 
615
#endif
 
616
 
 
617
        if (passwd && passwd[0] == '!') {
 
618
                locked = TRUE;
 
619
        }
 
620
        else {
 
621
                locked = FALSE;
 
622
        }
 
623
 
 
624
        if (user->locked != locked) {
 
625
                user->locked = locked;
 
626
                changed = TRUE;
 
627
                g_object_notify (G_OBJECT (user), "locked");
 
628
        }
 
629
 
 
630
        if (passwd[0] == 0) {
 
631
                mode = PASSWORD_MODE_NONE;
 
632
        }
 
633
        else {
 
634
                mode = PASSWORD_MODE_REGULAR;
 
635
        }
 
636
 
 
637
#ifdef HAVE_SHADOW_H
 
638
        if (spent) {
 
639
                if (spent->sp_lstchg == 0) {
 
640
                        mode = PASSWORD_MODE_SET_AT_LOGIN;
 
641
                }
 
642
        }
 
643
#endif
 
644
 
 
645
        if (user->password_mode != mode) {
 
646
                user->password_mode = mode;
 
647
                changed = TRUE;
 
648
                g_object_notify (G_OBJECT (user), "password-mode");
 
649
        }
 
650
 
 
651
        user->system_account = daemon_local_user_is_excluded (user->daemon,
 
652
                                                              user->user_name,
 
653
                                                              user->uid);
 
654
 
 
655
        g_object_thaw_notify (G_OBJECT (user));
 
656
 
 
657
        if (changed)
 
658
                g_signal_emit (user, signals[CHANGED], 0);
 
659
}
 
660
 
 
661
void
 
662
user_local_update_from_keyfile (User     *user,
 
663
                                GKeyFile *keyfile)
 
664
{
 
665
        gchar *s;
 
666
 
 
667
        g_object_freeze_notify (G_OBJECT (user));
 
668
 
 
669
        s = g_key_file_get_string (keyfile, "User", "Language", NULL);
 
670
        if (s != NULL) {
 
671
                /* TODO: validate / normalize */
 
672
                g_free (user->language);
 
673
                user->language = s;
 
674
        }
 
675
 
 
676
        s = g_key_file_get_string (keyfile, "User", "FormatsLocale", NULL);
 
677
        if (s != NULL) {
 
678
                g_free (user->formats_locale);
 
679
                user->formats_locale = s;
 
680
        }
 
681
 
 
682
        s = g_key_file_get_string (keyfile, "User", "XSession", NULL);
 
683
        if (s != NULL) {
 
684
                g_free (user->x_session);
 
685
                user->x_session = s;
 
686
        }
 
687
 
 
688
        s = g_key_file_get_string (keyfile, "User", "Email", NULL);
 
689
        if (s != NULL) {
 
690
                g_free (user->email);
 
691
                user->email = s;
 
692
        }
 
693
 
 
694
        s = g_key_file_get_string (keyfile, "User", "Location", NULL);
 
695
        if (s != NULL) {
 
696
                g_free (user->location);
 
697
                user->location = s;
 
698
        }
 
699
 
 
700
        s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL);
 
701
        if (s != NULL) {
 
702
                g_free (user->password_hint);
 
703
                user->password_hint = s;
 
704
        }
 
705
 
 
706
        s = g_key_file_get_string (keyfile, "User", "Background", NULL);
 
707
        if (s != NULL) {
 
708
                g_free (user->background_file);
 
709
                user->background_file = s;
 
710
        }
 
711
 
 
712
        s = g_key_file_get_string (keyfile, "User", "Icon", NULL);
 
713
        if (s != NULL) {
 
714
                g_free (user->icon_file);
 
715
                user->icon_file = s;
 
716
        }
 
717
 
 
718
        g_object_thaw_notify (G_OBJECT (user));
 
719
}
 
720
 
 
721
static void
 
722
user_local_save_to_keyfile (User     *user,
 
723
                            GKeyFile *keyfile)
 
724
{
 
725
        if (user->email)
 
726
                g_key_file_set_string (keyfile, "User", "Email", user->email);
 
727
 
 
728
        if (user->language)
 
729
                g_key_file_set_string (keyfile, "User", "Language", user->language);
 
730
 
 
731
        if (user->formats_locale)
 
732
                g_key_file_set_string (keyfile, "User", "FormatsLocale", user->formats_locale);
 
733
 
 
734
        if (user->x_session)
 
735
                g_key_file_set_string (keyfile, "User", "XSession", user->x_session);
 
736
 
 
737
        if (user->location)
 
738
                g_key_file_set_string (keyfile, "User", "Location", user->location);
 
739
 
 
740
        if (user->password_hint)
 
741
                g_key_file_set_string (keyfile, "User", "PasswordHint", user->password_hint);
 
742
 
 
743
        if (user->background_file)
 
744
                g_key_file_set_string (keyfile, "User", "Background", user->background_file);
 
745
 
 
746
        if (user->icon_file)
 
747
                g_key_file_set_string (keyfile, "User", "Icon", user->icon_file);
 
748
}
 
749
 
 
750
static void
 
751
save_extra_data (User *user)
 
752
{
 
753
        gchar *filename;
 
754
        GKeyFile *keyfile;
 
755
        gchar *data;
 
756
        GError *error;
 
757
 
 
758
        keyfile = g_key_file_new ();
 
759
        user_local_save_to_keyfile (user, keyfile);
 
760
 
 
761
        error = NULL;
 
762
        data = g_key_file_to_data (keyfile, NULL, &error);
 
763
        if (error == NULL) {
 
764
                filename = g_build_filename ("/var/lib/AccountsService/users",
 
765
                                             user->user_name,
 
766
                                             NULL);
 
767
                g_file_set_contents (filename, data, -1, &error);
 
768
                g_free (filename);
 
769
        }
 
770
        if (error) {
 
771
                g_warning ("Saving data for user %s failed: %s",
 
772
                           user->user_name, error->message);
 
773
                g_error_free (error);
 
774
        }
 
775
        g_key_file_free (keyfile);
 
776
}
 
777
 
 
778
static void
 
779
move_extra_data (const gchar *old_name,
 
780
                 const gchar *new_name)
 
781
{
 
782
        gchar *old_filename;
 
783
        gchar *new_filename;
 
784
 
 
785
        old_filename = g_build_filename ("/var/lib/AccountsService/users",
 
786
                                         old_name, NULL);
 
787
        new_filename = g_build_filename ("/var/lib/AccountsService/users",
 
788
                                         new_name, NULL);
 
789
 
 
790
        g_rename (old_filename, new_filename);
 
791
 
 
792
        g_free (old_filename);
 
793
        g_free (new_filename);
 
794
}
 
795
 
 
796
static gchar *
 
797
compute_object_path (User *user)
 
798
{
 
799
        gchar *object_path;
 
800
 
 
801
        object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%ld",
 
802
                                       (gint64) user->uid);
 
803
 
 
804
        return object_path;
 
805
}
 
806
 
 
807
void
 
808
user_local_register (User *user)
 
809
{
 
810
        DBusConnection *connection;
 
811
        GError *error = NULL;
 
812
 
 
813
        user->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
 
814
        if (user->system_bus_connection == NULL) {
 
815
                if (error != NULL) {
 
816
                        g_critical ("error getting system bus: %s", error->message);
 
817
                        g_error_free (error);
 
818
                }
 
819
                goto error;
 
820
        }
 
821
 
 
822
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
823
        user->object_path = compute_object_path (user);
 
824
 
 
825
        if (dbus_g_connection_lookup_g_object (user->system_bus_connection, user->object_path) != NULL) {
 
826
                g_critical ("Duplicate object at path %s.", user->object_path);
 
827
                goto error;
 
828
        }
 
829
 
 
830
        dbus_g_connection_register_g_object (user->system_bus_connection,
 
831
                                             user->object_path,
 
832
                                             G_OBJECT (user));
 
833
 
 
834
 error:
 
835
        return;
 
836
}
 
837
 
 
838
void
 
839
user_local_unregister (User *user)
 
840
{
 
841
        dbus_g_connection_unregister_g_object (user->system_bus_connection,
 
842
                                               G_OBJECT (user));
 
843
}
 
844
 
 
845
User *
 
846
user_local_new (Daemon *daemon, uid_t uid)
 
847
{
 
848
        User *user;
 
849
 
 
850
        user = g_object_new (TYPE_USER, NULL);
 
851
        user->daemon = daemon;
 
852
        user->uid = uid;
 
853
 
 
854
        return user;
 
855
}
 
856
 
 
857
const gchar *
 
858
user_local_get_user_name (User *user)
 
859
{
 
860
        return user->user_name;
 
861
}
 
862
 
 
863
const gchar *
 
864
user_local_get_object_path (User *user)
 
865
{
 
866
        return user->object_path;
 
867
}
 
868
 
 
869
uid_t
 
870
user_local_get_uid (User *user)
 
871
{
 
872
        return user->uid;
 
873
}
 
874
 
 
875
static void
 
876
throw_error (DBusGMethodInvocation *context,
 
877
             gint                   error_code,
 
878
             const gchar           *format,
 
879
             ...)
 
880
{
 
881
        GError *error;
 
882
        va_list args;
 
883
        gchar *message;
 
884
 
 
885
        va_start (args, format);
 
886
        message = g_strdup_vprintf (format, args);
 
887
        va_end (args);
 
888
 
 
889
        error = g_error_new (ERROR, error_code, "%s", message);
 
890
        dbus_g_method_return_error (context, error);
 
891
        g_error_free (error);
 
892
 
 
893
        g_free (message);
 
894
}
 
895
 
 
896
static void
 
897
user_change_real_name_authorized_cb (Daemon                *daemon,
 
898
                                     User                  *user,
 
899
                                     DBusGMethodInvocation *context,
 
900
                                     gpointer               data)
 
901
 
 
902
{
 
903
        gchar *name = data;
 
904
        GError *error;
 
905
        gchar *argv[6];
 
906
 
 
907
        if (g_strcmp0 (user->real_name, name) != 0) {
 
908
                sys_log (context,
 
909
                         "change real name of user '%s' (%d) to '%s'",
 
910
                         user->user_name, user->uid, name);
 
911
 
 
912
                argv[0] = "/usr/sbin/usermod";
 
913
                argv[1] = "-c";
 
914
                argv[2] = name;
 
915
                argv[3] = "--";
 
916
                argv[4] = user->user_name;
 
917
                argv[5] = NULL;
 
918
 
 
919
                error = NULL;
 
920
                if (!spawn_with_login_uid (context, argv, &error)) {
 
921
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
922
                        g_error_free (error);
 
923
                        return;
 
924
                }
 
925
 
 
926
                g_free (user->real_name);
 
927
                user->real_name = g_strdup (name);
 
928
 
 
929
                g_signal_emit (user, signals[CHANGED], 0);
 
930
 
 
931
                g_object_notify (G_OBJECT (user), "real-name");
 
932
        }
 
933
 
 
934
        dbus_g_method_return (context);
 
935
}
 
936
 
 
937
gboolean
 
938
user_set_real_name (User                  *user,
 
939
                    const gchar           *real_name,
 
940
                    DBusGMethodInvocation *context)
 
941
{
 
942
        gchar *sender;
 
943
        DBusConnection *connection;
 
944
        DBusError dbus_error;
 
945
        uid_t uid;
 
946
        const gchar *action_id;
 
947
 
 
948
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
949
        sender = dbus_g_method_get_sender (context);
 
950
        dbus_error_init (&dbus_error);
 
951
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
952
        if (dbus_error_is_set (&dbus_error)) {
 
953
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
954
                dbus_error_free (&dbus_error);
 
955
 
 
956
                return TRUE;
 
957
        }
 
958
 
 
959
        if (user->uid == uid)
 
960
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
961
        else
 
962
                action_id = "org.freedesktop.accounts.user-administration";
 
963
 
 
964
        daemon_local_check_auth (user->daemon,
 
965
                                 user,
 
966
                                 action_id,
 
967
                                 TRUE,
 
968
                                 user_change_real_name_authorized_cb,
 
969
                                 context,
 
970
                                 g_strdup (real_name),
 
971
                                 (GDestroyNotify)g_free);
 
972
 
 
973
        return TRUE;
 
974
}
 
975
 
 
976
static void
 
977
user_change_user_name_authorized_cb (Daemon                *daemon,
 
978
                                     User                  *user,
 
979
                                     DBusGMethodInvocation *context,
 
980
                                     gpointer               data)
 
981
 
 
982
{
 
983
        gchar *name = data;
 
984
        gchar *old_name;
 
985
        GError *error;
 
986
        gchar *argv[6];
 
987
 
 
988
        if (g_strcmp0 (user->user_name, name) != 0) {
 
989
                old_name = g_strdup (user->user_name);
 
990
                sys_log (context,
 
991
                         "change name of user '%s' (%d) to '%s'",
 
992
                         old_name, user->uid, name);
 
993
 
 
994
                argv[0] = "/usr/sbin/usermod";
 
995
                argv[1] = "-l";
 
996
                argv[2] = name;
 
997
                argv[3] = "--";
 
998
                argv[4] = user->user_name;
 
999
                argv[5] = NULL;
 
1000
 
 
1001
                error = NULL;
 
1002
                if (!spawn_with_login_uid (context, argv, &error)) {
 
1003
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
1004
                        g_error_free (error);
 
1005
                        return;
 
1006
                }
 
1007
 
 
1008
                g_free (user->user_name);
 
1009
                user->user_name = g_strdup (name);
 
1010
 
 
1011
                move_extra_data (old_name, name);
 
1012
 
 
1013
                g_signal_emit (user, signals[CHANGED], 0);
 
1014
 
 
1015
                g_object_notify (G_OBJECT (user), "user-name");
 
1016
        }
 
1017
 
 
1018
        dbus_g_method_return (context);
 
1019
}
 
1020
 
 
1021
 
 
1022
gboolean
 
1023
user_set_user_name (User                  *user,
 
1024
                    const gchar           *user_name,
 
1025
                    DBusGMethodInvocation *context)
 
1026
{
 
1027
        gchar *sender;
 
1028
        DBusConnection *connection;
 
1029
        DBusError dbus_error;
 
1030
        uid_t uid;
 
1031
 
 
1032
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
1033
        sender = dbus_g_method_get_sender (context);
 
1034
        dbus_error_init (&dbus_error);
 
1035
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
1036
        if (dbus_error_is_set (&dbus_error)) {
 
1037
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
1038
                dbus_error_free (&dbus_error);
 
1039
 
 
1040
                return TRUE;
 
1041
        }
 
1042
 
 
1043
        daemon_local_check_auth (user->daemon,
 
1044
                                 user,
 
1045
                                 "org.freedesktop.accounts.user-administration",
 
1046
                                 TRUE,
 
1047
                                 user_change_user_name_authorized_cb,
 
1048
                                 context,
 
1049
                                 g_strdup (user_name),
 
1050
                                 (GDestroyNotify)g_free);
 
1051
 
 
1052
        return TRUE;
 
1053
}
 
1054
 
 
1055
static void
 
1056
user_change_email_authorized_cb (Daemon                *daemon,
 
1057
                                 User                  *user,
 
1058
                                 DBusGMethodInvocation *context,
 
1059
                                 gpointer               data)
 
1060
 
 
1061
{
 
1062
        gchar *email = data;
 
1063
 
 
1064
        if (g_strcmp0 (user->email, email) != 0) {
 
1065
                g_free (user->email);
 
1066
                user->email = g_strdup (email);
 
1067
 
 
1068
                save_extra_data (user);
 
1069
 
 
1070
                g_signal_emit (user, signals[CHANGED], 0);
 
1071
 
 
1072
                g_object_notify (G_OBJECT (user), "email");
 
1073
        }
 
1074
 
 
1075
        dbus_g_method_return (context);
 
1076
}
 
1077
 
 
1078
 
 
1079
 
 
1080
gboolean
 
1081
user_set_email (User                  *user,
 
1082
                const gchar           *email,
 
1083
                DBusGMethodInvocation *context)
 
1084
{
 
1085
        gchar *sender;
 
1086
        DBusConnection *connection;
 
1087
        DBusError dbus_error;
 
1088
        uid_t uid;
 
1089
        const gchar *action_id;
 
1090
 
 
1091
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
1092
        sender = dbus_g_method_get_sender (context);
 
1093
        dbus_error_init (&dbus_error);
 
1094
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
1095
        if (dbus_error_is_set (&dbus_error)) {
 
1096
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
1097
                dbus_error_free (&dbus_error);
 
1098
 
 
1099
                return TRUE;
 
1100
        }
 
1101
 
 
1102
        if (user->uid == uid)
 
1103
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
1104
        else
 
1105
                action_id = "org.freedesktop.accounts.user-administration";
 
1106
 
 
1107
        daemon_local_check_auth (user->daemon,
 
1108
                                 user,
 
1109
                                 action_id,
 
1110
                                 TRUE,
 
1111
                                 user_change_email_authorized_cb,
 
1112
                                 context,
 
1113
                                 g_strdup (email),
 
1114
                                 (GDestroyNotify)g_free);
 
1115
 
 
1116
        return TRUE;
 
1117
}
 
1118
 
 
1119
static gboolean
 
1120
user_drop_privileges_to_user (User *user)
 
1121
{
 
1122
        if (setresgid (user->gid, user->gid, -1) != 0) {
 
1123
                g_warning ("setresgid() failed");
 
1124
                return FALSE;
 
1125
        }
 
1126
        if (setresuid (user->uid, user->uid, -1) != 0) {
 
1127
                g_warning ("setresuid() failed");
 
1128
                return FALSE;
 
1129
        }
 
1130
        return TRUE;
 
1131
}
 
1132
 
 
1133
static void
 
1134
user_regain_privileges ()
 
1135
{
 
1136
        setresuid (0, 0, -1);
 
1137
        setresgid (0, 0, -1);
 
1138
}
 
1139
 
 
1140
static void
 
1141
user_get_profile_env (User   *user,
 
1142
                      gchar **language,
 
1143
                      gchar **lang,
 
1144
                      gchar **lcmess)
 
1145
{
 
1146
        *language = NULL;
 
1147
        *lang = NULL;
 
1148
        *lcmess = NULL;
 
1149
 
 
1150
        FILE  *fp;
 
1151
        gchar *profile_path = g_build_path ("/", user->home_dir, ".profile", NULL);
 
1152
 
 
1153
        if ((fp = fopen (profile_path, "r"))) {
 
1154
                gchar line[50];
 
1155
                gchar **tokens;
 
1156
                while ((fgets (line, 50, fp)) != NULL) {
 
1157
                        if (g_str_has_prefix (line, "export LANGUAGE=\"")) {
 
1158
                                tokens = g_strsplit (line, "\"", 3);
 
1159
                                *language = g_strdup (tokens[1]);
 
1160
                                g_strfreev (tokens);
 
1161
                        }
 
1162
                        if (g_str_has_prefix (line, "export LANG=\"")) {
 
1163
                                tokens = g_strsplit (line, "\"", 3);
 
1164
                                *lang = g_strdup (tokens[1]);
 
1165
                                g_strfreev (tokens);
 
1166
                        }
 
1167
                        if (g_str_has_prefix (line, "export LC_MESSAGES=\"")) {
 
1168
                                tokens = g_strsplit (line, "\"", 3);
 
1169
                                *lcmess = g_strdup (tokens[1]);
 
1170
                                g_strfreev (tokens);
 
1171
                        }
 
1172
                }
 
1173
                fclose (fp);
 
1174
        }
 
1175
 
 
1176
        g_free (profile_path);
 
1177
}
 
1178
 
 
1179
static gchar *
 
1180
user_locale_utf8_fix (const gchar *locale)
 
1181
{
 
1182
        if (locale == NULL || !g_strrstr (locale, ".utf8"))
 
1183
                return g_strdup (locale);
 
1184
 
 
1185
        gchar **tokens = g_strsplit_set (locale, ".8", 3);
 
1186
        gchar *fixed_locale = g_strconcat (tokens[0], ".UTF-8", tokens[2], NULL);
 
1187
        g_strfreev (tokens);
 
1188
 
 
1189
        return fixed_locale;
 
1190
}
 
1191
 
 
1192
static gchar *
 
1193
user_language_validate (User        *user,
 
1194
                        const gchar *lang)
 
1195
{
 
1196
        gboolean     ret;
 
1197
        const gchar *program = "/usr/share/language-tools/language-validate";
 
1198
        gchar       *command = g_strconcat (program, " ", lang, NULL);
 
1199
        gchar       *validated_language;
 
1200
        GError      *error = NULL;
 
1201
 
 
1202
        if (!user_drop_privileges_to_user (user))
 
1203
                return NULL;
 
1204
        ret = g_spawn_command_line_sync (command, &validated_language, NULL, NULL, &error);
 
1205
        user_regain_privileges ();
 
1206
 
 
1207
        g_free (command);
 
1208
        if (!ret) {
 
1209
                g_warning ("Couldn't get validated language: %s", error->message);
 
1210
                g_error_free (error);
 
1211
                return NULL;
 
1212
        }
 
1213
        return g_strchomp (validated_language);
 
1214
}
 
1215
 
 
1216
static gchar *
 
1217
user_locale_validate (const gchar           *locale,
 
1218
                      DBusGMethodInvocation *context)
 
1219
{
 
1220
        gchar *validated_locale = NULL;
 
1221
        gchar *tmp_locale = NULL;
 
1222
        gchar *current = g_strdup (setlocale (LC_ALL, NULL));
 
1223
 
 
1224
        if (locale == NULL || strlen (locale) < 2)
 
1225
                goto out;
 
1226
        tmp_locale = g_strdup (locale);
 
1227
        g_strchomp (tmp_locale);
 
1228
 
 
1229
        if (!setlocale (LC_ALL, tmp_locale)) {
 
1230
                throw_error (context, ERROR_FAILED, "'%s' is not a valid locale name", tmp_locale);
 
1231
                goto out;
 
1232
        }
 
1233
        validated_locale = user_locale_utf8_fix (tmp_locale);
 
1234
 
 
1235
out:
 
1236
        setlocale (LC_ALL, current);
 
1237
        g_free (tmp_locale);
 
1238
        g_free (current);
 
1239
 
 
1240
        return validated_locale;
 
1241
}
 
1242
 
 
1243
static gchar *
 
1244
user_get_fallback_value (User        *user,
 
1245
                         const gchar *property)
 
1246
{
 
1247
        gchar *fallback_value = NULL;
 
1248
        gchar *system_language = NULL;
 
1249
        gchar *system_lang = NULL;
 
1250
        gchar *system_lctime = NULL;
 
1251
 
 
1252
        gchar *pam_env_path = g_build_path ("/", user->home_dir, ".pam_environment", NULL);
 
1253
        gchar *profile_language = NULL;
 
1254
        gchar *profile_lang = NULL;
 
1255
        gchar *profile_lcmess = NULL;
 
1256
 
 
1257
        /* take ~/.profile into account if not migrated */
 
1258
        if (!g_file_test (pam_env_path, G_FILE_TEST_IS_REGULAR))
 
1259
                user_get_profile_env (user,
 
1260
                                      &profile_language,
 
1261
                                      &profile_lang,
 
1262
                                      &profile_lcmess);
 
1263
 
 
1264
        FILE  *fp;
 
1265
        if ((fp = fopen ("/etc/default/locale", "r"))) {
 
1266
                gchar line[50];
 
1267
                gchar **tokens;
 
1268
                while ((fgets (line, 50, fp)) != NULL) {
 
1269
                        if (g_str_has_prefix (line, "LANGUAGE=\"")) {
 
1270
                                tokens = g_strsplit (line, "\"", 3);
 
1271
                                system_language = g_strdup (tokens[1]);
 
1272
                                g_strfreev (tokens);
 
1273
                        }
 
1274
                        if (g_str_has_prefix (line, "LANG=\"")) {
 
1275
                                tokens = g_strsplit (line, "\"", 3);
 
1276
                                system_lang = g_strdup (tokens[1]);
 
1277
                                g_strfreev (tokens);
 
1278
                        }
 
1279
                        if (g_str_has_prefix (line, "LC_TIME=\"")) {
 
1280
                                tokens = g_strsplit (line, "\"", 3);
 
1281
                                system_lctime = g_strdup (tokens[1]);
 
1282
                                g_strfreev (tokens);
 
1283
                        }
 
1284
                }
 
1285
                fclose (fp);
 
1286
        }
 
1287
 
 
1288
        if (g_strcmp0 (property, "Language") == 0) {
 
1289
                gchar *tmp = NULL;
 
1290
                if (profile_language)
 
1291
                        tmp = profile_language;
 
1292
                else if (system_language)
 
1293
                        tmp = system_language;
 
1294
                else if (profile_lcmess)
 
1295
                        tmp = profile_lcmess;
 
1296
                else if (profile_lang)
 
1297
                        tmp = profile_lang;
 
1298
                else if (system_lang)
 
1299
                        tmp = system_lang;
 
1300
                fallback_value = user_language_validate (user, tmp);
 
1301
        }
 
1302
        
 
1303
        if (g_strcmp0 (property, "FormatsLocale") == 0) {
 
1304
                gchar *tmp = NULL;
 
1305
                if (system_lctime)
 
1306
                        tmp = system_lctime;
 
1307
                else if (profile_lang)
 
1308
                        tmp = profile_lang;
 
1309
                else if (system_lang)
 
1310
                        tmp = system_lang;
 
1311
                fallback_value = user_locale_utf8_fix (tmp);
 
1312
        }
 
1313
 
 
1314
        g_free (system_language);
 
1315
        g_free (system_lang);
 
1316
        g_free (system_lctime);
 
1317
        g_free (pam_env_path);
 
1318
        g_free (profile_language);
 
1319
        g_free (profile_lang);
 
1320
        g_free (profile_lcmess);
 
1321
 
 
1322
        return fallback_value;
 
1323
}
 
1324
 
 
1325
static gchar *
 
1326
user_update_environment (User                  *user,
 
1327
                         gchar                 *data,
 
1328
                         const gchar           *script,
 
1329
                         DBusGMethodInvocation *context)
 
1330
 
 
1331
/* This function updates ~/.pam_environment by means of the help files in /usr/share/language-tools. */
 
1332
{
 
1333
        gchar        *program;
 
1334
        gchar        *validated_data = NULL;
 
1335
        gint          i;
 
1336
        const gchar  *allowed_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._+-:/ @";
 
1337
        gboolean      ret;
 
1338
        GError       *error = NULL;
 
1339
 
 
1340
        program = g_build_path ("/", "/usr/share/language-tools", script, NULL);
 
1341
        gchar *command[] = { program, user->home_dir, data, NULL };
 
1342
 
 
1343
        /* test for odd characters in arguments */
 
1344
        for (i = 1; i <= 2; i++) {
 
1345
                if (strlen (command[i]) != strspn (command[i], allowed_chars)) {
 
1346
                        throw_error (context, ERROR_FAILED, "non-permitted character(s) in argument");
 
1347
                        goto out;
 
1348
                }
 
1349
        }
 
1350
 
 
1351
        /* set applicable environment variables in ~/.pam_environment */
 
1352
        if (!user_drop_privileges_to_user (user))
 
1353
                goto out;
 
1354
        ret = g_spawn_sync ( NULL,
 
1355
                             command,
 
1356
                             NULL,
 
1357
                             G_SPAWN_STDERR_TO_DEV_NULL,
 
1358
                             NULL,
 
1359
                             NULL,
 
1360
                             &validated_data,
 
1361
                             NULL,
 
1362
                             NULL,
 
1363
                             &error );
 
1364
        user_regain_privileges ();
 
1365
        if (!ret) {
 
1366
                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", program, error->message);
 
1367
                g_error_free (error);
 
1368
                validated_data = NULL;
 
1369
                goto out;
 
1370
        }
 
1371
        if (validated_data == NULL || strlen (validated_data) <= 1) {
 
1372
                throw_error (context, ERROR_FAILED, "running '%s' failed: no output", program);
 
1373
                validated_data = NULL;
 
1374
                goto out;
 
1375
        }
 
1376
 
 
1377
out:
 
1378
        g_free (program);
 
1379
        if (validated_data == NULL)
 
1380
                return NULL;
 
1381
 
 
1382
        return g_strchomp (validated_data);
 
1383
}
 
1384
 
 
1385
static gboolean
 
1386
user_migration_from_profile (User                   *user,
 
1387
                             DBusGMethodInvocation  *context,
 
1388
                             gboolean               *is_migrate,
 
1389
                             gchar                 **profile_language,
 
1390
                             gchar                 **profile_formats)
 
1391
{
 
1392
        gchar    *pam_env_path;
 
1393
        gchar    *language = NULL;
 
1394
        gchar    *lang = NULL;
 
1395
        gchar    *lcmess = NULL;
 
1396
        gchar    *tmp;
 
1397
        gchar    *command = NULL;
 
1398
        GError   *error = NULL;
 
1399
        gboolean  ret = FALSE;
 
1400
 
 
1401
        *is_migrate = FALSE;
 
1402
        *profile_language = NULL;
 
1403
        *profile_formats = NULL;
 
1404
 
 
1405
        pam_env_path = g_build_path ("/", user->home_dir, ".pam_environment", NULL);
 
1406
        if (g_file_test (pam_env_path, G_FILE_TEST_IS_REGULAR))
 
1407
                goto out;
 
1408
        else
 
1409
                *is_migrate = TRUE;
 
1410
 
 
1411
        user_get_profile_env (user, &language, &lang, &lcmess);
 
1412
 
 
1413
        tmp = NULL;
 
1414
        if (language)
 
1415
                tmp = language;
 
1416
        else if (lcmess)
 
1417
                tmp = lcmess;
 
1418
        if (tmp) {
 
1419
                *profile_language = user_update_environment (user,
 
1420
                                                             tmp,
 
1421
                                                             "set-language-helper",
 
1422
                                                             context);
 
1423
                if (*profile_language == NULL)
 
1424
                        goto out;
 
1425
        }
 
1426
 
 
1427
        tmp = NULL;
 
1428
        if (lang && lcmess && g_strcmp0 (lang, lcmess) != 0)
 
1429
                tmp = lang;
 
1430
        else if (lang && !lcmess)
 
1431
                tmp = lang;
 
1432
        if (tmp) {
 
1433
                *profile_formats = user_update_environment (user,
 
1434
                                                            user_locale_validate (tmp, context),
 
1435
                                                            "save-to-pam-env",
 
1436
                                                            context);
 
1437
                if (*profile_formats == NULL)
 
1438
                        goto out;
 
1439
        }
 
1440
 
 
1441
        const gchar *program = "/usr/share/language-tools/del-profile-env-settings";
 
1442
        command = g_strconcat (program, " '", user->home_dir, "'", NULL);
 
1443
        if (!user_drop_privileges_to_user (user))
 
1444
                goto out;
 
1445
        gboolean is_success = g_spawn_command_line_sync (command, NULL, NULL, NULL, &error);
 
1446
        user_regain_privileges ();
 
1447
        if (!is_success) {
 
1448
                throw_error (context, ERROR_FAILED, "couldn't edit ~/.profile: %s", error->message);
 
1449
                g_error_free (error);
 
1450
                goto out;
 
1451
        }
 
1452
 
 
1453
        ret = TRUE;
 
1454
 
 
1455
out:
 
1456
        g_free (pam_env_path);
 
1457
        g_free (language);
 
1458
        g_free (lang);
 
1459
        g_free (lcmess);
 
1460
        g_free (command);
 
1461
 
 
1462
        return ret;
 
1463
}
 
1464
 
 
1465
static void
 
1466
user_change_language_authorized_cb (Daemon                *daemon,
 
1467
                                    User                  *user,
 
1468
                                    DBusGMethodInvocation *context,
 
1469
                                    gpointer               data)
 
1470
 
 
1471
{
 
1472
        const gchar *fallback_language = user_get_fallback_value (user, "Language");
 
1473
        gboolean     is_language_changed = (user->language && g_strcmp0 (user->language, data) != 0)
 
1474
                         || (!user->language && g_strcmp0 (data, fallback_language) != 0);
 
1475
 
 
1476
        gboolean     is_migrate;
 
1477
        gchar       *dummy;
 
1478
        gchar       *profile_formats;
 
1479
 
 
1480
        gchar *profile_path = g_build_path ("/", user->home_dir, ".profile", NULL);
 
1481
        if (!g_file_test (profile_path, G_FILE_TEST_IS_REGULAR)) {
 
1482
 
 
1483
                /* SetLanguage was probably called from a login greeter,
 
1484
                   and HOME not mounted and/or not decrypted.
 
1485
                   Hence don't save anything, or else accountsservice
 
1486
                   and ~/.pam_environment would become out of sync. */
 
1487
                throw_error (context, ERROR_FAILED, "not access to HOME yet so language not saved");
 
1488
                goto out2;
 
1489
        }
 
1490
 
 
1491
        gboolean is_success = user_migration_from_profile (user,
 
1492
                                                           context,
 
1493
                                                           &is_migrate,
 
1494
                                                           &dummy,
 
1495
                                                           &profile_formats);
 
1496
        if (is_migrate && !is_success)
 
1497
                goto out2;
 
1498
 
 
1499
        if (is_language_changed || is_migrate) {
 
1500
                gchar *language = user_update_environment (user,
 
1501
                                                           data,
 
1502
                                                           "set-language-helper",
 
1503
                                                           context);
 
1504
                if (language != NULL) {
 
1505
                        g_free (user->language);
 
1506
                        user->language = g_strdup (language);
 
1507
                }
 
1508
 
 
1509
                gchar *locale = NULL;
 
1510
 
 
1511
                if (profile_formats)
 
1512
                        locale = profile_formats;
 
1513
                else if (!user->formats_locale && is_language_changed) {
 
1514
 
 
1515
                        /* set the user formats (certain LC_* variables) explicitly
 
1516
                           in order to prevent surprises when LANG is changed */
 
1517
                        FILE *fp;
 
1518
                        if ((fp = fopen ("/etc/default/locale", "r"))) {
 
1519
                                gchar line[50];
 
1520
                                while ((fgets (line, 50, fp)) != NULL) {
 
1521
                                        if (g_str_has_prefix (line, "LC_TIME=\"")) {
 
1522
                                                gchar **tokens = g_strsplit (line, "\"", 3);
 
1523
                                                locale = g_strdup (tokens[1]);
 
1524
                                                g_strfreev (tokens);
 
1525
                                                break;
 
1526
                                        }
 
1527
                                }
 
1528
                                fclose (fp);
 
1529
                        }
 
1530
                        if (locale == NULL) {
 
1531
                                GError *error = NULL;
 
1532
                                const gchar *program = "/usr/share/language-tools/language2locale";
 
1533
                                if (!user_drop_privileges_to_user (user))
 
1534
                                        goto out1;
 
1535
                                gchar *command = g_strconcat (program, " ", fallback_language, NULL);
 
1536
                                gboolean ret = g_spawn_command_line_sync (command, &locale, NULL, NULL, &error);
 
1537
                                user_regain_privileges ();
 
1538
                                if (!ret) {
 
1539
                                        throw_error (context, ERROR_FAILED,
 
1540
                                            "language-to-locale conversion failed: %s", error->message);
 
1541
                                        g_error_free (error);
 
1542
                                        locale = NULL;
 
1543
                                }
 
1544
                                g_free (command);
 
1545
                        }
 
1546
                }
 
1547
 
 
1548
                if (locale != NULL && strlen (locale) > 0) {
 
1549
                        gchar *formats_locale = user_update_environment (user,
 
1550
                                                                         user_locale_validate (locale, context),
 
1551
                                                                         "save-to-pam-env",
 
1552
                                                                         context);
 
1553
                        if (formats_locale != NULL) {
 
1554
                                g_free (user->formats_locale);
 
1555
                                user->formats_locale = g_strdup (formats_locale);
 
1556
                        }
 
1557
                }
 
1558
 
 
1559
out1:
 
1560
                save_extra_data (user);
 
1561
 
 
1562
                g_signal_emit (user, signals[CHANGED], 0);
 
1563
 
 
1564
                g_object_notify (G_OBJECT (user), "language");
 
1565
        }
 
1566
 
 
1567
out2:
 
1568
        g_free (profile_path);
 
1569
        dbus_g_method_return (context);
 
1570
}
 
1571
 
 
1572
 
 
1573
 
 
1574
gboolean
 
1575
user_set_language (User                  *user,
 
1576
                   const gchar           *language,
 
1577
                   DBusGMethodInvocation *context)
 
1578
{
 
1579
        gchar *sender;
 
1580
        DBusConnection *connection;
 
1581
        DBusError dbus_error;
 
1582
        uid_t uid;
 
1583
        const gchar *action_id;
 
1584
 
 
1585
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
1586
        sender = dbus_g_method_get_sender (context);
 
1587
        dbus_error_init (&dbus_error);
 
1588
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
1589
        if (dbus_error_is_set (&dbus_error)) {
 
1590
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
1591
                dbus_error_free (&dbus_error);
 
1592
 
 
1593
                return TRUE;
 
1594
        }
 
1595
 
 
1596
        if (user->uid == uid)
 
1597
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
1598
        else
 
1599
                action_id = "org.freedesktop.accounts.user-administration";
 
1600
 
 
1601
        daemon_local_check_auth (user->daemon,
 
1602
                                 user,
 
1603
                                 action_id,
 
1604
                                 TRUE,
 
1605
                                 user_change_language_authorized_cb,
 
1606
                                 context,
 
1607
                                 g_strdup (language),
 
1608
                                 (GDestroyNotify)g_free);
 
1609
 
 
1610
        return TRUE;
 
1611
}
 
1612
 
 
1613
static void
 
1614
user_change_formats_locale_authorized_cb (Daemon                *daemon,
 
1615
                                          User                  *user,
 
1616
                                          DBusGMethodInvocation *context,
 
1617
                                          gpointer               data)
 
1618
 
 
1619
{
 
1620
        gboolean  is_migrate;
 
1621
        gchar    *profile_language;
 
1622
        gchar    *dummy;
 
1623
 
 
1624
        gboolean is_success = user_migration_from_profile (user,
 
1625
                                                           context,
 
1626
                                                           &is_migrate,
 
1627
                                                           &profile_language,
 
1628
                                                           &dummy);
 
1629
        if (is_migrate && !is_success)
 
1630
                goto out;
 
1631
 
 
1632
        if (g_strcmp0 (user->formats_locale, data) != 0 || is_migrate) {
 
1633
                gchar *formats_locale = user_update_environment (user,
 
1634
                                                                 user_locale_validate (data, context),
 
1635
                                                                 "save-to-pam-env",
 
1636
                                                                 context);
 
1637
                if (formats_locale != NULL) {
 
1638
                        g_free (user->formats_locale);
 
1639
                        user->formats_locale = g_strdup (formats_locale);
 
1640
                }
 
1641
 
 
1642
                if (profile_language != NULL) {
 
1643
                        gchar *language = user_update_environment (user,
 
1644
                                                                   profile_language,
 
1645
                                                                   "set-language-helper",
 
1646
                                                                   context);
 
1647
                        if (language != NULL) {
 
1648
                                g_free (user->language);
 
1649
                                user->language = g_strdup (language);
 
1650
                        }
 
1651
                }
 
1652
 
 
1653
                save_extra_data (user);
 
1654
 
 
1655
                g_signal_emit (user, signals[CHANGED], 0);
 
1656
 
 
1657
                g_object_notify (G_OBJECT (user), "formats_locale");
 
1658
        }
 
1659
 
 
1660
out:
 
1661
        dbus_g_method_return (context);
 
1662
}
 
1663
 
 
1664
gboolean
 
1665
user_set_formats_locale (User                  *user,
 
1666
                         const gchar           *formats_locale,
 
1667
                         DBusGMethodInvocation *context)
 
1668
{
 
1669
        gchar *sender;
 
1670
        DBusConnection *connection;
 
1671
        DBusError dbus_error;
 
1672
        uid_t uid;
 
1673
        const gchar *action_id;
 
1674
 
 
1675
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
1676
        sender = dbus_g_method_get_sender (context);
 
1677
        dbus_error_init (&dbus_error);
 
1678
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
1679
        if (dbus_error_is_set (&dbus_error)) {
 
1680
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
1681
                dbus_error_free (&dbus_error);
 
1682
 
 
1683
                return TRUE;
 
1684
        }
 
1685
 
 
1686
        if (user->uid == uid)
 
1687
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
1688
        else
 
1689
                action_id = "org.freedesktop.accounts.user-administration";
 
1690
 
 
1691
        daemon_local_check_auth (user->daemon,
 
1692
                                 user,
 
1693
                                 action_id,
 
1694
                                 TRUE,
 
1695
                                 user_change_formats_locale_authorized_cb,
 
1696
                                 context,
 
1697
                                 g_strdup (formats_locale),
 
1698
                                 (GDestroyNotify) g_free);
 
1699
 
 
1700
        return TRUE;
 
1701
}
 
1702
 
 
1703
static void
 
1704
user_change_x_session_authorized_cb (Daemon                *daemon,
 
1705
                                     User                  *user,
 
1706
                                     DBusGMethodInvocation *context,
 
1707
                                     gpointer               data)
 
1708
 
 
1709
{
 
1710
        gchar *x_session = data;
 
1711
 
 
1712
        if (g_strcmp0 (user->x_session, x_session) != 0) {
 
1713
                g_free (user->x_session);
 
1714
                user->x_session = g_strdup (x_session);
 
1715
 
 
1716
                save_extra_data (user);
 
1717
 
 
1718
                g_signal_emit (user, signals[CHANGED], 0);
 
1719
 
 
1720
                g_object_notify (G_OBJECT (user), "x-session");
 
1721
        }
 
1722
 
 
1723
        dbus_g_method_return (context);
 
1724
}
 
1725
 
 
1726
gboolean
 
1727
user_set_x_session (User                  *user,
 
1728
                    const gchar           *x_session,
 
1729
                    DBusGMethodInvocation *context)
 
1730
{
 
1731
        gchar *sender;
 
1732
        DBusConnection *connection;
 
1733
        DBusError dbus_error;
 
1734
        uid_t uid;
 
1735
        const gchar *action_id;
 
1736
 
 
1737
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
1738
        sender = dbus_g_method_get_sender (context);
 
1739
        dbus_error_init (&dbus_error);
 
1740
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
1741
        if (dbus_error_is_set (&dbus_error)) {
 
1742
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
1743
                dbus_error_free (&dbus_error);
 
1744
 
 
1745
                return TRUE;
 
1746
        }
 
1747
 
 
1748
        if (user->uid == uid)
 
1749
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
1750
        else
 
1751
                action_id = "org.freedesktop.accounts.user-administration";
 
1752
 
 
1753
        daemon_local_check_auth (user->daemon,
 
1754
                                 user,
 
1755
                                 action_id,
 
1756
                                 TRUE,
 
1757
                                 user_change_x_session_authorized_cb,
 
1758
                                 context,
 
1759
                                 g_strdup (x_session),
 
1760
                                 (GDestroyNotify) g_free);
 
1761
 
 
1762
        return TRUE;
 
1763
}
 
1764
 
 
1765
static void
 
1766
user_change_location_authorized_cb (Daemon                *daemon,
 
1767
                                    User                  *user,
 
1768
                                    DBusGMethodInvocation *context,
 
1769
                                    gpointer               data)
 
1770
 
 
1771
{
 
1772
        gchar *location = data;
 
1773
 
 
1774
        if (g_strcmp0 (user->location, location) != 0) {
 
1775
                g_free (user->location);
 
1776
                user->location = g_strdup (location);
 
1777
 
 
1778
                save_extra_data (user);
 
1779
 
 
1780
                g_signal_emit (user, signals[CHANGED], 0);
 
1781
 
 
1782
                g_object_notify (G_OBJECT (user), "location");
 
1783
        }
 
1784
 
 
1785
        dbus_g_method_return (context);
 
1786
}
 
1787
 
 
1788
gboolean
 
1789
user_set_location (User                  *user,
 
1790
                   const gchar           *location,
 
1791
                   DBusGMethodInvocation *context)
 
1792
{
 
1793
        gchar *sender;
 
1794
        DBusConnection *connection;
 
1795
        DBusError dbus_error;
 
1796
        uid_t uid;
 
1797
        const gchar *action_id;
 
1798
 
 
1799
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
1800
        sender = dbus_g_method_get_sender (context);
 
1801
        dbus_error_init (&dbus_error);
 
1802
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
1803
        if (dbus_error_is_set (&dbus_error)) {
 
1804
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
1805
                dbus_error_free (&dbus_error);
 
1806
 
 
1807
                return TRUE;
 
1808
        }
 
1809
 
 
1810
        if (user->uid == uid)
 
1811
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
1812
        else
 
1813
                action_id = "org.freedesktop.accounts.user-administration";
 
1814
 
 
1815
        daemon_local_check_auth (user->daemon,
 
1816
                                 user,
 
1817
                                 action_id,
 
1818
                                 TRUE,
 
1819
                                 user_change_location_authorized_cb,
 
1820
                                 context,
 
1821
                                 g_strdup (location),
 
1822
                                 (GDestroyNotify)g_free);
 
1823
 
 
1824
        return TRUE;
 
1825
}
 
1826
 
 
1827
static void
 
1828
user_change_home_dir_authorized_cb (Daemon                *daemon,
 
1829
                                    User                  *user,
 
1830
                                    DBusGMethodInvocation *context,
 
1831
                                    gpointer               data)
 
1832
 
 
1833
{
 
1834
        gchar *home_dir = data;
 
1835
        GError *error;
 
1836
        gchar *argv[7];
 
1837
 
 
1838
        if (g_strcmp0 (user->home_dir, home_dir) != 0) {
 
1839
                sys_log (context,
 
1840
                         "change home directory of user '%s' (%d) to '%s'",
 
1841
                         user->user_name, user->uid, home_dir);
 
1842
 
 
1843
                argv[0] = "/usr/sbin/usermod";
 
1844
                argv[1] = "-m";
 
1845
                argv[2] = "-d";
 
1846
                argv[3] = home_dir;
 
1847
                argv[4] = "--";
 
1848
                argv[5] = user->user_name;
 
1849
                argv[6] = NULL;
 
1850
 
 
1851
                error = NULL;
 
1852
                if (!spawn_with_login_uid (context, argv, &error)) {
 
1853
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
1854
                        g_error_free (error);
 
1855
                        return;
 
1856
                }
 
1857
 
 
1858
                g_free (user->home_dir);
 
1859
                user->home_dir = g_strdup (home_dir);
 
1860
 
 
1861
                g_signal_emit (user, signals[CHANGED], 0);
 
1862
 
 
1863
                g_object_notify (G_OBJECT (user), "home-directory");
 
1864
        }
 
1865
 
 
1866
        dbus_g_method_return (context);
 
1867
}
 
1868
 
 
1869
gboolean
 
1870
user_set_home_directory (User                  *user,
 
1871
                         const gchar           *home_dir,
 
1872
                         DBusGMethodInvocation *context)
 
1873
{
 
1874
        daemon_local_check_auth (user->daemon,
 
1875
                                 user,
 
1876
                                 "org.freedesktop.accounts.user-administration",
 
1877
                                 TRUE,
 
1878
                                 user_change_home_dir_authorized_cb,
 
1879
                                 context,
 
1880
                                 g_strdup (home_dir),
 
1881
                                 (GDestroyNotify)g_free);
 
1882
 
 
1883
        return TRUE;
 
1884
}
 
1885
 
 
1886
static void
 
1887
user_change_shell_authorized_cb (Daemon                *daemon,
 
1888
                                 User                  *user,
 
1889
                                 DBusGMethodInvocation *context,
 
1890
                                 gpointer               data)
 
1891
 
 
1892
{
 
1893
        gchar *shell = data;
 
1894
        GError *error;
 
1895
        gchar *argv[6];
 
1896
 
 
1897
        if (g_strcmp0 (user->shell, shell) != 0) {
 
1898
                sys_log (context,
 
1899
                         "change shell of user '%s' (%d) to '%s'",
 
1900
                         user->user_name, user->uid, shell);
 
1901
 
 
1902
                argv[0] = "/usr/sbin/usermod";
 
1903
                argv[1] = "-s";
 
1904
                argv[2] = shell;
 
1905
                argv[3] = "--";
 
1906
                argv[4] = user->user_name;
 
1907
                argv[5] = NULL;
 
1908
 
 
1909
                error = NULL;
 
1910
                if (!spawn_with_login_uid (context, argv, &error)) {
 
1911
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
1912
                        g_error_free (error);
 
1913
                        return;
 
1914
                }
 
1915
 
 
1916
                g_free (user->shell);
 
1917
                user->shell = g_strdup (shell);
 
1918
 
 
1919
                g_signal_emit (user, signals[CHANGED], 0);
 
1920
 
 
1921
                g_object_notify (G_OBJECT (user), "shell");
 
1922
        }
 
1923
 
 
1924
        dbus_g_method_return (context);
 
1925
}
 
1926
 
 
1927
gboolean
 
1928
user_set_shell (User                  *user,
 
1929
                const gchar           *shell,
 
1930
                DBusGMethodInvocation *context)
 
1931
{
 
1932
        daemon_local_check_auth (user->daemon,
 
1933
                                 user,
 
1934
                                 "org.freedesktop.accounts.user-administration",
 
1935
                                 TRUE,
 
1936
                                 user_change_shell_authorized_cb,
 
1937
                                 context,
 
1938
                                 g_strdup (shell),
 
1939
                                 (GDestroyNotify)g_free);
 
1940
 
 
1941
        return TRUE;
 
1942
}
 
1943
 
 
1944
static void
 
1945
become_user (gpointer data)
 
1946
{
 
1947
        struct passwd *pw = data;
 
1948
 
 
1949
        if (pw == NULL ||
 
1950
            initgroups (pw->pw_name, pw->pw_gid) != 0 ||
 
1951
            setgid (pw->pw_gid) != 0 ||
 
1952
            setuid (pw->pw_uid) != 0) {
 
1953
                exit (1);
 
1954
        }
 
1955
}
 
1956
 
 
1957
static void
 
1958
user_change_background_file_authorized_cb (Daemon                *daemon,
 
1959
                                           User                  *user,
 
1960
                                           DBusGMethodInvocation *context,
 
1961
                                           gpointer               data)
 
1962
{
 
1963
        gchar *filename;
 
1964
        GFile *file;
 
1965
        GFileInfo *info;
 
1966
        GFileType type;
 
1967
 
 
1968
        filename = g_strdup (data);
 
1969
 
 
1970
        if (filename == NULL ||
 
1971
            *filename == '\0') {
 
1972
                g_free (filename);
 
1973
                filename = NULL;
 
1974
 
 
1975
                goto background_saved;
 
1976
        }
 
1977
 
 
1978
        file = g_file_new_for_path (filename);
 
1979
        info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE,
 
1980
                                  0, NULL, NULL);
 
1981
        type = g_file_info_get_file_type (info);
 
1982
 
 
1983
        g_object_unref (info);
 
1984
        g_object_unref (file);
 
1985
 
 
1986
        if (type != G_FILE_TYPE_REGULAR) {
 
1987
                g_debug ("not a regular file\n");
 
1988
                throw_error (context, ERROR_FAILED, "file '%s' is not a regular file", filename);
 
1989
                g_free (filename);
 
1990
                return;
 
1991
        }
 
1992
 
 
1993
background_saved:
 
1994
        g_free (user->background_file);
 
1995
        user->background_file = filename;
 
1996
 
 
1997
        save_extra_data (user);
 
1998
 
 
1999
        g_signal_emit (user, signals[CHANGED], 0);
 
2000
 
 
2001
        g_object_notify (G_OBJECT (user), "background-file");
 
2002
 
 
2003
        dbus_g_method_return (context);
 
2004
}
 
2005
 
 
2006
gboolean
 
2007
user_set_background_file (User                  *user,
 
2008
                         const gchar           *filename,
 
2009
                         DBusGMethodInvocation *context)
 
2010
{
 
2011
        gchar *sender;
 
2012
        DBusConnection *connection;
 
2013
        DBusError dbus_error;
 
2014
        uid_t uid;
 
2015
        const gchar *action_id;
 
2016
 
 
2017
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
2018
        sender = dbus_g_method_get_sender (context);
 
2019
        dbus_error_init (&dbus_error);
 
2020
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
2021
        if (dbus_error_is_set (&dbus_error)) {
 
2022
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
2023
                dbus_error_free (&dbus_error);
 
2024
 
 
2025
                return TRUE;
 
2026
        }
 
2027
 
 
2028
        if (user->uid == uid)
 
2029
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
2030
        else
 
2031
                action_id = "org.freedesktop.accounts.user-administration";
 
2032
 
 
2033
        daemon_local_check_auth (user->daemon,
 
2034
                                 user,
 
2035
                                 action_id,
 
2036
                                 TRUE,
 
2037
                                 user_change_background_file_authorized_cb,
 
2038
                                 context,
 
2039
                                 g_strdup (filename),
 
2040
                                 (GDestroyNotify)g_free);
 
2041
 
 
2042
        return TRUE;
 
2043
}
 
2044
 
 
2045
static void
 
2046
user_change_icon_file_authorized_cb (Daemon                *daemon,
 
2047
                                     User                  *user,
 
2048
                                     DBusGMethodInvocation *context,
 
2049
                                     gpointer               data)
 
2050
 
 
2051
{
 
2052
        gchar *filename;
 
2053
        GFile *file;
 
2054
        GFileInfo *info;
 
2055
        guint32 mode;
 
2056
        GFileType type;
 
2057
        guint64 size;
 
2058
 
 
2059
        filename = g_strdup (data);
 
2060
 
 
2061
        if (filename == NULL ||
 
2062
            *filename == '\0') {
 
2063
                char *dest_path;
 
2064
                GFile *dest;
 
2065
                GError *error;
 
2066
 
 
2067
                g_free (filename);
 
2068
                filename = NULL;
 
2069
 
 
2070
                dest_path = g_build_filename (ICONDIR, user->user_name, NULL);
 
2071
                dest = g_file_new_for_path (dest_path);
 
2072
                g_free (dest_path);
 
2073
 
 
2074
                error = NULL;
 
2075
                if (!g_file_delete (dest, NULL, &error)) {
 
2076
                        g_object_unref (dest);
 
2077
                        throw_error (context, ERROR_FAILED, "failed to remove user icon, %s", error->message);
 
2078
                        g_error_free (error);
 
2079
                        return;
 
2080
                }
 
2081
                g_object_unref (dest);
 
2082
                goto icon_saved;
 
2083
        }
 
2084
 
 
2085
        file = g_file_new_for_path (filename);
 
2086
        info = g_file_query_info (file, G_FILE_ATTRIBUTE_UNIX_MODE ","
 
2087
                                        G_FILE_ATTRIBUTE_STANDARD_TYPE ","
 
2088
                                        G_FILE_ATTRIBUTE_STANDARD_SIZE,
 
2089
                                  0, NULL, NULL);
 
2090
        mode = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
 
2091
        type = g_file_info_get_file_type (info);
 
2092
        size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
 
2093
 
 
2094
        g_object_unref (info);
 
2095
        g_object_unref (file);
 
2096
 
 
2097
        if (type != G_FILE_TYPE_REGULAR) {
 
2098
                g_debug ("not a regular file\n");
 
2099
                throw_error (context, ERROR_FAILED, "file '%s' is not a regular file", filename);
 
2100
                g_free (filename);
 
2101
                return;
 
2102
        }
 
2103
 
 
2104
        if (size > 1048576) {
 
2105
                g_debug ("file too large\n");
 
2106
                /* 1MB ought to be enough for everybody */
 
2107
                throw_error (context, ERROR_FAILED, "file '%s' is too large to be used as an icon", filename);
 
2108
                g_free (filename);
 
2109
                return;
 
2110
        }
 
2111
 
 
2112
        if ((mode & S_IROTH) == 0 ||
 
2113
            (!g_str_has_prefix (filename, DATADIR) &&
 
2114
             !g_str_has_prefix (filename, ICONDIR))) {
 
2115
                gchar *dest_path;
 
2116
                GFile *dest;
 
2117
                gchar *argv[3];
 
2118
                gint std_out;
 
2119
                GError *error;
 
2120
                GInputStream *input;
 
2121
                GOutputStream *output;
 
2122
                gint uid;
 
2123
                gssize bytes;
 
2124
                struct passwd *pw;
 
2125
 
 
2126
                if (!get_caller_uid (context, &uid)) {
 
2127
                        throw_error (context, ERROR_FAILED, "failed to copy file, could not determine caller UID");
 
2128
                        g_free (filename);
 
2129
                        return;
 
2130
                }
 
2131
 
 
2132
                dest_path = g_build_filename (ICONDIR, user->user_name, NULL);
 
2133
                dest = g_file_new_for_path (dest_path);
 
2134
 
 
2135
                error = NULL;
 
2136
                output = G_OUTPUT_STREAM (g_file_replace (dest, NULL, FALSE, 0, NULL, &error));
 
2137
                if (!output) {
 
2138
                        throw_error (context, ERROR_FAILED, "creating file '%s' failed: %s", dest_path, error->message);
 
2139
                        g_error_free (error);
 
2140
                        g_free (filename);
 
2141
                        g_free (dest_path);
 
2142
                        g_object_unref (dest);
 
2143
                        return;
 
2144
                }
 
2145
 
 
2146
                argv[0] = "/bin/cat";
 
2147
                argv[1] = filename;
 
2148
                argv[2] = NULL;
 
2149
 
 
2150
                pw = getpwuid (uid);
 
2151
 
 
2152
                error = NULL;
 
2153
                if (!g_spawn_async_with_pipes (NULL, argv, NULL, 0, become_user, pw, NULL, NULL, &std_out, NULL, &error)) {
 
2154
                        throw_error (context, ERROR_FAILED, "reading file '%s' failed: %s", filename, error->message);
 
2155
                        g_error_free (error);
 
2156
                        g_free (filename);
 
2157
                        g_free (dest_path);
 
2158
                        g_object_unref (dest);
 
2159
                        return;
 
2160
                }
 
2161
 
 
2162
                input = g_unix_input_stream_new (std_out, FALSE);
 
2163
 
 
2164
                error = NULL;
 
2165
                bytes = g_output_stream_splice (output, input, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
 
2166
                if (bytes < 0 || bytes != size) {
 
2167
                        throw_error (context, ERROR_FAILED, "copying file '%s' to '%s' failed: %s", filename, dest_path, error ? error->message : "unknown reason");
 
2168
                        if (error)
 
2169
                                g_error_free (error);
 
2170
 
 
2171
                        g_file_delete (dest, NULL, NULL);
 
2172
 
 
2173
                        g_free (filename);
 
2174
                        g_free (dest_path);
 
2175
                        g_object_unref (dest);
 
2176
                        g_object_unref (input);
 
2177
                        g_object_unref (output);
 
2178
                        return;
 
2179
                }
 
2180
 
 
2181
                g_object_unref (dest);
 
2182
                g_object_unref (input);
 
2183
                g_object_unref (output);
 
2184
 
 
2185
                g_free (filename);
 
2186
                filename = dest_path;
 
2187
        }
 
2188
 
 
2189
icon_saved:
 
2190
        g_free (user->icon_file);
 
2191
        user->icon_file = filename;
 
2192
 
 
2193
        save_extra_data (user);
 
2194
 
 
2195
        g_signal_emit (user, signals[CHANGED], 0);
 
2196
 
 
2197
        g_object_notify (G_OBJECT (user), "icon-file");
 
2198
 
 
2199
        dbus_g_method_return (context);
 
2200
}
 
2201
 
 
2202
gboolean
 
2203
user_set_icon_file (User                  *user,
 
2204
                    const gchar           *filename,
 
2205
                    DBusGMethodInvocation *context)
 
2206
{
 
2207
        gchar *sender;
 
2208
        DBusConnection *connection;
 
2209
        DBusError dbus_error;
 
2210
        uid_t uid;
 
2211
        const gchar *action_id;
 
2212
 
 
2213
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
2214
        sender = dbus_g_method_get_sender (context);
 
2215
        dbus_error_init (&dbus_error);
 
2216
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
2217
        if (dbus_error_is_set (&dbus_error)) {
 
2218
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
2219
                dbus_error_free (&dbus_error);
 
2220
 
 
2221
                return TRUE;
 
2222
        }
 
2223
 
 
2224
        if (user->uid == uid)
 
2225
                action_id = "org.freedesktop.accounts.change-own-user-data";
 
2226
        else
 
2227
                action_id = "org.freedesktop.accounts.user-administration";
 
2228
 
 
2229
        daemon_local_check_auth (user->daemon,
 
2230
                                 user,
 
2231
                                 action_id,
 
2232
                                 TRUE,
 
2233
                                 user_change_icon_file_authorized_cb,
 
2234
                                 context,
 
2235
                                 g_strdup (filename),
 
2236
                                 (GDestroyNotify)g_free);
 
2237
 
 
2238
        return TRUE;
 
2239
}
 
2240
 
 
2241
static void
 
2242
user_change_locked_authorized_cb (Daemon                *daemon,
 
2243
                                  User                  *user,
 
2244
                                  DBusGMethodInvocation *context,
 
2245
                                  gpointer               data)
 
2246
 
 
2247
{
 
2248
        gboolean locked = GPOINTER_TO_INT (data);
 
2249
        GError *error;
 
2250
        gchar *argv[5];
 
2251
 
 
2252
        if (user->locked != locked) {
 
2253
                sys_log (context,
 
2254
                         "%s account of user '%s' (%d)",
 
2255
                         locked ? "locking" : "unlocking", user->user_name, user->uid);
 
2256
                argv[0] = "/usr/sbin/usermod";
 
2257
                argv[1] = locked ? "-L" : "-U";
 
2258
                argv[2] = "--";
 
2259
                argv[3] = user->user_name;
 
2260
                argv[4] = NULL;
 
2261
 
 
2262
                error = NULL;
 
2263
                if (!spawn_with_login_uid (context, argv, &error)) {
 
2264
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2265
                        g_error_free (error);
 
2266
                        return;
 
2267
                }
 
2268
 
 
2269
                user->locked = locked;
 
2270
 
 
2271
                g_signal_emit (user, signals[CHANGED], 0);
 
2272
 
 
2273
                g_object_notify (G_OBJECT (user), "locked");
 
2274
        }
 
2275
 
 
2276
        dbus_g_method_return (context);
 
2277
}
 
2278
 
 
2279
gboolean
 
2280
user_set_locked (User                  *user,
 
2281
                 gboolean               locked,
 
2282
                 DBusGMethodInvocation *context)
 
2283
{
 
2284
        daemon_local_check_auth (user->daemon,
 
2285
                                 user,
 
2286
                                 "org.freedesktop.accounts.user-administration",
 
2287
                                 TRUE,
 
2288
                                 user_change_locked_authorized_cb,
 
2289
                                 context,
 
2290
                                 GINT_TO_POINTER (locked),
 
2291
                                 NULL);
 
2292
 
 
2293
        return TRUE;
 
2294
}
 
2295
 
 
2296
static void
 
2297
user_change_account_type_authorized_cb (Daemon                *daemon,
 
2298
                                        User                  *user,
 
2299
                                        DBusGMethodInvocation *context,
 
2300
                                        gpointer               data)
 
2301
 
 
2302
{
 
2303
        gint account_type = GPOINTER_TO_INT (data);
 
2304
        GError *error;
 
2305
        gid_t *groups;
 
2306
        gint ngroups;
 
2307
        GString *str;
 
2308
        gid_t admin;
 
2309
        struct group *grp;
 
2310
        gint i;
 
2311
        gchar *argv[6];
 
2312
 
 
2313
        if (user->account_type != account_type) {
 
2314
                sys_log (context,
 
2315
                         "change account type of user '%s' (%d) to %d",
 
2316
                         user->user_name, user->uid, account_type);
 
2317
 
 
2318
                grp = getgrnam ("sudo");
 
2319
                if (grp == NULL) {
 
2320
                        throw_error (context, ERROR_FAILED, "failed to set account type: sudo group not found");
 
2321
                        return;
 
2322
                }
 
2323
                admin = grp->gr_gid;
 
2324
 
 
2325
                ngroups = get_user_groups (user->user_name, user->gid, &groups);
 
2326
 
 
2327
                str = g_string_new ("");
 
2328
                for (i = 0; i < ngroups; i++) {
 
2329
                        if (groups[i] == admin)
 
2330
                                continue;
 
2331
                        g_string_append_printf (str, "%d,", groups[i]);
 
2332
                }
 
2333
                switch (account_type) {
 
2334
                case ACCOUNT_TYPE_ADMINISTRATOR:
 
2335
                        g_string_append_printf (str, "%d", admin);
 
2336
                        break;
 
2337
                default:
 
2338
                        /* remove excess comma */
 
2339
                        g_string_truncate (str, str->len - 1);
 
2340
                }
 
2341
 
 
2342
                g_free (groups);
 
2343
 
 
2344
                argv[0] = "/usr/sbin/usermod";
 
2345
                argv[1] = "-G";
 
2346
                argv[2] = str->str;
 
2347
                argv[3] = "--";
 
2348
                argv[4] = user->user_name;
 
2349
                argv[5] = NULL;
 
2350
 
 
2351
                g_string_free (str, FALSE);
 
2352
 
 
2353
                error = NULL;
 
2354
                if (!spawn_with_login_uid (context, argv, &error)) {
 
2355
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2356
                        g_error_free (error);
 
2357
                        return;
 
2358
                }
 
2359
 
 
2360
                user->account_type = account_type;
 
2361
 
 
2362
                g_signal_emit (user, signals[CHANGED], 0);
 
2363
 
 
2364
                g_object_notify (G_OBJECT (user), "account-type");
 
2365
        }
 
2366
 
 
2367
        dbus_g_method_return (context);
 
2368
}
 
2369
 
 
2370
gboolean
 
2371
user_set_account_type (User                  *user,
 
2372
                       gint                   account_type,
 
2373
                       DBusGMethodInvocation *context)
 
2374
{
 
2375
        if (account_type < 0 || account_type > ACCOUNT_TYPE_LAST) {
 
2376
                throw_error (context, ERROR_FAILED, "unknown account type: %d", account_type);
 
2377
                return FALSE;
 
2378
        }
 
2379
 
 
2380
        daemon_local_check_auth (user->daemon,
 
2381
                                 user,
 
2382
                                 "org.freedesktop.accounts.user-administration",
 
2383
                                 TRUE,
 
2384
                                 user_change_account_type_authorized_cb,
 
2385
                                 context,
 
2386
                                 GINT_TO_POINTER (account_type),
 
2387
                                 NULL);
 
2388
 
 
2389
        return TRUE;
 
2390
}
 
2391
 
 
2392
static void
 
2393
user_change_password_mode_authorized_cb (Daemon                *daemon,
 
2394
                                         User                  *user,
 
2395
                                         DBusGMethodInvocation *context,
 
2396
                                         gpointer               data)
 
2397
 
 
2398
{
 
2399
        gint mode = GPOINTER_TO_INT (data);
 
2400
        GError *error;
 
2401
        gchar *argv[6];
 
2402
 
 
2403
        if (user->password_mode != mode) {
 
2404
                sys_log (context,
 
2405
                         "change password mode of user '%s' (%d) to %d",
 
2406
                         user->user_name, user->uid, mode);
 
2407
 
 
2408
                g_object_freeze_notify (G_OBJECT (user));
 
2409
 
 
2410
                if (mode == PASSWORD_MODE_SET_AT_LOGIN ||
 
2411
                    mode == PASSWORD_MODE_NONE) {
 
2412
 
 
2413
                        argv[0] = "/usr/bin/passwd";
 
2414
                        argv[1] = "-d";
 
2415
                        argv[2] = "--";
 
2416
                        argv[3] = user->user_name;
 
2417
                        argv[4] = NULL;
 
2418
 
 
2419
                        error = NULL;
 
2420
                        if (!spawn_with_login_uid (context, argv, &error)) {
 
2421
                                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2422
                                g_error_free (error);
 
2423
                                return;
 
2424
                        }
 
2425
 
 
2426
                        if (mode == PASSWORD_MODE_NONE) {
 
2427
                                argv[0] = "/usr/bin/gpasswd";
 
2428
                                argv[1] = "-a";
 
2429
                                argv[2] = user->user_name;
 
2430
                                argv[3] = "nopasswdlogin";
 
2431
                                argv[4] = NULL;
 
2432
 
 
2433
                                error = NULL;
 
2434
                                if (!spawn_with_login_uid (context, argv, &error)) {
 
2435
                                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2436
                                        g_error_free (error);
 
2437
                                        return;
 
2438
                                }
 
2439
                        }
 
2440
 
 
2441
                        if (mode == PASSWORD_MODE_SET_AT_LOGIN) {
 
2442
                                argv[0] = "/usr/bin/chage";
 
2443
                                argv[1] = "-d";
 
2444
                                argv[2] = "0";
 
2445
                                argv[3] = "--";
 
2446
                                argv[4] = user->user_name;
 
2447
                                argv[5] = NULL;
 
2448
 
 
2449
                                error = NULL;
 
2450
                                if (!spawn_with_login_uid (context, argv, &error)) {
 
2451
                                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2452
                                        g_error_free (error);
 
2453
                                        return;
 
2454
                                }
 
2455
                        }
 
2456
 
 
2457
                        g_free (user->password_hint);
 
2458
                        user->password_hint = NULL;
 
2459
 
 
2460
                        g_object_notify (G_OBJECT (user), "password-hint");
 
2461
 
 
2462
                        /* removing the password has the side-effect of
 
2463
                         * unlocking the account
 
2464
                         */
 
2465
                        if (user->locked) {
 
2466
                                user->locked = FALSE;
 
2467
                                g_object_notify (G_OBJECT (user), "locked");
 
2468
                        }
 
2469
                }
 
2470
                else if (user->locked) {
 
2471
                        argv[0] = "/usr/sbin/usermod";
 
2472
                        argv[1] = "-U";
 
2473
                        argv[2] = "--";
 
2474
                        argv[3] = user->user_name;
 
2475
                        argv[4] = NULL;
 
2476
 
 
2477
                        error = NULL;
 
2478
                        if (!spawn_with_login_uid (context, argv, &error)) {
 
2479
                                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2480
                                g_error_free (error);
 
2481
                                return;
 
2482
                        }
 
2483
 
 
2484
                        user->locked = FALSE;
 
2485
                        g_object_notify (G_OBJECT (user), "locked");
 
2486
                }
 
2487
 
 
2488
                /* Remember to remove user from nopasswdlogin group if we're
 
2489
                   switching away from no-password mode */
 
2490
                if (user->password_mode == PASSWORD_MODE_NONE) {
 
2491
                        argv[0] = "/usr/bin/gpasswd";
 
2492
                        argv[1] = "-d";
 
2493
                        argv[2] = user->user_name;
 
2494
                        argv[3] = "nopasswdlogin";
 
2495
                        argv[4] = NULL;
 
2496
 
 
2497
                        error = NULL;
 
2498
                        if (!spawn_with_login_uid (context, argv, &error)) {
 
2499
                                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2500
                                g_error_free (error);
 
2501
                                return;
 
2502
                        }
 
2503
                }
 
2504
 
 
2505
                user->password_mode = mode;
 
2506
 
 
2507
                g_object_notify (G_OBJECT (user), "password-mode");
 
2508
 
 
2509
                save_extra_data (user);
 
2510
 
 
2511
                g_object_thaw_notify (G_OBJECT (user));
 
2512
 
 
2513
                g_signal_emit (user, signals[CHANGED], 0);
 
2514
        }
 
2515
 
 
2516
        dbus_g_method_return (context);
 
2517
}
 
2518
 
 
2519
gboolean
 
2520
user_set_password_mode (User                  *user,
 
2521
                        gint                   mode,
 
2522
                        DBusGMethodInvocation *context)
 
2523
{
 
2524
        gchar *sender;
 
2525
        DBusConnection *connection;
 
2526
        DBusError dbus_error;
 
2527
        uid_t uid;
 
2528
        const gchar *action_id;
 
2529
 
 
2530
        if (mode < 0 || mode > PASSWORD_MODE_LAST) {
 
2531
                throw_error (context, ERROR_FAILED, "unknown password mode: %d", mode);
 
2532
                return FALSE;
 
2533
        }
 
2534
 
 
2535
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
2536
        sender = dbus_g_method_get_sender (context);
 
2537
        dbus_error_init (&dbus_error);
 
2538
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
2539
        if (dbus_error_is_set (&dbus_error)) {
 
2540
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
2541
                dbus_error_free (&dbus_error);
 
2542
 
 
2543
                return TRUE;
 
2544
        }
 
2545
 
 
2546
        action_id = "org.freedesktop.accounts.user-administration";
 
2547
 
 
2548
        daemon_local_check_auth (user->daemon,
 
2549
                                 user,
 
2550
                                 action_id,
 
2551
                                 TRUE,
 
2552
                                 user_change_password_mode_authorized_cb,
 
2553
                                 context,
 
2554
                                 GINT_TO_POINTER (mode),
 
2555
                                 NULL);
 
2556
 
 
2557
        return TRUE;
 
2558
}
 
2559
 
 
2560
static void
 
2561
user_change_password_authorized_cb (Daemon                *daemon,
 
2562
                                    User                  *user,
 
2563
                                    DBusGMethodInvocation *context,
 
2564
                                    gpointer               data)
 
2565
 
 
2566
{
 
2567
        gchar **strings = data;
 
2568
        GError *error;
 
2569
        gchar *argv[6];
 
2570
 
 
2571
        sys_log (context,
 
2572
                 "set password and hint of user '%s' (%d)",
 
2573
                 user->user_name, user->uid);
 
2574
 
 
2575
        g_object_freeze_notify (G_OBJECT (user));
 
2576
 
 
2577
        argv[0] = "/usr/sbin/usermod";
 
2578
        argv[1] = "-p";
 
2579
        argv[2] = strings[0];
 
2580
        argv[3] = "--";
 
2581
        argv[4] = user->user_name;
 
2582
        argv[5] = NULL;
 
2583
 
 
2584
        error = NULL;
 
2585
        if (!spawn_with_login_uid (context, argv, &error)) {
 
2586
                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2587
                g_error_free (error);
 
2588
                return;
 
2589
        }
 
2590
 
 
2591
        /* Drop user from nopasswdlogin group */
 
2592
        argv[0] = "/usr/bin/gpasswd";
 
2593
        argv[1] = "-d";
 
2594
        argv[2] = user->user_name;
 
2595
        argv[3] = "nopasswdlogin";
 
2596
        argv[4] = NULL;
 
2597
 
 
2598
        error = NULL;
 
2599
        if (!spawn_with_login_uid (context, argv, &error)) {
 
2600
                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
 
2601
                g_error_free (error);
 
2602
                return;
 
2603
        }
 
2604
 
 
2605
        if (user->password_mode != PASSWORD_MODE_REGULAR) {
 
2606
                user->password_mode = PASSWORD_MODE_REGULAR;
 
2607
                g_object_notify (G_OBJECT (user), "password-mode");
 
2608
        }
 
2609
 
 
2610
        if (user->locked) {
 
2611
                user->locked = FALSE;
 
2612
                g_object_notify (G_OBJECT (user), "locked");
 
2613
        }
 
2614
 
 
2615
        if (g_strcmp0 (user->password_hint, strings[1]) != 0) {
 
2616
                g_free (user->password_hint);
 
2617
                user->password_hint = g_strdup (strings[1]);
 
2618
                g_object_notify (G_OBJECT (user), "password-hint");
 
2619
        }
 
2620
 
 
2621
        save_extra_data (user);
 
2622
 
 
2623
        g_object_thaw_notify (G_OBJECT (user));
 
2624
 
 
2625
        g_signal_emit (user, signals[CHANGED], 0);
 
2626
 
 
2627
        dbus_g_method_return (context);
 
2628
}
 
2629
 
 
2630
static void
 
2631
free_passwords (gchar **strings)
 
2632
{
 
2633
        memset (strings[0], 0, strlen (strings[0]));
 
2634
        g_strfreev (strings);
 
2635
}
 
2636
 
 
2637
gboolean
 
2638
user_set_password (User                  *user,
 
2639
                   const gchar           *password,
 
2640
                   const gchar           *hint,
 
2641
                   DBusGMethodInvocation *context)
 
2642
{
 
2643
        gchar *sender;
 
2644
        DBusConnection *connection;
 
2645
        DBusError dbus_error;
 
2646
        uid_t uid;
 
2647
        gchar **data;
 
2648
 
 
2649
        connection = dbus_g_connection_get_connection (user->system_bus_connection);
 
2650
        sender = dbus_g_method_get_sender (context);
 
2651
        dbus_error_init (&dbus_error);
 
2652
        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
 
2653
        if (dbus_error_is_set (&dbus_error)) {
 
2654
                throw_error (context, ERROR_FAILED, dbus_error.message);
 
2655
                dbus_error_free (&dbus_error);
 
2656
 
 
2657
                return TRUE;
 
2658
        }
 
2659
 
 
2660
        data = g_new (gchar *, 3);
 
2661
        data[0] = g_strdup (password);
 
2662
        data[1] = g_strdup (hint);
 
2663
        data[2] = NULL;
 
2664
 
 
2665
        daemon_local_check_auth (user->daemon,
 
2666
                                 user,
 
2667
                                 "org.freedesktop.accounts.user-administration",
 
2668
                                 TRUE,
 
2669
                                 user_change_password_authorized_cb,
 
2670
                                 context,
 
2671
                                 data,
 
2672
                                 (GDestroyNotify)free_passwords);
 
2673
 
 
2674
        memset ((char*)password, 0, strlen (password));
 
2675
 
 
2676
        return TRUE;
 
2677
}
 
2678
 
 
2679
static void
 
2680
user_change_automatic_login_authorized_cb (Daemon                *daemon,
 
2681
                                           User                  *user,
 
2682
                                           DBusGMethodInvocation *context,
 
2683
                                           gpointer               data)
 
2684
{
 
2685
        gboolean enabled = GPOINTER_TO_INT (data);
 
2686
        GError *error = NULL;
 
2687
 
 
2688
        sys_log (context,
 
2689
                 "%s automatic login for user '%s' (%d)",
 
2690
                 enabled ? "enable" : "disable", user->user_name, user->uid);
 
2691
 
 
2692
        if (!daemon_local_set_automatic_login (daemon, user, enabled, &error)) {
 
2693
                throw_error (context, ERROR_FAILED, "failed to change automatic login: %s", error->message);
 
2694
                g_error_free (error);
 
2695
                return;
 
2696
        }
 
2697
 
 
2698
        dbus_g_method_return (context);
 
2699
}
 
2700
 
 
2701
gboolean
 
2702
user_set_automatic_login (User                  *user,
 
2703
                          gboolean               enabled,
 
2704
                          DBusGMethodInvocation *context)
 
2705
{
 
2706
        daemon_local_check_auth (user->daemon,
 
2707
                                 user,
 
2708
                                 "org.freedesktop.accounts.user-administration",
 
2709
                                 TRUE,
 
2710
                                 user_change_automatic_login_authorized_cb,
 
2711
                                 context,
 
2712
                                 GINT_TO_POINTER (enabled),
 
2713
                                 NULL);
 
2714
 
 
2715
        return TRUE;
 
2716
}
 
2717