1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
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 Red Hat, Inc.
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.
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.
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
28
#include <sys/types.h>
33
#include <glib/gi18n.h>
34
#include <glib/gstdio.h>
38
#include <gio/gunixoutputstream.h>
40
#include <dbus/dbus-glib.h>
43
#include "um-account-type.h"
47
#define UM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_USER, UmUserClass))
48
#define UM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UM_TYPE_USER))
49
#define UM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), UM_TYPE_USER, UmUserClass))
51
#define MAX_FILE_SIZE 65536
63
guint64 login_frequency;
66
gboolean automatic_login;
67
gboolean system_account;
71
collect_props (const gchar *key,
73
UserProperties *props)
75
gboolean handled = TRUE;
77
if (strcmp (key, "Uid") == 0) {
78
props->uid = g_value_get_uint64 (value);
80
else if (strcmp (key, "UserName") == 0) {
81
props->user_name = g_value_dup_string (value);
83
else if (strcmp (key, "RealName") == 0) {
84
props->real_name = g_value_dup_string (value);
86
else if (strcmp (key, "AccountType") == 0) {
87
props->account_type = g_value_get_int (value);
89
else if (strcmp (key, "Email") == 0) {
90
props->email = g_value_dup_string (value);
92
else if (strcmp (key, "Language") == 0) {
93
props->language = g_value_dup_string (value);
95
else if (strcmp (key, "Location") == 0) {
96
props->location = g_value_dup_string (value);
98
else if (strcmp (key, "LoginFrequency") == 0) {
99
props->login_frequency = g_value_get_uint64 (value);
101
else if (strcmp (key, "IconFile") == 0) {
102
props->icon_file = g_value_dup_string (value);
104
else if (strcmp (key, "Locked") == 0) {
105
props->locked = g_value_get_boolean (value);
107
else if (strcmp (key, "AutomaticLogin") == 0) {
108
props->automatic_login = g_value_get_boolean (value);
110
else if (strcmp (key, "SystemAccount") == 0) {
111
props->system_account = g_value_get_boolean (value);
113
else if (strcmp (key, "PasswordMode") == 0) {
114
props->password_mode = g_value_get_int (value);
116
else if (strcmp (key, "PasswordHint") == 0) {
117
props->password_hint = g_value_dup_string (value);
119
else if (strcmp (key, "HomeDirectory") == 0) {
122
else if (strcmp (key, "Shell") == 0) {
130
g_debug ("unhandled property %s", key);
134
user_properties_free (UserProperties *props)
136
g_free (props->user_name);
137
g_free (props->real_name);
138
g_free (props->password_hint);
139
g_free (props->email);
140
g_free (props->language);
141
g_free (props->location);
142
g_free (props->icon_file);
146
static UserProperties *
147
user_properties_get (DBusGConnection *bus,
148
const gchar *object_path)
150
UserProperties *props;
153
GHashTable *hash_table;
155
props = g_new0 (UserProperties, 1);
157
proxy = dbus_g_proxy_new_for_name (bus,
158
"org.freedesktop.Accounts",
160
"org.freedesktop.DBus.Properties");
162
if (!dbus_g_proxy_call (proxy,
166
"org.freedesktop.Accounts.User",
168
dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
171
g_debug ("Error calling GetAll() when retrieving properties for %s: %s", object_path, error->message);
172
g_error_free (error);
177
g_hash_table_foreach (hash_table, (GHFunc) collect_props, props);
178
g_hash_table_unref (hash_table);
181
g_object_unref (proxy);
189
DBusGConnection *bus;
193
UserProperties *props;
198
typedef struct _UmUserClass
200
GObjectClass parent_class;
208
static guint signals[LAST_SIGNAL] = { 0 };
210
static void um_user_finalize (GObject *object);
212
G_DEFINE_TYPE (UmUser, um_user, G_TYPE_OBJECT)
215
um_user_class_init (UmUserClass *class)
217
GObjectClass *gobject_class;
219
gobject_class = G_OBJECT_CLASS (class);
221
gobject_class->finalize = um_user_finalize;
223
signals[CHANGED] = g_signal_new ("changed",
224
G_TYPE_FROM_CLASS (class),
228
g_cclosure_marshal_VOID__VOID,
234
um_user_init (UmUser *user)
239
um_user_finalize (GObject *object)
243
user = UM_USER (object);
245
dbus_g_connection_unref (user->bus);
246
g_free (user->object_path);
248
if (user->proxy != NULL)
249
g_object_unref (user->proxy);
251
if (user->props != NULL)
252
user_properties_free (user->props);
254
(*G_OBJECT_CLASS (um_user_parent_class)->finalize) (object);
258
um_user_get_uid (UmUser *user)
260
g_return_val_if_fail (UM_IS_USER (user), -1);
262
return user->props->uid;
266
um_user_get_real_name (UmUser *user)
268
g_return_val_if_fail (UM_IS_USER (user), NULL);
270
return user->props->real_name;
274
um_user_get_display_name (UmUser *user)
276
g_return_val_if_fail (UM_IS_USER (user), NULL);
278
return user->display_name ? user->display_name
279
: user->props->real_name;
283
um_user_get_user_name (UmUser *user)
285
g_return_val_if_fail (UM_IS_USER (user), NULL);
287
return user->props->user_name;
291
um_user_get_account_type (UmUser *user)
293
g_return_val_if_fail (UM_IS_USER (user), UM_ACCOUNT_TYPE_STANDARD);
295
return user->props->account_type;
299
um_user_get_login_frequency (UmUser *user)
301
g_return_val_if_fail (UM_IS_USER (user), 0);
303
return user->props->login_frequency;
307
um_user_collate (UmUser *user1,
315
g_return_val_if_fail (UM_IS_USER (user1), 0);
316
g_return_val_if_fail (UM_IS_USER (user2), 0);
318
num1 = user1->props->login_frequency;
319
num2 = user2->props->login_frequency;
328
/* if login frequency is equal try names */
329
if (user1->props->real_name != NULL) {
330
str1 = user1->props->real_name;
332
str1 = user1->props->user_name;
335
if (user2->props->real_name != NULL) {
336
str2 = user2->props->real_name;
338
str2 = user2->props->user_name;
341
if (str1 == NULL && str2 != NULL) {
345
if (str1 != NULL && str2 == NULL) {
349
if (str1 == NULL && str2 == NULL) {
353
return g_utf8_collate (str1, str2);
357
check_user_file (const char *filename,
358
gssize max_file_size)
360
struct stat fileinfo;
362
if (max_file_size < 0) {
363
max_file_size = G_MAXSIZE;
366
/* Exists/Readable? */
367
if (stat (filename, &fileinfo) < 0) {
368
g_debug ("File does not exist");
372
/* Is a regular file */
373
if (G_UNLIKELY (!S_ISREG (fileinfo.st_mode))) {
374
g_debug ("File is not a regular file");
378
/* Size is kosher? */
379
if (G_UNLIKELY (fileinfo.st_size > max_file_size)) {
380
g_debug ("File is too large");
387
static cairo_surface_t *
388
surface_from_pixbuf (GdkPixbuf *pixbuf)
390
cairo_surface_t *surface;
393
surface = cairo_image_surface_create (gdk_pixbuf_get_has_alpha (pixbuf) ?
394
CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
395
gdk_pixbuf_get_width (pixbuf),
396
gdk_pixbuf_get_height (pixbuf));
397
cr = cairo_create (surface);
398
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
406
* go_cairo_convert_data_to_pixbuf:
407
* @src: a pointer to pixel data in cairo format
408
* @dst: a pointer to pixel data in pixbuf format
409
* @width: image width
410
* @height: image height
411
* @rowstride: data rowstride
413
* Converts the pixel data stored in @src in CAIRO_FORMAT_ARGB32 cairo format
414
* to GDK_COLORSPACE_RGB pixbuf format and move them
415
* to @dst. If @src == @dst, pixel are converted in place.
419
go_cairo_convert_data_to_pixbuf (unsigned char *dst,
420
unsigned char const *src,
427
unsigned char a, b, c;
429
g_return_if_fail (dst != NULL);
431
#define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END
433
if (src == dst || src == NULL) {
434
for (i = 0; i < height; i++) {
435
for (j = 0; j < width; j++) {
436
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
437
MULT(a, dst[2], dst[3], t);
438
MULT(b, dst[1], dst[3], t);
439
MULT(c, dst[0], dst[3], t);
444
MULT(a, dst[1], dst[0], t);
445
MULT(b, dst[2], dst[0], t);
446
MULT(c, dst[3], dst[0], t);
454
dst += rowstride - width * 4;
457
for (i = 0; i < height; i++) {
458
for (j = 0; j < width; j++) {
459
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
460
MULT(dst[0], src[2], src[3], t);
461
MULT(dst[1], src[1], src[3], t);
462
MULT(dst[2], src[0], src[3], t);
465
MULT(dst[0], src[1], src[0], t);
466
MULT(dst[1], src[2], src[0], t);
467
MULT(dst[2], src[3], src[0], t);
473
src += rowstride - width * 4;
474
dst += rowstride - width * 4;
481
cairo_to_pixbuf (guint8 *src_data,
482
GdkPixbuf *dst_pixbuf)
490
w = gdk_pixbuf_get_width (dst_pixbuf);
491
h = gdk_pixbuf_get_height (dst_pixbuf);
492
rowstride = gdk_pixbuf_get_rowstride (dst_pixbuf);
494
dst = gdk_pixbuf_get_pixels (dst_pixbuf);
497
go_cairo_convert_data_to_pixbuf (dst, src, w, h, rowstride);
501
frame_pixbuf (GdkPixbuf *source)
505
cairo_surface_t *surface;
515
w = gdk_pixbuf_get_width (source) + frame_width * 2;
516
h = gdk_pixbuf_get_height (source) + frame_width * 2;
519
dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
524
rowstride = gdk_pixbuf_get_rowstride (dest);
527
data = g_new0 (guint8, h * rowstride);
529
surface = cairo_image_surface_create_for_data (data,
534
cr = cairo_create (surface);
535
cairo_surface_destroy (surface);
538
cairo_rectangle (cr, 0, 0, w, h);
539
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
542
rounded_rectangle (cr, 1.0, 0.5, 0.5, radius, w - 1, h - 1);
543
cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
544
cairo_fill_preserve (cr);
546
surface = surface_from_pixbuf (source);
547
cairo_set_source_surface (cr, surface, frame_width, frame_width);
549
cairo_surface_destroy (surface);
551
cairo_to_pixbuf (data, dest);
560
um_user_render_icon (UmUser *user,
569
g_return_val_if_fail (UM_IS_USER (user), NULL);
570
g_return_val_if_fail (icon_size > 12, NULL);
573
if (user->props->icon_file) {
574
res = check_user_file (user->props->icon_file,
577
pixbuf = gdk_pixbuf_new_from_file_at_size (user->props->icon_file,
587
if (pixbuf != NULL) {
592
pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
596
GTK_ICON_LOOKUP_FORCE_SIZE,
599
g_warning ("%s", error->message);
600
g_error_free (error);
605
if (pixbuf != NULL && with_frame) {
606
framed = frame_pixbuf (pixbuf);
607
if (framed != NULL) {
608
g_object_unref (pixbuf);
617
um_user_get_email (UmUser *user)
619
g_return_val_if_fail (UM_IS_USER (user), NULL);
621
return user->props->email;
625
um_user_get_language (UmUser *user)
627
g_return_val_if_fail (UM_IS_USER (user), NULL);
629
if (*user->props->language == '\0')
631
return user->props->language;
635
um_user_get_location (UmUser *user)
637
g_return_val_if_fail (UM_IS_USER (user), NULL);
639
return user->props->location;
643
um_user_get_password_mode (UmUser *user)
645
g_return_val_if_fail (UM_IS_USER (user), UM_PASSWORD_MODE_NONE);
647
return user->props->password_mode;
651
um_user_get_password_hint (UmUser *user)
653
g_return_val_if_fail (UM_IS_USER (user), NULL);
655
return user->props->password_hint;
659
um_user_get_icon_file (UmUser *user)
661
g_return_val_if_fail (UM_IS_USER (user), NULL);
663
return user->props->icon_file;
667
um_user_get_locked (UmUser *user)
669
g_return_val_if_fail (UM_IS_USER (user), FALSE);
671
return user->props->locked;
674
um_user_get_automatic_login (UmUser *user)
676
g_return_val_if_fail (UM_IS_USER (user), FALSE);
678
return user->props->automatic_login;
682
um_user_is_system_account (UmUser *user)
684
g_return_val_if_fail (UM_IS_USER (user), FALSE);
686
return user->props->system_account;
690
um_user_get_object_path (UmUser *user)
692
g_return_val_if_fail (UM_IS_USER (user), NULL);
694
return user->object_path;
698
update_info (UmUser *user)
700
UserProperties *props;
702
props = user_properties_get (user->bus, user->object_path);
704
if (user->props != NULL)
705
user_properties_free (user->props);
715
changed_handler (DBusGProxy *proxy,
718
UmUser *user = UM_USER (data);
720
if (update_info (user)) {
721
if (user->display_name != NULL) {
722
um_user_show_full_display_name (user);
725
g_signal_emit (user, signals[CHANGED], 0);
730
um_user_new_from_object_path (const gchar *object_path)
735
user = (UmUser *)g_object_new (UM_TYPE_USER, NULL);
736
user->object_path = g_strdup (object_path);
739
user->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
740
if (user->bus == NULL) {
741
g_warning ("Couldn't connect to system bus: %s", error->message);
745
user->proxy = dbus_g_proxy_new_for_name (user->bus,
746
"org.freedesktop.Accounts",
748
"org.freedesktop.Accounts.User");
749
dbus_g_proxy_set_default_timeout (user->proxy, INT_MAX);
750
dbus_g_proxy_add_signal (user->proxy, "Changed", G_TYPE_INVALID);
752
dbus_g_proxy_connect_signal (user->proxy, "Changed",
753
G_CALLBACK (changed_handler), user, NULL);
755
if (!update_info (user))
761
g_object_unref (user);
766
um_user_set_email (UmUser *user,
769
GError *error = NULL;
771
if (!dbus_g_proxy_call (user->proxy,
774
G_TYPE_STRING, email,
777
g_warning ("SetEmail call failed: %s", error->message);
778
g_error_free (error);
784
um_user_set_language (UmUser *user,
785
const gchar *language)
787
GError *error = NULL;
789
if (!dbus_g_proxy_call (user->proxy,
792
G_TYPE_STRING, language,
795
g_warning ("SetLanguage call failed: %s", error->message);
796
g_error_free (error);
802
um_user_set_location (UmUser *user,
803
const gchar *location)
805
GError *error = NULL;
807
if (!dbus_g_proxy_call (user->proxy,
810
G_TYPE_STRING, location,
813
g_warning ("SetLocation call failed: %s", error->message);
814
g_error_free (error);
820
um_user_set_user_name (UmUser *user,
821
const gchar *user_name)
823
GError *error = NULL;
825
if (!dbus_g_proxy_call (user->proxy,
828
G_TYPE_STRING, user_name,
831
g_warning ("SetUserName call failed: %s", error->message);
832
g_error_free (error);
838
um_user_set_real_name (UmUser *user,
839
const gchar *real_name)
841
GError *error = NULL;
843
if (!dbus_g_proxy_call (user->proxy,
846
G_TYPE_STRING, real_name,
849
g_warning ("SetRealName call failed: %s", error->message);
850
g_error_free (error);
856
um_user_set_icon_file (UmUser *user,
857
const gchar *icon_file)
859
GError *error = NULL;
861
if (!dbus_g_proxy_call (user->proxy,
864
G_TYPE_STRING, icon_file,
867
g_warning ("SetIconFile call failed: %s", error->message);
868
g_error_free (error);
874
um_user_set_icon_data (UmUser *user,
879
GOutputStream *stream;
882
path = g_build_filename (g_get_tmp_dir (), "usericonXXXXXX", NULL);
883
fd = g_mkstemp (path);
886
g_warning ("failed to create temporary file for image data");
891
stream = g_unix_output_stream_new (fd, TRUE);
894
if (!gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error, NULL)) {
895
g_warning ("failed to save image: %s", error->message);
896
g_error_free (error);
897
g_object_unref (stream);
901
g_object_unref (stream);
903
um_user_set_icon_file (user, path);
905
/* if we ever make the dbus call async, the g_remove call needs
906
* to wait for its completion
914
um_user_set_account_type (UmUser *user,
917
GError *error = NULL;
919
if (!dbus_g_proxy_call (user->proxy,
922
G_TYPE_INT, account_type,
925
g_warning ("SetAccountType call failed: %s", error->message);
926
g_error_free (error);
932
salt_char (GRand *rand)
934
gchar salt[] = "ABCDEFGHIJKLMNOPQRSTUVXYZ"
935
"abcdefghijklmnopqrstuvxyz"
938
return salt[g_rand_int_range (rand, 0, G_N_ELEMENTS (salt))];
942
make_crypted (const gchar *plain)
949
rand = g_rand_new ();
950
salt = g_string_sized_new (21);
953
g_string_append (salt, "$6$");
954
for (i = 0; i < 16; i++) {
955
g_string_append_c (salt, salt_char (rand));
957
g_string_append_c (salt, '$');
959
result = g_strdup (crypt (plain, salt->str));
961
g_string_free (salt, TRUE);
968
um_user_set_password (UmUser *user,
970
const gchar *password,
973
GError *error = NULL;
976
if (password_mode == 0) {
977
crypted = make_crypted (password);
978
if (!dbus_g_proxy_call (user->proxy,
981
G_TYPE_STRING, crypted,
985
g_warning ("SetPassword call failed: %s", error->message);
986
g_error_free (error);
988
memset (crypted, 0, strlen (crypted));
991
else if (password_mode == 3 || password_mode == 4) {
992
if (!dbus_g_proxy_call (user->proxy,
995
G_TYPE_BOOLEAN, (password_mode == 3),
998
g_warning ("SetLocked call failed: %s", error->message);
999
g_error_free (error);
1003
if (!dbus_g_proxy_call (user->proxy,
1006
G_TYPE_INT, password_mode,
1009
g_warning ("SetPasswordMode call failed: %s", error->message);
1010
g_error_free (error);
1016
um_user_is_logged_in (UmUser *user)
1023
proxy = dbus_g_proxy_new_for_name (user->bus,
1024
"org.freedesktop.ConsoleKit",
1025
"/org/freedesktop/ConsoleKit/Manager",
1026
"org.freedesktop.ConsoleKit.Manager");
1030
if (!dbus_g_proxy_call (proxy,
1031
"GetSessionsForUnixUser",
1033
G_TYPE_UINT, um_user_get_uid (user),
1035
dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &array,
1037
g_warning ("GetSessionsForUnixUser failed: %s", error->message);
1038
g_error_free (error);
1042
n_sessions = array->len;
1044
g_ptr_array_foreach (array, (GFunc)g_free, NULL);
1045
g_ptr_array_free (array, TRUE);
1047
g_object_unref (proxy);
1049
return n_sessions > 0;
1054
um_user_set_automatic_login (UmUser *user,
1057
GError *error = NULL;
1059
if (!dbus_g_proxy_call (user->proxy,
1060
"SetAutomaticLogin",
1062
G_TYPE_BOOLEAN, enabled,
1065
g_warning ("SetAutomaticLogin call failed: %s", error->message);
1066
g_error_free (error);
1071
um_user_show_full_display_name (UmUser *user)
1075
g_return_if_fail (UM_IS_USER (user));
1077
if (user->props->real_name != NULL) {
1078
uniq_name = g_strdup_printf ("%s (%s)",
1079
user->props->real_name,
1080
user->props->user_name);
1085
if (uniq_name && g_strcmp0 (uniq_name, user->display_name) != 0) {
1086
g_free (user->display_name);
1087
user->display_name = uniq_name;
1095
um_user_show_short_display_name (UmUser *user)
1097
g_return_if_fail (UM_IS_USER (user));
1099
if (user->display_name) {
1100
g_free (user->display_name);
1101
user->display_name = NULL;