2
* Copyright (C) 2002 Diego Gonzalez
3
* Copyright (C) 2006 Johannes H. Jensen
5
* Written by: Diego Gonzalez <diego@pemas.net>
6
* Modified by: Johannes H. Jensen <joh@deworks.net>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2, or (at your option)
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23
* Parts of this code come from Gnome-System-Tools.
30
/* Are all of these needed? */
31
#include <gdk/gdkkeysyms.h>
40
#include <sys/types.h>
44
#include "capplet-util.h"
45
#include "eel-alert-dialog.h"
49
PASSWD_STATE_NONE, /* Passwd is not asking for anything */
50
PASSWD_STATE_AUTH, /* Passwd is asking for our current password */
51
PASSWD_STATE_NEW, /* Passwd is asking for our new password */
52
PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */
53
PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */
59
/* Commonly used widgets */
60
GtkEntry *current_password;
61
GtkEntry *new_password;
62
GtkEntry *retyped_password;
63
GtkImage *dialog_image;
64
GtkLabel *status_label;
66
/* Whether we have authenticated */
67
gboolean authenticated;
69
/* Communication with the passwd program */
72
GIOChannel *backend_stdin;
73
GIOChannel *backend_stdout;
75
GQueue *backend_stdin_queue; /* Write queue to backend_stdin */
78
guint backend_child_watch_id; /* g_child_watch_add (PID) */
79
guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */
81
/* State of the passwd program */
82
PasswdState backend_state;
86
/* Buffer size for backend output */
92
#define PASSDLG_ERROR (gnome_about_me_password_error_quark())
94
GQuark gnome_about_me_password_error_quark(void)
99
q = g_quark_from_static_string("gnome_about_me_password_error");
108
PASSDLG_ERROR_NEW_PASSWORD_EMPTY,
109
PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY,
110
PASSDLG_ERROR_PASSWORDS_NOT_EQUAL,
111
PASSDLG_ERROR_BACKEND, /* Backend error */
112
PASSDLG_ERROR_USER, /* Generic user error */
113
PASSDLG_ERROR_FAILED /* Fatal failure, error->message should explain */
124
stop_passwd (PasswordDialog *pdialog);
127
free_passwd_resources (PasswordDialog *pdialog);
130
io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog);
133
passdlg_set_auth_state (PasswordDialog *pdialog, gboolean state);
136
passdlg_set_status (PasswordDialog *pdialog, gchar *msg);
139
passdlg_set_busy (PasswordDialog *pdialog, gboolean busy);
142
passdlg_clear (PasswordDialog *pdialog);
145
passdlg_refresh_password_state (PasswordDialog *pdialog);
152
* Spawning and closing of backend {{
157
child_watch_cb (GPid pid, gint status, PasswordDialog *pdialog)
159
if (WIFEXITED (status)) {
160
if (WEXITSTATUS (status) >= 255) {
161
g_warning (_("Child exited unexpectedly"));
165
free_passwd_resources (pdialog);
168
/* Spawn passwd backend
169
* Returns: TRUE on success, FALSE otherwise and sets error appropriately */
171
spawn_passwd (PasswordDialog *pdialog, GError **error)
175
gint my_stdin, my_stdout, my_stderr;
177
argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */
180
envp[0] = NULL; /* If we pass an empty array as the environment,
181
* will the childs environment be empty, and the
182
* locales set to the C default? From the manual:
183
* "If envp is NULL, the child inherits its
184
* parent'senvironment."
185
* If I'm wrong here, we somehow have to set
189
if (!g_spawn_async_with_pipes (NULL, /* Working directory */
190
argv, /* Argument vector */
191
envp, /* Environment */
192
G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */
193
NULL, /* Child setup */
194
NULL, /* Data to child setup */
195
&pdialog->backend_pid, /* PID */
196
&my_stdin, /* Stdin */
197
&my_stdout, /* Stdout */
198
&my_stderr, /* Stderr */
199
error)) { /* GError */
201
/* An error occured */
202
free_passwd_resources (pdialog);
208
if (dup2 (my_stderr, my_stdout) == -1) {
212
PASSDLG_ERROR_BACKEND,
216
stop_passwd (pdialog);
221
/* Open IO Channels */
222
pdialog->backend_stdin = g_io_channel_unix_new (my_stdin);
223
pdialog->backend_stdout = g_io_channel_unix_new (my_stdout);
225
/* Set raw encoding */
226
/* Set nonblocking mode */
227
if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
228
g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
229
g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
230
g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {
233
stop_passwd (pdialog);
237
/* Turn off buffering */
238
g_io_channel_set_buffered (pdialog->backend_stdin, FALSE);
239
g_io_channel_set_buffered (pdialog->backend_stdout, FALSE);
241
/* Add IO Channel watcher */
242
pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout,
244
(GIOFunc) io_watch_stdout, pdialog);
246
/* Add child watcher */
247
pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog);
254
/* Stop passwd backend */
256
stop_passwd (PasswordDialog *pdialog)
258
/* This is the standard way of returning from the dialog with passwd.
259
* If we return this way we can safely kill passwd as it has completed
263
if (pdialog->backend_pid != -1) {
264
kill (pdialog->backend_pid, 9);
267
/* We must run free_passwd_resources here and not let our child
268
* watcher do it, since it will access invalid memory after the
269
* dialog has been closed and cleaned up.
271
* If we had more than a single thread we'd need to remove
272
* the child watch before trying to kill the child.
274
free_passwd_resources (pdialog);
277
/* Clean up passwd resources */
279
free_passwd_resources (PasswordDialog *pdialog)
281
GError *error = NULL;
283
/* Remove the child watcher */
284
if (pdialog->backend_child_watch_id != 0) {
286
g_source_remove (pdialog->backend_child_watch_id);
288
pdialog->backend_child_watch_id = 0;
292
/* Close IO channels (internal file descriptors are automatically closed) */
293
if (pdialog->backend_stdin != NULL) {
295
if (g_io_channel_shutdown (pdialog->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) {
296
g_warning (_("Could not shutdown backend_stdin IO channel: %s"), error->message);
297
g_error_free (error);
301
g_io_channel_unref (pdialog->backend_stdin);
303
pdialog->backend_stdin = NULL;
306
if (pdialog->backend_stdout != NULL) {
308
if (g_io_channel_shutdown (pdialog->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) {
309
g_warning (_("Could not shutdown backend_stdout IO channel: %s"), error->message);
310
g_error_free (error);
314
g_io_channel_unref (pdialog->backend_stdout);
316
pdialog->backend_stdout = NULL;
319
/* Remove IO watcher */
320
if (pdialog->backend_stdout_watch_id != 0) {
322
g_source_remove (pdialog->backend_stdout_watch_id);
324
pdialog->backend_stdout_watch_id = 0;
328
if (pdialog->backend_pid != -1) {
330
g_spawn_close_pid (pdialog->backend_pid);
332
pdialog->backend_pid = -1;
335
/* Clear backend state */
336
pdialog->backend_state = PASSWD_STATE_NONE;
340
* }} Spawning and closing of backend
344
* Backend communication code {{
347
/* Write the first element of queue through channel */
349
io_queue_pop (GQueue *queue, GIOChannel *channel)
353
GError *error = NULL;
355
buf = g_queue_pop_head (queue);
359
if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) {
360
g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message);
361
g_error_free (error);
368
/* Goes through the argument list, checking if one of them occurs in str
369
* Returns: TRUE as soon as an element is found to match, FALSE otherwise */
371
is_string_complete (gchar *str, ...)
376
if (strlen (str) == 0) {
382
while ((arg = va_arg (ap, char *)) != NULL) {
383
if (g_strrstr (str, arg) != NULL) {
394
/* Authentication attempt succeeded. Update the GUI accordingly. */
396
authenticated_user (PasswordDialog *pdialog)
398
pdialog->backend_state = PASSWD_STATE_NEW;
400
if (pdialog->authenticated) {
401
/* This is a re-authentication
402
* It succeeded, so pop our new password from the queue */
403
io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin);
406
/* Update UI state */
407
passdlg_set_auth_state (pdialog, TRUE);
408
passdlg_set_status (pdialog, _("Authenticated!"));
410
/* Check to see if the passwords are valid
411
* (They might be non-empty if the user had to re-authenticate,
412
* and thus we need to enable the change-password-button) */
413
passdlg_refresh_password_state (pdialog);
417
* IO watcher for stdout, called whenever there is data to read from the backend.
418
* This is where most of the actual IO handling happens.
421
io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog)
423
static GString *str = NULL; /* Persistent buffer */
425
gchar buf[BUFSIZE]; /* Temporary buffer */
427
GError *error = NULL;
429
gchar *msg = NULL; /* Status error message */
432
gboolean reinit = FALSE;
434
/* Initialize buffer */
436
str = g_string_new ("");
439
dialog = pdialog->ui;
441
if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &error) != G_IO_STATUS_NORMAL) {
442
g_warning ("IO Channel read error: %s", error->message);
443
g_error_free (error);
448
str = g_string_append_len (str, buf, bytes_read);
450
/* In which state is the backend? */
451
switch (pdialog->backend_state) {
452
case PASSWD_STATE_AUTH:
453
/* Passwd is asking for our current password */
455
if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) {
456
/* Which response did we get? */
457
passdlg_set_busy (pdialog, FALSE);
459
if (g_strrstr (str->str, "assword: ") != NULL) {
460
/* Authentication successful */
462
authenticated_user (pdialog);
465
/* Authentication failed */
467
if (pdialog->authenticated) {
468
/* This is a re-auth, and it failed.
469
* The password must have been changed in the meantime!
470
* Ask the user to re-authenticate
473
/* Update status message and auth state */
474
passdlg_set_status (pdialog, _("Your password has been changed since you initially authenticated! Please re-authenticate."));
476
passdlg_set_status (pdialog, _("That password was incorrect."));
479
/* Focus current password */
480
gtk_widget_grab_focus (GTK_WIDGET (pdialog->current_password));
486
case PASSWD_STATE_NEW:
487
/* Passwd is asking for our new password */
489
if (is_string_complete (str->str, "assword: ", NULL)) {
490
/* Advance to next state */
491
pdialog->backend_state = PASSWD_STATE_RETYPE;
493
/* Pop retyped password from queue and into IO channel */
494
io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin);
499
case PASSWD_STATE_RETYPE:
500
/* Passwd is asking for our retyped new password */
502
if (is_string_complete (str->str, "successfully",
507
"simpl", /* catches both simple and simplistic */
516
"1 numeric or special",
520
/* What response did we get? */
521
passdlg_set_busy (pdialog, FALSE);
523
if (g_strrstr (str->str, "successfully") != NULL) {
526
passdlg_clear (pdialog);
527
passdlg_set_status (pdialog, _("Your password has been changed."));
531
/* Focus new password */
532
gtk_widget_grab_focus (GTK_WIDGET (pdialog->new_password));
534
if (g_strrstr (str->str, "recovered") != NULL) {
535
/* What does this indicate?
536
* "Authentication information cannot be recovered?" from libpam? */
537
msg = g_strdup_printf (_("System error: %s."), str->str);
538
} else if (g_strrstr (str->str, "short") != NULL ||
539
g_strrstr (str->str, "longer") != NULL) {
540
msg = g_strdup (_("The password is too short."));
541
} else if (g_strrstr (str->str, "palindrome") != NULL ||
542
g_strrstr (str->str, "simpl") != NULL ||
543
g_strrstr (str->str, "dictionary") != NULL) {
544
msg = g_strdup (_("The password is too simple."));
545
} else if (g_strrstr (str->str, "similar") != NULL ||
546
g_strrstr (str->str, "different") != NULL ||
547
g_strrstr (str->str, "case") != NULL ||
548
g_strrstr (str->str, "wrapped") != NULL) {
549
msg = g_strdup (_("The old and new passwords are too similar."));
550
} else if (g_strrstr (str->str, "1 numeric or special") != NULL) {
551
msg = g_strdup (_("The new password must contain numeric or special character(s)."));
552
} else if (g_strrstr (str->str, "unchanged") != NULL ||
553
g_strrstr (str->str, "match") != NULL) {
554
msg = g_strdup (_("The old and new passwords are the same."));
555
} else if (g_strrstr (str->str, "recent") != NULL) {
556
msg = g_strdup (_("The new password has already been used recently."));
557
} else if (g_strrstr (str->str, "failure") != NULL) {
558
/* Authentication failure */
559
msg = g_strdup (_("Your password has been changed since you initially authenticated! Please re-authenticate."));
561
passdlg_set_auth_state (pdialog, FALSE);
568
/* An error occured! */
569
passdlg_set_status (pdialog, msg);
572
/* At this point, passwd might have exited, in which case
573
* child_watch_cb should clean up for us and remove this watcher.
574
* On some error conditions though, passwd just re-prompts us
575
* for our new password. */
577
pdialog->backend_state = PASSWD_STATE_ERR;
580
/* child_watch_cb should clean up for us now */
583
case PASSWD_STATE_NONE:
584
/* Passwd is not asking for anything yet */
585
if (is_string_complete (str->str, "assword: ", NULL)) {
587
/* If the user does not have a password set,
588
* passwd will immediately ask for the new password,
589
* so skip the AUTH phase */
590
if (is_string_complete (str->str, "new", "New", NULL)) {
593
pdialog->backend_state = PASSWD_STATE_NEW;
595
passdlg_set_busy (pdialog, FALSE);
596
authenticated_user (pdialog);
598
/* since passwd didn't ask for our old password
599
* in this case, simply remove it from the queue */
600
pw = g_queue_pop_head (pdialog->backend_stdin_queue);
604
pdialog->backend_state = PASSWD_STATE_AUTH;
606
/* Pop the IO queue, i.e. send current password */
607
io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin);
614
/* Passwd has returned an error */
620
g_string_free (str, TRUE);
624
/* Continue calling us */
629
* }} Backend communication code
632
/* Adds the current password to the IO queue */
634
authenticate (PasswordDialog *pdialog)
638
s = g_strdup_printf ("%s\n", gtk_entry_get_text (pdialog->current_password));
640
g_queue_push_tail (pdialog->backend_stdin_queue, s);
643
/* Adds the new password twice to the IO queue */
645
update_password (PasswordDialog *pdialog)
649
s = g_strdup_printf ("%s\n", gtk_entry_get_text (pdialog->new_password));
651
g_queue_push_tail (pdialog->backend_stdin_queue, s);
652
/* We need to allocate new space because io_queue_pop() g_free()s
653
* every element of the queue after it's done */
654
g_queue_push_tail (pdialog->backend_stdin_queue, g_strdup (s));
657
/* Sets dialog busy state according to busy
660
* Sets the cursor to busy
661
* Disables the interface to prevent that the user interferes
662
* Reverts all this when non-busy
664
* Note that this function takes into account the
665
* authentication state of the dialog. So setting the
666
* dialog to busy and then back to normal should leave
667
* the dialog unchanged.
670
passdlg_set_busy (PasswordDialog *pdialog, gboolean busy)
674
GdkCursor *cursor = NULL;
677
dialog = pdialog->ui;
680
toplevel = WID ("change-password");
681
display = gtk_widget_get_display (toplevel);
683
cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
686
gdk_window_set_cursor (gtk_widget_get_window (toplevel), cursor);
687
gdk_display_flush (display);
690
gdk_cursor_unref (cursor);
693
/* Disable/Enable UI */
694
if (pdialog->authenticated) {
695
/* Authenticated state */
697
/* Enable/disable new password section */
698
g_object_set (pdialog->new_password, "sensitive", !busy, NULL);
699
g_object_set (pdialog->retyped_password, "sensitive", !busy, NULL);
700
g_object_set (WID ("new-password-label"), "sensitive", !busy, NULL);
701
g_object_set (WID ("retyped-password-label"), "sensitive", !busy, NULL);
703
/* Enable/disable change password button */
704
g_object_set (WID ("change-password-button"), "sensitive", !busy, NULL);
707
/* Not-authenticated state */
709
/* Enable/disable auth section state */
710
g_object_set (pdialog->current_password, "sensitive", !busy, NULL);
711
g_object_set (WID ("authenticate-button"), "sensitive", !busy, NULL);
712
g_object_set (WID ("current-password-label"), "sensitive", !busy, NULL);
716
/* Launch an error dialog */
718
passdlg_error_dialog (GtkWindow *parent, const gchar *title,
719
const gchar *msg, const gchar *details)
723
dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL,
724
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
727
gtk_window_set_title (GTK_WINDOW (dialog), title);
730
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
732
gtk_dialog_run (GTK_DIALOG (dialog));
733
gtk_widget_destroy (dialog);
736
/* Set authenticated state of dialog according to state
738
* When in authenticated state:
739
* Disables authentication-part of interface
740
* Enables new-password-part of interface
741
* When in not-authenticated state:
742
* Enables authentication-part of interface
743
* Disables new-password-part of interface
744
* Disables the change-password-button
747
passdlg_set_auth_state (PasswordDialog *pdialog, gboolean state)
751
dialog = pdialog->ui;
753
/* Widgets which require a not-authenticated state to be accessible */
754
g_object_set (pdialog->current_password, "sensitive", !state, NULL);
755
g_object_set (WID ("current-password-label"), "sensitive", !state, NULL);
756
g_object_set (WID ("authenticate-button"), "sensitive", !state, NULL);
758
/* Widgets which require authentication to be accessible */
759
g_object_set (pdialog->new_password, "sensitive", state, NULL);
760
g_object_set (pdialog->retyped_password, "sensitive", state, NULL);
761
g_object_set (WID ("new-password-label"), "sensitive", state, NULL);
762
g_object_set (WID ("retyped-password-label"), "sensitive", state, NULL);
765
/* Disable change-password-button when in not-authenticated state */
766
g_object_set (WID ("change-password-button"), "sensitive", FALSE, NULL);
769
pdialog->authenticated = state;
772
/* Authenticated state */
774
/* Focus new password */
775
gtk_widget_grab_focus (GTK_WIDGET (pdialog->new_password));
777
/* Set open lock image */
778
gtk_image_set_from_icon_name (GTK_IMAGE (pdialog->dialog_image), "changes-allow", GTK_ICON_SIZE_DIALOG);
780
/* Not authenticated state */
782
/* Focus current password */
783
gtk_widget_grab_focus (GTK_WIDGET (pdialog->current_password));
785
/* Set closed lock image */
786
gtk_image_set_from_icon_name (GTK_IMAGE (pdialog->dialog_image), "changes-prevent", GTK_ICON_SIZE_DIALOG);
790
/* Set status field message */
792
passdlg_set_status (PasswordDialog *pdialog, gchar *msg)
794
g_object_set (pdialog->status_label, "label", msg, NULL);
797
/* Clear dialog (except the status message) */
799
passdlg_clear (PasswordDialog *pdialog)
801
/* Set non-authenticated state */
802
passdlg_set_auth_state (pdialog, FALSE);
804
/* Clear password entries */
805
gtk_entry_set_text (pdialog->current_password, "");
806
gtk_entry_set_text (pdialog->new_password, "");
807
gtk_entry_set_text (pdialog->retyped_password, "");
810
/* Start backend and handle errors
811
* If backend is already running, stop it
812
* If an error occurs, show error dialog */
814
passdlg_spawn_passwd (PasswordDialog *pdialog)
816
GError *error = NULL;
819
/* If backend is running, stop it */
820
stop_passwd (pdialog);
823
if (!spawn_passwd (pdialog, &error)) {
824
GtkWidget *parent = GTK_WIDGET (gtk_builder_get_object (pdialog->ui, "change-password"));
826
/* translators: Unable to launch <program>: <error message> */
827
details = g_strdup_printf (_("Unable to launch %s: %s"),
828
"/usr/bin/passwd", error->message);
830
passdlg_error_dialog (GTK_WINDOW (parent),
831
_("Unable to launch backend"),
832
_("A system error has occurred"),
836
g_error_free (error);
844
/* Called when the "Authenticate" button is clicked */
846
passdlg_authenticate (GtkButton *button, PasswordDialog *pdialog)
848
/* Set busy as this can be a long process */
849
passdlg_set_busy (pdialog, TRUE);
851
/* Update status message */
852
passdlg_set_status (pdialog, _("Checking password..."));
855
if (!passdlg_spawn_passwd (pdialog)) {
856
passdlg_set_busy (pdialog, FALSE);
860
authenticate (pdialog);
862
/* Our IO watcher should now handle the rest */
865
/* Validate passwords
867
* PASSDLG_ERROR_NONE (0) if passwords are valid
868
* PASSDLG_ERROR_NEW_PASSWORD_EMPTY
869
* PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY
870
* PASSDLG_ERROR_PASSWORDS_NOT_EQUAL
873
passdlg_validate_passwords (PasswordDialog *pdialog)
876
const gchar *new_password, *retyped_password;
879
dialog = pdialog->ui;
881
new_password = gtk_entry_get_text (pdialog->new_password);
882
retyped_password = gtk_entry_get_text (pdialog->retyped_password);
884
nlen = g_utf8_strlen (new_password, -1);
885
rlen = g_utf8_strlen (retyped_password, -1);
888
/* New password empty */
889
return PASSDLG_ERROR_NEW_PASSWORD_EMPTY;
893
/* Retyped password empty */
894
return PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY;
897
if (nlen != rlen || strncmp (new_password, retyped_password, nlen) != 0) {
898
/* Passwords not equal */
899
return PASSDLG_ERROR_PASSWORDS_NOT_EQUAL;
903
return PASSDLG_ERROR_NONE;
906
/* Refresh the valid password UI state, i.e. re-validate
907
* and enable/disable the Change Password button.
908
* Returns: Return value of passdlg_validate_passwords */
910
passdlg_refresh_password_state (PasswordDialog *pdialog)
914
gboolean valid = FALSE;
916
dialog = pdialog->ui;
918
ret = passdlg_validate_passwords (pdialog);
920
if (ret == PASSDLG_ERROR_NONE) {
924
g_object_set (WID ("change-password-button"), "sensitive", valid, NULL);
929
/* Called whenever any of the new password fields have changed */
931
passdlg_check_password (GtkEntry *entry, PasswordDialog *pdialog)
935
ret = passdlg_refresh_password_state (pdialog);
938
case PASSDLG_ERROR_NONE:
939
passdlg_set_status (pdialog, _("Click <b>Change password</b> to change your password."));
941
case PASSDLG_ERROR_NEW_PASSWORD_EMPTY:
942
passdlg_set_status (pdialog, _("Please type your password in the <b>New password</b> field."));
944
case PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY:
945
passdlg_set_status (pdialog, _("Please type your password again in the <b>Retype new password</b> field."));
947
case PASSDLG_ERROR_PASSWORDS_NOT_EQUAL:
948
passdlg_set_status (pdialog, _("The two passwords are not equal."));
951
g_warning ("Unknown passdlg_check_password error: %d", ret);
956
/* Called when the "Change password" dialog-button is clicked
957
* Returns: TRUE if we want to keep the dialog running, FALSE otherwise */
959
passdlg_process_response (PasswordDialog *pdialog, gint response_id)
962
if (response_id == GTK_RESPONSE_OK) {
963
/* Set busy as this can be a long process */
964
passdlg_set_busy (pdialog, TRUE);
966
/* Stop passwd if an error occured and it is still running */
967
if (pdialog->backend_state == PASSWD_STATE_ERR) {
969
/* Stop passwd, free resources */
970
stop_passwd (pdialog);
973
/* Check that the backend is still running, or that an error
974
* hass occured but it has not yet exited */
975
if (pdialog->backend_pid == -1) {
976
/* If it is not, re-run authentication */
979
if (!passdlg_spawn_passwd (pdialog)) {
983
/* Add current and new passwords to queue */
984
authenticate (pdialog);
985
update_password (pdialog);
987
/* Only add new passwords to queue */
988
update_password (pdialog);
990
/* Pop new password through the backend */
991
io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin);
994
/* Our IO watcher should now handle the rest */
996
/* Keep the dialog running */
1003
/* Activates (moves focus or activates) widget w */
1005
passdlg_activate (GtkEntry *entry, GtkWidget *w)
1007
if (GTK_IS_BUTTON (w)) {
1008
gtk_widget_activate (w);
1010
gtk_widget_grab_focus (w);
1014
/* Initialize password dialog */
1016
passdlg_init (PasswordDialog *pdialog, GtkWindow *parent)
1019
GtkWidget *wpassdlg;
1020
GtkAccelGroup *group;
1022
/* Initialize dialog */
1023
dialog = gtk_builder_new ();
1024
gtk_builder_add_from_file (dialog, GNOMECC_UI_DIR "/gnome-about-me-password.ui", NULL);
1025
pdialog->ui = dialog;
1027
wpassdlg = WID ("change-password");
1028
capplet_set_icon (wpassdlg, "user-info");
1030
group = gtk_accel_group_new ();
1033
* Initialize backend
1036
/* Initialize backend_pid. -1 means the backend is not running */
1037
pdialog->backend_pid = -1;
1039
/* Initialize IO Channels */
1040
pdialog->backend_stdin = NULL;
1041
pdialog->backend_stdout = NULL;
1043
/* Initialize write queue */
1044
pdialog->backend_stdin_queue = g_queue_new ();
1046
/* Initialize watchers */
1047
pdialog->backend_child_watch_id = 0;
1048
pdialog->backend_stdout_watch_id = 0;
1050
/* Initialize backend state */
1051
pdialog->backend_state = PASSWD_STATE_NONE;
1057
/* Initialize pdialog widgets */
1058
pdialog->current_password = GTK_ENTRY (WID ("current-password"));
1059
pdialog->new_password = GTK_ENTRY (WID ("new-password"));
1060
pdialog->retyped_password = GTK_ENTRY (WID ("retyped-password"));
1061
pdialog->dialog_image = GTK_IMAGE (WID ("dialog-image"));
1062
pdialog->status_label = GTK_LABEL (WID ("status-label"));
1064
/* Initialize accelerators */
1065
gtk_widget_add_accelerator (GTK_WIDGET (pdialog->current_password),
1070
gtk_widget_add_accelerator (GTK_WIDGET (pdialog->new_password),
1075
/* Activate authenticate-button when enter is pressed in current-password */
1076
g_signal_connect (G_OBJECT (pdialog->current_password), "activate",
1077
G_CALLBACK (passdlg_activate), WID ("authenticate-button"));
1079
/* Activate retyped-password when enter is pressed in new-password */
1080
g_signal_connect (G_OBJECT (pdialog->new_password), "activate",
1081
G_CALLBACK (passdlg_activate), pdialog->retyped_password);
1083
/* Clear status message */
1084
passdlg_set_status (pdialog, "");
1086
/* Set non-authenticated state */
1087
passdlg_set_auth_state (pdialog, FALSE);
1089
/* Connect signal handlers */
1090
g_signal_connect (G_OBJECT (WID ("authenticate-button")), "clicked",
1091
G_CALLBACK (passdlg_authenticate), pdialog);
1093
/* Verify new passwords on-the-fly */
1094
g_signal_connect (G_OBJECT (WID ("new-password")), "changed",
1095
G_CALLBACK (passdlg_check_password), pdialog);
1096
g_signal_connect (G_OBJECT (WID ("retyped-password")), "changed",
1097
G_CALLBACK (passdlg_check_password), pdialog);
1099
/* Set misc dialog properties */
1100
gtk_window_set_resizable (GTK_WINDOW (wpassdlg), FALSE);
1101
gtk_window_set_transient_for (GTK_WINDOW (wpassdlg), GTK_WINDOW (parent));
1106
gnome_about_me_password (GtkWindow *parent)
1108
PasswordDialog *pdialog;
1110
GtkWidget *wpassdlg;
1115
/* Initialize dialog */
1116
pdialog = g_new0 (PasswordDialog, 1);
1117
passdlg_init (pdialog, parent);
1119
dialog = pdialog->ui;
1120
wpassdlg = WID ("change-password");
1123
gtk_widget_show_all (wpassdlg);
1126
result = gtk_dialog_run (GTK_DIALOG (wpassdlg));
1127
response = passdlg_process_response (pdialog, result);
1131
stop_passwd (pdialog);
1132
gtk_widget_destroy (wpassdlg);
1133
g_queue_free (pdialog->backend_stdin_queue);
1134
g_object_unref (dialog);