user.c 77.8 KB
Newer Older
Matthias Clasen's avatar
Matthias Clasen committed
1 2 3 4 5
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
  * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
  * Copyright (C) 2009-2010 Red Hat, Inc.
6
  * Copyright © 2013 Canonical Limited
Matthias Clasen's avatar
Matthias Clasen committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */

#define _BSD_SOURCE

25 26
#include "config.h"

27
#include <stdlib.h>
Matthias Clasen's avatar
Matthias Clasen committed
28 29 30 31 32
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <grp.h>
Ed Schouten's avatar
Ed Schouten committed
33
#ifdef HAVE_SHADOW_H
34
#include <shadow.h>
Ed Schouten's avatar
Ed Schouten committed
35
#endif
Matthias Clasen's avatar
Matthias Clasen committed
36 37 38 39

#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
Matthias Clasen's avatar
Matthias Clasen committed
40
#include <glib/gstdio.h>
Matthias Clasen's avatar
Matthias Clasen committed
41
#include <gio/gio.h>
42
#include <gio/gunixinputstream.h>
Matthias Clasen's avatar
Matthias Clasen committed
43
#include <polkit/polkit.h>
Matthias Clasen's avatar
Matthias Clasen committed
44

45
#include "user-classify.h"
Matthias Clasen's avatar
Matthias Clasen committed
46 47
#include "daemon.h"
#include "user.h"
Matthias Clasen's avatar
Matthias Clasen committed
48
#include "accounts-user-generated.h"
49
#include "util.h"
Matthias Clasen's avatar
Matthias Clasen committed
50 51

struct User {
Matthias Clasen's avatar
Matthias Clasen committed
52
        AccountsUserSkeleton parent;
Matthias Clasen's avatar
Matthias Clasen committed
53

Matthias Clasen's avatar
Matthias Clasen committed
54
        GDBusConnection *system_bus_connection;
Matthias Clasen's avatar
Matthias Clasen committed
55 56 57 58
        gchar *object_path;

        Daemon       *daemon;

Allison Lortie's avatar
Allison Lortie committed
59 60
        GKeyFile     *keyfile;

Matthias Clasen's avatar
Matthias Clasen committed
61
        gid_t         gid;
62 63 64 65 66 67
        gint64        expiration_time;
        gint64        last_change_time;
        gint64        min_days_between_changes;
        gint64        max_days_between_changes;
        gint64        days_to_warn;
        gint64        days_after_expiration_until_lock;
68
        GVariant     *login_history;
Matthias Clasen's avatar
Matthias Clasen committed
69
        gchar        *icon_file;
Matthias Clasen's avatar
Matthias Clasen committed
70
        gchar        *default_icon_file;
71
        gchar        *gecos;
72
        gboolean      account_expiration_policy_known;
73
        gboolean      cached;
74 75 76

        guint        *extension_ids;
        guint         n_extension_ids;
77 78

        guint         changed_timeout_id;
Matthias Clasen's avatar
Matthias Clasen committed
79 80 81 82
};

typedef struct UserClass
{
Matthias Clasen's avatar
Matthias Clasen committed
83
        AccountsUserSkeletonClass parent_class;
Matthias Clasen's avatar
Matthias Clasen committed
84 85
} UserClass;

Matthias Clasen's avatar
Matthias Clasen committed
86
static void user_accounts_user_iface_init (AccountsUserIface *iface);
Matthias Clasen's avatar
Matthias Clasen committed
87

Matthias Clasen's avatar
Matthias Clasen committed
88
G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init));
Matthias Clasen's avatar
Matthias Clasen committed
89 90

static gint
Ray Strode's avatar
Ray Strode committed
91
account_type_from_pwent (struct passwd *pwent)
Matthias Clasen's avatar
Matthias Clasen committed
92 93 94 95
{
        struct group *grp;
        gint i;

Ray Strode's avatar
Ray Strode committed
96 97 98 99 100
        if (pwent->pw_uid == 0) {
                g_debug ("user is root so account type is administrator");
                return ACCOUNT_TYPE_ADMINISTRATOR;
        }

101
        grp = getgrnam (ADMIN_GROUP);
Matthias Clasen's avatar
Matthias Clasen committed
102
        if (grp == NULL) {
103
                g_debug (ADMIN_GROUP " group not found");
Matthias Clasen's avatar
Matthias Clasen committed
104 105 106
                return ACCOUNT_TYPE_STANDARD;
        }

107 108
        for (i = 0; grp->gr_mem[i] != NULL; i++) {
                if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) {
Matthias Clasen's avatar
Matthias Clasen committed
109
                        return ACCOUNT_TYPE_ADMINISTRATOR;
110
                }
Matthias Clasen's avatar
Matthias Clasen committed
111 112
        }

113
        return ACCOUNT_TYPE_STANDARD;
Matthias Clasen's avatar
Matthias Clasen committed
114 115
}

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
static void
user_reset_icon_file (User *user)
{
        const char *icon_file;
        gboolean    icon_is_default;
        const char *home_dir;

        icon_file = accounts_user_get_icon_file (ACCOUNTS_USER (user));

        if (icon_file == NULL || g_strcmp0 (icon_file, user->default_icon_file) == 0) {
                icon_is_default = TRUE;
        } else {
                icon_is_default = FALSE;
        }

        g_free (user->default_icon_file);
        home_dir = accounts_user_get_home_directory (ACCOUNTS_USER (user));

        user->default_icon_file = g_build_filename (home_dir, ".face", NULL);

        if (icon_is_default) {
                accounts_user_set_icon_file (ACCOUNTS_USER (user), user->default_icon_file);
        }
}

Matthias Clasen's avatar
Matthias Clasen committed
141
void
142
user_update_from_pwent (User          *user,
143 144
                        struct passwd *pwent,
                        struct spwd   *spent)
Matthias Clasen's avatar
Matthias Clasen committed
145
{
146
        g_autofree gchar *real_name = NULL;
147
        gboolean is_system_account;
Matthias Clasen's avatar
Matthias Clasen committed
148 149
        const gchar *passwd;
        gboolean locked;
150
        PasswordMode mode;
151
        AccountType account_type;
152

Matthias Clasen's avatar
Matthias Clasen committed
153 154
        g_object_freeze_notify (G_OBJECT (user));

155
        g_clear_pointer (&user->gecos, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
156 157 158 159 160 161 162
        if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
                gchar *first_comma = NULL;
                gchar *valid_utf8_name = NULL;

                if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
                        valid_utf8_name = pwent->pw_gecos;
                        first_comma = g_utf8_strchr (valid_utf8_name, -1, ',');
163
                        user->gecos = g_strdup (pwent->pw_gecos);
Matthias Clasen's avatar
Matthias Clasen committed
164 165
                }
                else {
Matthias Clasen's avatar
Matthias Clasen committed
166 167 168 169 170 171 172 173
                        g_warning ("User %s has invalid UTF-8 in GECOS field. "
                                   "It would be a good thing to check /etc/passwd.",
                                   pwent->pw_name ? pwent->pw_name : "");
                }

                if (first_comma) {
                        real_name = g_strndup (valid_utf8_name,
                                                  (first_comma - valid_utf8_name));
Matthias Clasen's avatar
Matthias Clasen committed
174 175
                }
                else if (valid_utf8_name) {
Matthias Clasen's avatar
Matthias Clasen committed
176
                        real_name = g_strdup (valid_utf8_name);
Matthias Clasen's avatar
Matthias Clasen committed
177 178
                }
                else {
Matthias Clasen's avatar
Matthias Clasen committed
179 180 181 182
                        real_name = NULL;
                }

                if (real_name && real_name[0] == '\0') {
183
                        g_clear_pointer (&real_name, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
184
                }
Matthias Clasen's avatar
Matthias Clasen committed
185 186
        }
        else {
Matthias Clasen's avatar
Matthias Clasen committed
187 188 189
                real_name = NULL;
        }

190 191
        accounts_user_set_real_name (ACCOUNTS_USER (user), real_name);
        accounts_user_set_uid (ACCOUNTS_USER (user), pwent->pw_uid);
Matthias Clasen's avatar
Matthias Clasen committed
192 193 194

        user->gid = pwent->pw_gid;

195
        account_type = account_type_from_pwent (pwent);
196
        accounts_user_set_account_type (ACCOUNTS_USER (user), account_type);
Matthias Clasen's avatar
Matthias Clasen committed
197

198 199 200
        accounts_user_set_user_name (ACCOUNTS_USER (user), pwent->pw_name);
        accounts_user_set_home_directory (ACCOUNTS_USER (user), pwent->pw_dir);
        user_reset_icon_file (user);
Matthias Clasen's avatar
Matthias Clasen committed
201

202
        accounts_user_set_shell (ACCOUNTS_USER (user), pwent->pw_shell);
Matthias Clasen's avatar
Matthias Clasen committed
203

204
        passwd = NULL;
205
        if (spent)
Matthias Clasen's avatar
Matthias Clasen committed
206
                passwd = spent->sp_pwdp;
207

Matthias Clasen's avatar
Matthias Clasen committed
208 209
        if (passwd && passwd[0] == '!') {
                locked = TRUE;
210 211
        }
        else {
Matthias Clasen's avatar
Matthias Clasen committed
212 213 214
                locked = FALSE;
        }

215
        accounts_user_set_locked (ACCOUNTS_USER (user), locked);
Matthias Clasen's avatar
Matthias Clasen committed
216

217
        if (passwd == NULL || passwd[0] != 0) {
218
                mode = PASSWORD_MODE_REGULAR;
Matthias Clasen's avatar
Matthias Clasen committed
219 220
        }
        else {
221
                mode = PASSWORD_MODE_NONE;
Matthias Clasen's avatar
Matthias Clasen committed
222 223 224 225 226
        }

        if (spent) {
                if (spent->sp_lstchg == 0) {
                        mode = PASSWORD_MODE_SET_AT_LOGIN;
227
                }
228 229 230 231 232 233 234

                user->expiration_time = spent->sp_expire;
                user->last_change_time  = spent->sp_lstchg;
                user->min_days_between_changes = spent->sp_min;
                user->max_days_between_changes = spent->sp_max;
                user->days_to_warn  = spent->sp_warn;
                user->days_after_expiration_until_lock = spent->sp_inact;
235
                user->account_expiration_policy_known = TRUE;
236 237
        }

238 239 240 241 242 243
        accounts_user_set_password_mode (ACCOUNTS_USER (user), mode);
        is_system_account = !user_classify_is_human (accounts_user_get_uid (ACCOUNTS_USER (user)),
                                                     accounts_user_get_user_name (ACCOUNTS_USER (user)),
                                                     accounts_user_get_shell (ACCOUNTS_USER (user)),
                                                     passwd);
        accounts_user_set_system_account (ACCOUNTS_USER (user), is_system_account);
244

Matthias Clasen's avatar
Matthias Clasen committed
245 246 247 248
        g_object_thaw_notify (G_OBJECT (user));
}

void
249 250
user_update_from_keyfile (User     *user,
                          GKeyFile *keyfile)
Matthias Clasen's avatar
Matthias Clasen committed
251 252 253 254 255 256 257
{
        gchar *s;

        g_object_freeze_notify (G_OBJECT (user));

        s = g_key_file_get_string (keyfile, "User", "Language", NULL);
        if (s != NULL) {
258 259
                accounts_user_set_language (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
260 261
        }

262 263
        s = g_key_file_get_string (keyfile, "User", "XSession", NULL);
        if (s != NULL) {
264
                accounts_user_set_xsession (ACCOUNTS_USER (user), s);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

                /* for backward compat */
                accounts_user_set_session (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
        }

        s = g_key_file_get_string (keyfile, "User", "Session", NULL);
        if (s != NULL) {
                accounts_user_set_session (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
        }

        s = g_key_file_get_string (keyfile, "User", "SessionType", NULL);
        if (s != NULL) {
                accounts_user_set_session_type (ACCOUNTS_USER (user), s);
280
                g_clear_pointer (&s, g_free);
281 282
        }

Matthias Clasen's avatar
Matthias Clasen committed
283 284
        s = g_key_file_get_string (keyfile, "User", "Email", NULL);
        if (s != NULL) {
285 286
                accounts_user_set_email (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
287 288 289 290
        }

        s = g_key_file_get_string (keyfile, "User", "Location", NULL);
        if (s != NULL) {
291 292
                accounts_user_set_location (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
293 294 295 296
        }

        s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL);
        if (s != NULL) {
297 298
                accounts_user_set_password_hint (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
299 300 301 302
        }

        s = g_key_file_get_string (keyfile, "User", "Icon", NULL);
        if (s != NULL) {
303 304
                accounts_user_set_icon_file (ACCOUNTS_USER (user), s);
                g_clear_pointer (&s, g_free);
Matthias Clasen's avatar
Matthias Clasen committed
305 306
        }

307 308 309 310
        if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) {
            gboolean system_account;

            system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL);
311
            accounts_user_set_system_account (ACCOUNTS_USER (user), system_account);
312 313
        }

Allison Lortie's avatar
Allison Lortie committed
314 315
        g_clear_pointer (&user->keyfile, g_key_file_unref);
        user->keyfile = g_key_file_ref (keyfile);
316
        user_set_cached (user, TRUE);
Ray Strode's avatar
Ray Strode committed
317
        user_set_saved (user, TRUE);
Allison Lortie's avatar
Allison Lortie committed
318

Matthias Clasen's avatar
Matthias Clasen committed
319 320 321
        g_object_thaw_notify (G_OBJECT (user));
}

Stef Walter's avatar
Stef Walter committed
322 323 324 325
void
user_update_local_account_property (User          *user,
                                    gboolean       local)
{
326
        accounts_user_set_local_account (ACCOUNTS_USER (user), local);
Stef Walter's avatar
Stef Walter committed
327 328
}

329 330 331 332
void
user_update_system_account_property (User          *user,
                                     gboolean       system)
{
333
        accounts_user_set_system_account (ACCOUNTS_USER (user), system);
334 335
}

Matthias Clasen's avatar
Matthias Clasen committed
336
static void
337 338
user_save_to_keyfile (User     *user,
                      GKeyFile *keyfile)
Matthias Clasen's avatar
Matthias Clasen committed
339
{
Allison Lortie's avatar
Allison Lortie committed
340 341
        g_key_file_remove_group (keyfile, "User", NULL);

342 343
        if (accounts_user_get_email (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user)));
Matthias Clasen's avatar
Matthias Clasen committed
344

345 346
        if (accounts_user_get_language (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user)));
Matthias Clasen's avatar
Matthias Clasen committed
347

348 349 350 351 352 353
        if (accounts_user_get_session (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "Session", accounts_user_get_session (ACCOUNTS_USER (user)));

        if (accounts_user_get_session_type (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "SessionType", accounts_user_get_session_type (ACCOUNTS_USER (user)));

354 355
        if (accounts_user_get_xsession (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "XSession", accounts_user_get_xsession (ACCOUNTS_USER (user)));
356

357 358
        if (accounts_user_get_location (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "Location", accounts_user_get_location (ACCOUNTS_USER (user)));
Matthias Clasen's avatar
Matthias Clasen committed
359

360 361
        if (accounts_user_get_password_hint (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "PasswordHint", accounts_user_get_password_hint (ACCOUNTS_USER (user)));
Matthias Clasen's avatar
Matthias Clasen committed
362

363 364
        if (accounts_user_get_icon_file (ACCOUNTS_USER (user)))
                g_key_file_set_string (keyfile, "User", "Icon", accounts_user_get_icon_file (ACCOUNTS_USER (user)));
365

366
        g_key_file_set_boolean (keyfile, "User", "SystemAccount", accounts_user_get_system_account (ACCOUNTS_USER (user)));
367 368

        user_set_cached (user, TRUE);
Matthias Clasen's avatar
Matthias Clasen committed
369 370 371 372 373
}

static void
save_extra_data (User *user)
{
374 375 376
        g_autofree gchar *data = NULL;
        g_autofree gchar *filename = NULL;
        g_autoptr(GError) error = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
377

Allison Lortie's avatar
Allison Lortie committed
378
        user_save_to_keyfile (user, user->keyfile);
Matthias Clasen's avatar
Matthias Clasen committed
379

Allison Lortie's avatar
Allison Lortie committed
380
        data = g_key_file_to_data (user->keyfile, NULL, &error);
381
        if (data == NULL) {
Matthias Clasen's avatar
Matthias Clasen committed
382
                g_warning ("Saving data for user %s failed: %s",
383
                           accounts_user_get_user_name (ACCOUNTS_USER (user)), error->message);
384
                return;
Matthias Clasen's avatar
Matthias Clasen committed
385
        }
386 387

        filename = g_build_filename (USERDIR,
388
                                     accounts_user_get_user_name (ACCOUNTS_USER (user)),
389 390
                                     NULL);
        g_file_set_contents (filename, data, -1, &error);
Ray Strode's avatar
Ray Strode committed
391 392

        user_set_saved (user, TRUE);
Matthias Clasen's avatar
Matthias Clasen committed
393 394 395 396 397 398
}

static void
move_extra_data (const gchar *old_name,
                 const gchar *new_name)
{
399 400
        g_autofree gchar *old_filename = NULL;
        g_autofree gchar *new_filename = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
401

402
        old_filename = g_build_filename (USERDIR,
Matthias Clasen's avatar
Matthias Clasen committed
403
                                         old_name, NULL);
404
        new_filename = g_build_filename (USERDIR,
Matthias Clasen's avatar
Matthias Clasen committed
405
                                         new_name, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
406 407 408 409

        g_rename (old_filename, new_filename);
}

410 411 412 413 414 415 416
static GVariant *
user_extension_get_value (User                    *user,
                          GDBusInterfaceInfo      *interface,
                          const GDBusPropertyInfo *property)
{
        const GVariantType *type = G_VARIANT_TYPE (property->signature);
        GVariant *value;
417
        g_autofree gchar *printed = NULL;
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
        gint i;

        /* First, try to get the value from the keyfile */
        printed = g_key_file_get_value (user->keyfile, interface->name, property->name, NULL);
        if (printed) {
                value = g_variant_parse (type, printed, NULL, NULL, NULL);
                if (value != NULL)
                        return value;
        }

        /* If that didn't work, try for a default value annotation */
        for (i = 0; property->annotations && property->annotations[i]; i++) {
                GDBusAnnotationInfo *annotation = property->annotations[i];

                if (g_str_equal (annotation->key, "org.freedesktop.Accounts.DefaultValue.String")) {
                        if (g_str_equal (property->signature, "s"))
                                return g_variant_ref_sink (g_variant_new_string (annotation->value));
                }
                else if (g_str_equal (annotation->key, "org.freedesktop.Accounts.DefaultValue")) {
                        value = g_variant_parse (type, annotation->value, NULL, NULL, NULL);
                        if (value != NULL)
                                return value;
                }
        }

        /* Nothing found... */
        return NULL;
}

static void
user_extension_get_property (User                  *user,
                             Daemon                *daemon,
                             GDBusInterfaceInfo    *interface,
                             GDBusMethodInvocation *invocation)
{
        const GDBusPropertyInfo *property = g_dbus_method_invocation_get_property_info (invocation);
454
        g_autoptr(GVariant) value = NULL;
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

        value = user_extension_get_value (user, interface, property);

        if (value) {
                g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", value));
        }
        else {
                g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                                       "Key '%s' is not set and has no default value",
                                                       property->name);
        }
}

static void
user_extension_get_all_properties (User                  *user,
                                   Daemon                *daemon,
                                   GDBusInterfaceInfo    *interface,
                                   GDBusMethodInvocation *invocation)
{
        GVariantBuilder builder;
        gint i;

        g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
        for (i = 0; interface->properties && interface->properties[i]; i++) {
                GDBusPropertyInfo *property = interface->properties[i];
480
                g_autoptr(GVariant) value = NULL;
481 482 483

                value = user_extension_get_value (user, interface, property);

484
                if (value)
485 486 487 488 489 490 491 492 493 494 495 496 497
                        g_variant_builder_add (&builder, "{sv}", property->name, value);
        }

        g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{sv})", &builder));
}

static void
user_extension_set_property (User                  *user,
                             Daemon                *daemon,
                             GDBusInterfaceInfo    *interface,
                             GDBusMethodInvocation *invocation)
{
        const GDBusPropertyInfo *property = g_dbus_method_invocation_get_property_info (invocation);
498 499 500
        g_autoptr(GVariant) value = NULL;
        g_autofree gchar *printed = NULL;
        g_autofree gchar *prev = NULL;
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578

        g_variant_get_child (g_dbus_method_invocation_get_parameters (invocation), 2, "v", &value);

        /* We'll always have the type when we parse it back so
         * we don't need it to be printed with annotations.
         */
        printed = g_variant_print (value, FALSE);

        /* May as well try to avoid the thrashing... */
        prev = g_key_file_get_value (user->keyfile, interface->name, property->name, NULL);

        if (!prev || !g_str_equal (printed, prev)) {
                g_key_file_set_value (user->keyfile, interface->name, property->name, printed);

                /* Emit a change signal.  Use invalidation
                 * because the data may not be world-readable.
                 */
                g_dbus_connection_emit_signal (g_dbus_method_invocation_get_connection (invocation),
                                               NULL, /* destination_bus_name */
                                               g_dbus_method_invocation_get_object_path (invocation),
                                               "org.freedesktop.DBus.Properties", "PropertiesChanged",
                                               g_variant_new_parsed ("( %s, %a{sv}, [ %s ] )",
                                                                     interface->name, NULL, property->name),
                                               NULL);

                accounts_user_emit_changed (ACCOUNTS_USER (user));
                save_extra_data (user);
        }

        g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
}

static void
user_extension_authentication_done (Daemon                *daemon,
                                    User                  *user,
                                    GDBusMethodInvocation *invocation,
                                    gpointer               user_data)
{
        GDBusInterfaceInfo *interface = user_data;
        const gchar *method_name;

        method_name = g_dbus_method_invocation_get_method_name (invocation);

        if (g_str_equal (method_name, "Get"))
                user_extension_get_property (user, daemon, interface, invocation);
        else if (g_str_equal (method_name, "GetAll"))
                user_extension_get_all_properties (user, daemon, interface, invocation);
        else if (g_str_equal (method_name, "Set"))
                user_extension_set_property (user, daemon, interface, invocation);
        else
                g_assert_not_reached ();
}

static void
user_extension_method_call (GDBusConnection       *connection,
                            const gchar           *sender,
                            const gchar           *object_path,
                            const gchar           *interface_name,
                            const gchar           *method_name,
                            GVariant              *parameters,
                            GDBusMethodInvocation *invocation,
                            gpointer               user_data)
{
        User *user = user_data;
        GDBusInterfaceInfo *iface_info;
        const gchar *annotation_name;
        const gchar *action_id;
        gint uid;
        gint i;

        /* We don't allow method calls on extension interfaces, so we
         * should only ever see property calls here.
         */
        g_assert_cmpstr (interface_name, ==, "org.freedesktop.DBus.Properties");

        /* Now get the real interface name */
        g_variant_get_child (parameters, 0, "&s", &interface_name);

579
        if (get_caller_uid (invocation, &uid) && (uid_t) uid == accounts_user_get_uid (ACCOUNTS_USER (user))) {
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
                /* Operation on sender's own User object */
                if (g_str_equal (method_name, "Set")) {
                        annotation_name = "org.freedesktop.Accounts.Authentication.ChangeOwn";
                        action_id = "org.freedesktop.accounts.change-own-user-data";
                }
                else {
                        annotation_name = "org.freedesktop.Accounts.Authentication.ReadOwn";
                        action_id = ""; /* reading allowed by default */
                }
        }
        else {
                /* Operation on someone else's User object */
                if (g_str_equal (method_name, "Set")) {
                        annotation_name = "org.freedesktop.Accounts.Authentication.ChangeAny";
                        action_id = "org.freedesktop.accounts.user-administration";
                }
                else {
                        annotation_name = "org.freedesktop.Accounts.Authentication.ReadAny";
                        action_id = ""; /* reading allowed by default */
                }
        }

        iface_info = g_hash_table_lookup (daemon_get_extension_ifaces (user->daemon), interface_name);
        g_assert (iface_info != NULL);

        for (i = 0; iface_info->annotations && iface_info->annotations[i]; i++) {
                if (g_str_equal (iface_info->annotations[i]->key, annotation_name)) {
                        action_id = iface_info->annotations[i]->value;
                        break;
                }
        }

        if (action_id[0] == '\0') {
                /* Should always allow this call, so just do it now */
                user_extension_authentication_done (user->daemon, user, invocation, iface_info);
        }
        else {
617
                daemon_local_check_auth (user->daemon, user, action_id,
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
                                         user_extension_authentication_done,
                                         invocation, iface_info, NULL);
        }
}

static void
user_register_extensions (User *user)
{
        static const GDBusInterfaceVTable vtable = {
                user_extension_method_call,
                NULL /* get_property */,
                NULL /* set_property */
        };
        GHashTable *extensions;
        GHashTableIter iter;
        gpointer iface;
        gint i = 0;

        g_assert (user->extension_ids == NULL);
        g_assert (user->n_extension_ids == 0);

        extensions = daemon_get_extension_ifaces (user->daemon);
        user->n_extension_ids = g_hash_table_size (extensions);
        user->extension_ids = g_new (guint, user->n_extension_ids);
        g_hash_table_iter_init (&iter, extensions);

        /* Ignore errors when registering more interfaces because (a)
         * they won't happen and (b) even if they do, we still want to
         * publish the main user interface.
         */
        while (g_hash_table_iter_next (&iter, NULL, &iface))
                user->extension_ids[i++] = g_dbus_connection_register_object (user->system_bus_connection,
650
                                                                              g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (user)), iface,
651 652 653
                                                                              &vtable, user, NULL, NULL);
}

Matthias Clasen's avatar
Matthias Clasen committed
654 655 656 657 658
static gchar *
compute_object_path (User *user)
{
        gchar *object_path;

659
        object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%lu",
660
                                       (gulong) accounts_user_get_uid (ACCOUNTS_USER (user)));
Matthias Clasen's avatar
Matthias Clasen committed
661 662 663 664

        return object_path;
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
static gboolean
on_user_changed_timeout (User *user)
{
        user->changed_timeout_id = 0;
        accounts_user_emit_changed (ACCOUNTS_USER (user));

        return G_SOURCE_REMOVE;
}

static void
on_user_property_notify (User *user)
{
        if (user->changed_timeout_id != 0)
                return;

        user->changed_timeout_id = g_timeout_add (250, (GSourceFunc) on_user_changed_timeout, user);
}

Matthias Clasen's avatar
Matthias Clasen committed
683
void
684
user_register (User *user)
Matthias Clasen's avatar
Matthias Clasen committed
685
{
686
        g_autoptr(GError) error = NULL;
687
        g_autofree gchar *object_path = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
688

Matthias Clasen's avatar
Matthias Clasen committed
689
        user->system_bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
Matthias Clasen's avatar
Matthias Clasen committed
690
        if (user->system_bus_connection == NULL) {
691
                if (error != NULL)
Matthias Clasen's avatar
Matthias Clasen committed
692
                        g_critical ("error getting system bus: %s", error->message);
Matthias Clasen's avatar
Matthias Clasen committed
693
                return;
Matthias Clasen's avatar
Matthias Clasen committed
694 695
        }

696
        object_path = compute_object_path (user);
Matthias Clasen's avatar
Matthias Clasen committed
697

Matthias Clasen's avatar
Matthias Clasen committed
698 699
        if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (user),
                                               user->system_bus_connection,
700
                                               object_path,
Matthias Clasen's avatar
Matthias Clasen committed
701
                                               &error)) {
702
                if (error != NULL)
Matthias Clasen's avatar
Matthias Clasen committed
703 704
                        g_critical ("error exporting user object: %s", error->message);
                return;
Matthias Clasen's avatar
Matthias Clasen committed
705
        }
706 707

        user_register_extensions (user);
708 709

        g_signal_connect (G_OBJECT (user), "notify", G_CALLBACK (on_user_property_notify), NULL);
Matthias Clasen's avatar
Matthias Clasen committed
710 711
}

712 713 714 715 716 717
void
user_save (User *user)
{
    save_extra_data (user);
}

Matthias Clasen's avatar
Matthias Clasen committed
718
void
719
user_unregister (User *user)
Matthias Clasen's avatar
Matthias Clasen committed
720
{
721 722
        g_signal_handlers_disconnect_by_func (G_OBJECT (user), G_CALLBACK (on_user_property_notify), NULL);

Matthias Clasen's avatar
Matthias Clasen committed
723
        g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (user));
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738

        if (user->extension_ids) {
                guint i;

                for (i = 0; i < user->n_extension_ids; i++) {
                        /* In theory, if an error happened during registration, we could have 0 here. */
                        if (user->extension_ids[i] == 0)
                                continue;

                        g_dbus_connection_unregister_object (user->system_bus_connection, user->extension_ids[i]);
                }

                g_clear_pointer (&user->extension_ids, g_free);
                user->n_extension_ids = 0;
        }
Matthias Clasen's avatar
Matthias Clasen committed
739 740
}

741 742 743 744 745 746
void
user_changed (User *user)
{
        accounts_user_emit_changed (ACCOUNTS_USER (user));
}

Matthias Clasen's avatar
Matthias Clasen committed
747
User *
748 749
user_new (Daemon *daemon,
          uid_t   uid)
Matthias Clasen's avatar
Matthias Clasen committed
750 751 752 753 754
{
        User *user;

        user = g_object_new (TYPE_USER, NULL);
        user->daemon = daemon;
755
        accounts_user_set_uid (ACCOUNTS_USER (user), uid);
Matthias Clasen's avatar
Matthias Clasen committed
756 757 758 759 760

        return user;
}

const gchar *
761
user_get_user_name (User *user)
Matthias Clasen's avatar
Matthias Clasen committed
762
{
763
        return accounts_user_get_user_name (ACCOUNTS_USER (user));
Matthias Clasen's avatar
Matthias Clasen committed
764 765
}

Ray Strode's avatar
Ray Strode committed
766
gboolean
767
user_get_system_account (User *user)
Ray Strode's avatar
Ray Strode committed
768
{
769
        return accounts_user_get_system_account (ACCOUNTS_USER (user));
Ray Strode's avatar
Ray Strode committed
770 771
}

Stef Walter's avatar
Stef Walter committed
772 773 774
gboolean
user_get_local_account (User *user)
{
775
        return accounts_user_get_local_account (ACCOUNTS_USER (user));;
Stef Walter's avatar
Stef Walter committed
776 777
}

Matthias Clasen's avatar
Matthias Clasen committed
778
const gchar *
779
user_get_object_path (User *user)
Matthias Clasen's avatar
Matthias Clasen committed
780
{
781
        return g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (user));
Matthias Clasen's avatar
Matthias Clasen committed
782 783
}

784
uid_t
785
user_get_uid (User *user)
786
{
787
        return accounts_user_get_uid (ACCOUNTS_USER (user));
788 789
}

790
const gchar *
791
user_get_shell(User *user)
792
{
793
	return accounts_user_get_shell (ACCOUNTS_USER (user));
794 795
}

796 797 798 799 800 801 802 803 804 805 806 807 808
gboolean
user_get_cached (User *user)
{
        return user->cached;
}

void
user_set_cached (User     *user,
                 gboolean  cached)
{
        user->cached = cached;
}

Ray Strode's avatar
Ray Strode committed
809 810 811 812 813 814 815
void
user_set_saved (User     *user,
                gboolean  saved)
{
        accounts_user_set_saved (ACCOUNTS_USER (user), saved);
}

Matthias Clasen's avatar
Matthias Clasen committed
816
static void
Matthias Clasen's avatar
Matthias Clasen committed
817
throw_error (GDBusMethodInvocation *context,
Matthias Clasen's avatar
Matthias Clasen committed
818 819 820 821 822
             gint                   error_code,
             const gchar           *format,
             ...)
{
        va_list args;
823
        g_autofree gchar *message = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
824 825 826 827 828

        va_start (args, format);
        message = g_strdup_vprintf (format, args);
        va_end (args);

Matthias Clasen's avatar
Matthias Clasen committed
829
        g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message);
Matthias Clasen's avatar
Matthias Clasen committed
830 831 832
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
833 834
user_change_real_name_authorized_cb (Daemon                *daemon,
                                     User                  *user,
Matthias Clasen's avatar
Matthias Clasen committed
835
                                     GDBusMethodInvocation *context,
Matthias Clasen's avatar
Matthias Clasen committed
836
                                     gpointer               data)
Matthias Clasen's avatar
Matthias Clasen committed
837 838 839

{
        gchar *name = data;
840
        g_autofree gchar *new_gecos = NULL;
841
        g_autoptr(GError) error = NULL;
842
        const gchar *first_comma = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
843
        const gchar *argv[6];
Matthias Clasen's avatar
Matthias Clasen committed
844

845
        if (g_strcmp0 (accounts_user_get_real_name (ACCOUNTS_USER (user)), name) != 0) {
846 847
                sys_log (context,
                         "change real name of user '%s' (%d) to '%s'",
848 849 850
                         accounts_user_get_user_name (ACCOUNTS_USER (user)),
                         accounts_user_get_uid (ACCOUNTS_USER (user)),
                         name);
Matthias Clasen's avatar
Matthias Clasen committed
851

852 853 854 855 856 857 858 859 860 861 862 863
                if (user->gecos != NULL)
                        first_comma = g_utf8_strchr (user->gecos, -1, ',');

                if (first_comma != NULL) {
                        /* Preserve the existing value of the GECOS
                         * except for the first element, full name.
                         */
                        new_gecos = g_strconcat (name, first_comma, NULL);
                } else {
                        new_gecos = g_strdup (name);
                }

864 865
                argv[0] = "/usr/sbin/usermod";
                argv[1] = "-c";
866
                argv[2] = new_gecos;
867
                argv[3] = "--";
868
                argv[4] = accounts_user_get_user_name (ACCOUNTS_USER (user));
869
                argv[5] = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
870

871
                if (!spawn_with_login_uid (context, argv, &error)) {
872
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
Matthias Clasen's avatar
Matthias Clasen committed
873 874 875
                        return;
                }

876
                accounts_user_set_real_name (ACCOUNTS_USER (user), name);
Matthias Clasen's avatar
Matthias Clasen committed
877 878
        }

Matthias Clasen's avatar
Matthias Clasen committed
879 880 881 882 883 884 885
        accounts_user_complete_set_real_name (ACCOUNTS_USER (user), context);
}

static gboolean
user_set_real_name (AccountsUser          *auser,
                    GDBusMethodInvocation *context,
                    const gchar           *real_name)
Matthias Clasen's avatar
Matthias Clasen committed
886
{
Matthias Clasen's avatar
Matthias Clasen committed
887
        User *user = (User*)auser;
888
        int uid;
Matthias Clasen's avatar
Matthias Clasen committed
889 890
        const gchar *action_id;

891 892
        if (!get_caller_uid (context, &uid)) {
                throw_error (context, ERROR_FAILED, "identifying caller failed");
893
                return TRUE;
894 895
        }

896 897 898 899 900
        if (g_utf8_strchr (real_name, -1, ',') != NULL) {
                throw_error (context, ERROR_FAILED, "setting real name failed: real name '%s' must not contain commas", real_name);
                return TRUE;
        }

901
        if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
Matthias Clasen's avatar
Matthias Clasen committed
902 903
                action_id = "org.freedesktop.accounts.change-own-user-data";
        else
Matthias Clasen's avatar
Matthias Clasen committed
904
                action_id = "org.freedesktop.accounts.user-administration";
Matthias Clasen's avatar
Matthias Clasen committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919

        daemon_local_check_auth (user->daemon,
                                 user,
                                 action_id,
                                 user_change_real_name_authorized_cb,
                                 context,
                                 g_strdup (real_name),
                                 (GDestroyNotify)g_free);

        return TRUE;
}

static void
user_change_user_name_authorized_cb (Daemon                *daemon,
                                     User                  *user,
Matthias Clasen's avatar
Matthias Clasen committed
920
                                     GDBusMethodInvocation *context,
Matthias Clasen's avatar
Matthias Clasen committed
921 922 923 924 925
                                     gpointer               data)

{
        gchar *name = data;
        gchar *old_name;
926
        g_autoptr(GError) error = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
927
        const gchar *argv[6];
Matthias Clasen's avatar
Matthias Clasen committed
928

929 930
        if (g_strcmp0 (accounts_user_get_user_name (ACCOUNTS_USER (user)), name) != 0) {
                old_name = g_strdup (accounts_user_get_user_name (ACCOUNTS_USER (user)));
931 932
                sys_log (context,
                         "change name of user '%s' (%d) to '%s'",
933 934 935
                         old_name,
                         accounts_user_get_uid (ACCOUNTS_USER (user)),
                         name);
Matthias Clasen's avatar
Matthias Clasen committed
936

937 938 939
                argv[0] = "/usr/sbin/usermod";
                argv[1] = "-l";
                argv[2] = name;
940
                argv[3] = "--";
941
                argv[4] = accounts_user_get_user_name (ACCOUNTS_USER (user));
942
                argv[5] = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
943

944
                if (!spawn_with_login_uid (context, argv, &error)) {
945
                        throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
Matthias Clasen's avatar
Matthias Clasen committed
946 947 948
                        return;
                }

949
                accounts_user_set_user_name (ACCOUNTS_USER (user), name);
Matthias Clasen's avatar
Matthias Clasen committed
950 951 952 953

                move_extra_data (old_name, name);
        }

Matthias Clasen's avatar
Matthias Clasen committed
954
        accounts_user_complete_set_user_name (ACCOUNTS_USER (user), context);
Matthias Clasen's avatar
Matthias Clasen committed
955 956 957
}


Matthias Clasen's avatar
Matthias Clasen committed
958 959 960 961
static gboolean
user_set_user_name (AccountsUser          *auser,
                    GDBusMethodInvocation *context,
                    const gchar           *user_name)
Matthias Clasen's avatar
Matthias Clasen committed
962
{
Matthias Clasen's avatar
Matthias Clasen committed
963
        User *user = (User*)auser;
Matthias Clasen's avatar
Matthias Clasen committed
964 965
        daemon_local_check_auth (user->daemon,
                                 user,
966
                                 "org.freedesktop.accounts.user-administration",
Matthias Clasen's avatar
Matthias Clasen committed
967 968 969 970 971 972 973 974 975
                                 user_change_user_name_authorized_cb,
                                 context,
                                 g_strdup (user_name),
                                 (GDestroyNotify)g_free);

        return TRUE;
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
976 977
user_change_email_authorized_cb (Daemon                *daemon,
                                 User                  *user,
Matthias Clasen's avatar
Matthias Clasen committed
978
                                 GDBusMethodInvocation *context,
Matthias Clasen's avatar
Matthias Clasen committed
979
                                 gpointer               data)
Matthias Clasen's avatar
Matthias Clasen committed
980 981 982 983

{
        gchar *email = data;

984 985
        if (g_strcmp0 (accounts_user_get_email (ACCOUNTS_USER (user)), email) != 0) {
                accounts_user_set_email (ACCOUNTS_USER (user), email);
Matthias Clasen's avatar
Matthias Clasen committed
986 987 988 989

                save_extra_data (user);
        }

990
        accounts_user_complete_set_email (ACCOUNTS_USER (user), context);
Matthias Clasen's avatar
Matthias Clasen committed
991 992 993 994
}



Matthias Clasen's avatar
Matthias Clasen committed
995 996 997 998
static gboolean
user_set_email (AccountsUser          *auser,
                GDBusMethodInvocation *context,
                const gchar           *email)
Matthias Clasen's avatar
Matthias Clasen committed
999
{
Matthias Clasen's avatar
Matthias Clasen committed
1000
        User *user = (User*)auser;
1001
        int uid;
Matthias Clasen's avatar
Matthias Clasen committed
1002 1003
        const gchar *action_id;

1004 1005
        if (!get_caller_uid (context, &uid)) {
                throw_error (context, ERROR_FAILED, "identifying caller failed");
1006
                return TRUE;
1007 1008
        }

1009
        if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
Matthias Clasen's avatar
Matthias Clasen committed
1010 1011
                action_id = "org.freedesktop.accounts.change-own-user-data";
        else
Matthias Clasen's avatar
Matthias Clasen committed
1012
                action_id = "org.freedesktop.accounts.user-administration";
Matthias Clasen's avatar
Matthias Clasen committed
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025

        daemon_local_check_auth (user->daemon,
                                 user,
                                 action_id,
                                 user_change_email_authorized_cb,
                                 context,
                                 g_strdup (email),
                                 (GDestroyNotify)g_free);

        return TRUE;
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
1026 1027
user_change_language_authorized_cb (Daemon                *daemon,
                                    User                  *user,
Matthias Clasen's avatar
Matthias Clasen committed
1028
                                    GDBusMethodInvocation *context,
Matthias Clasen's avatar
Matthias Clasen committed
1029
                                    gpointer               data)
Matthias Clasen's avatar
Matthias Clasen committed
1030 1031 1032 1033

{
        gchar *language = data;

1034 1035
        if (g_strcmp0 (accounts_user_get_language (ACCOUNTS_USER (user)), language) != 0) {
                accounts_user_set_language (ACCOUNTS_USER (user), language);
Matthias Clasen's avatar
Matthias Clasen committed
1036 1037 1038 1039

                save_extra_data (user);
        }

Matthias Clasen's avatar
Matthias Clasen committed
1040
        accounts_user_complete_set_language (ACCOUNTS_USER (user), context);
Matthias Clasen's avatar
Matthias Clasen committed
1041 1042 1043 1044
}



Matthias Clasen's avatar
Matthias Clasen committed
1045 1046 1047 1048
static gboolean
user_set_language (AccountsUser          *auser,
                   GDBusMethodInvocation *context,
                   const gchar           *language)
Matthias Clasen's avatar
Matthias Clasen committed
1049
{
Matthias Clasen's avatar
Matthias Clasen committed
1050
        User *user = (User*)auser;
1051
        int uid;
Matthias Clasen's avatar
Matthias Clasen committed
1052 1053
        const gchar *action_id;

1054 1055
        if (!get_caller_uid (context, &uid)) {
                throw_error (context, ERROR_FAILED, "identifying caller failed");
1056
                return TRUE;
1057 1058
        }

1059
        if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
Matthias Clasen's avatar
Matthias Clasen committed
1060 1061
                action_id = "org.freedesktop.accounts.change-own-user-data";
        else
Matthias Clasen's avatar
Matthias Clasen committed
1062
                action_id = "org.freedesktop.accounts.user-administration";
Matthias Clasen's avatar
Matthias Clasen committed
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074

        daemon_local_check_auth (user->daemon,
                                 user,
                                 action_id,
                                 user_change_language_authorized_cb,
                                 context,
                                 g_strdup (language),
                                 (GDestroyNotify)g_free);

        return TRUE;
}

1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
static void
user_change_session_authorized_cb (Daemon                *daemon,
                                   User                  *user,
                                   GDBusMethodInvocation *context,
                                   gpointer               user_data)

{
        const gchar *session = user_data;

        if (g_strcmp0 (accounts_user_get_session (ACCOUNTS_USER (user)), session) != 0) {
                accounts_user_set_session (ACCOUNTS_USER (user), session);

                save_extra_data (user);
        }

        accounts_user_complete_set_session (ACCOUNTS_USER (user), context);
}

static gboolean
user_set_session (AccountsUser          *auser,
                  GDBusMethodInvocation *context,
                  const gchar           *session)
{
        User *user = (User*)auser;
        int uid;
        const gchar *action_id;

        if (!get_caller_uid (context, &uid)) {
                throw_error (context, ERROR_FAILED, "identifying caller failed");
1104
                return TRUE;
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
        }

        if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
                action_id = "org.freedesktop.accounts.change-own-user-data";
        else
                action_id = "org.freedesktop.accounts.user-administration";

        daemon_local_check_auth (user->daemon,
                                 user,
                                 action_id,
                                 user_change_session_authorized_cb,
                                 context,
                                 g_strdup (session),
                                 (GDestroyNotify) g_free);

        return TRUE;
}

static void
user_change_session_type_authorized_cb (Daemon                *daemon,
                                        User                  *user,
                                        GDBusMethodInvocation *context,
                                        gpointer               user_data)

{
        const gchar *session_type = user_data;

        if (g_strcmp0 (accounts_user_get_session_type (ACCOUNTS_USER (user)), session_type) != 0) {
                accounts_user_set_session_type (ACCOUNTS_USER (user), session_type);

                save_extra_data (user);
        }

        accounts_user_complete_set_session_type (ACCOUNTS_USER (user), context);
}

static gboolean
user_set_session_type (AccountsUser          *auser,
                       GDBusMethodInvocation *context,
                       const gchar           *session_type)
{
        User *user = (User*)auser;
        int uid;
        const gchar *action_id;

        if (!get_caller_uid (context, &uid)) {
                throw_error (context, ERROR_FAILED, "identifying caller failed");
1152
                return TRUE;
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
        }

        if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
                action_id = "org.freedesktop.accounts.change-own-user-data";
        else
                action_id = "org.freedesktop.accounts.user-administration";

        daemon_local_check_auth (user->daemon,
                                 user,
                                 action_id,
                                 user_change_session_type_authorized_cb,
                                 context,
                                 g_strdup (session_type),
                                 (GDestroyNotify) g_free);

        return TRUE;
}