gnome-control-center/capplets/about-me/gnome-about-me-password.c

513 lines
12 KiB
C
Raw Normal View History

2004-11-03 20:29:08 +00:00
/* gnome-about-me.c
* Copyright (C) 2002 Diego Gonzalez
*
* Written by: Diego Gonzalez <diego@pemas.net>
*
* 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, 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.
*
* Parts of this code come from Gnome-System-Tools.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <gnome.h>
#include <pwd.h>
#include <stdlib.h>
2004-11-03 20:29:08 +00:00
#include <glade/glade.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/poll.h>
2004-11-03 20:29:08 +00:00
#include <termios.h>
#include <pty.h>
#include "capplet-util.h"
#include "eel-alert-dialog.h"
2004-11-03 20:29:08 +00:00
typedef struct {
GladeXML *xml;
2004-11-03 20:29:08 +00:00
GtkWidget *old_password;
GtkWidget *new_password;
GtkWidget *retyped_password;
guint timeout_id;
guint check_password_timeout_id;
gboolean good_password;
2004-11-03 20:29:08 +00:00
/* Communication with the passwd program */
int backend_pid;
int write_fd;
int read_fd;
FILE *write_stream;
FILE *read_stream;
2004-11-03 20:29:08 +00:00
} PasswordDialog;
enum
{
RESPONSE_APPLY = 1,
RESPONSE_CLOSE
};
static void passdlg_set_busy (PasswordDialog *dlg, gboolean busy);
#define REDRAW_NCHARS 1
static gboolean
wait_child (PasswordDialog *pdialog)
{
gint status, pid;
gchar *msg, *details, *title;
GladeXML *dialog;
GtkWidget *wmessage, *wbulb;
GtkWidget *wedialog;
2004-11-03 20:29:08 +00:00
dialog = pdialog->xml;
wmessage = WID ("message");
wbulb = WID ("bulb");
2004-11-03 20:29:08 +00:00
pid = waitpid (pdialog->backend_pid, &status, WNOHANG);
passdlg_set_busy (pdialog, FALSE);
2004-11-03 20:29:08 +00:00
if (pid > 0) {
if (WIFEXITED (status) && (WEXITSTATUS(status) == 0)) {
gtk_main_quit ();
2004-11-03 20:29:08 +00:00
return FALSE;
} else if ((WIFEXITED (status)) && (WEXITSTATUS (status)) && (WEXITSTATUS(status) < 255)) {
msg = g_strdup_printf ("<b>%s</b>", _("Old password is incorrect, please retype it"));
gtk_label_set_markup (GTK_LABEL (wmessage), msg);
g_free (msg);
gtk_image_set_from_file (GTK_IMAGE (wbulb),
GNOMECC_DATA_DIR "/pixmaps/gnome-about-me-bulb-off.png");
return FALSE;
2004-11-03 20:29:08 +00:00
} else if ((WIFEXITED (status)) && (WEXITSTATUS (status)) && (WEXITSTATUS (status) == 255)) {
msg = g_strdup (_("System error has occurred"));
details = g_strdup (_("Could not run /usr/bin/passwd"));
title = g_strdup (_("Unable to launch backend"));
2004-11-03 20:29:08 +00:00
} else {
msg = g_strdup (_("Unexpected error has occurred"));
title = g_strdup (_("Unexpected error has occurred"));
details = NULL;
2004-11-03 20:29:08 +00:00
}
wedialog = eel_alert_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
msg, NULL, title);
if (details != NULL)
eel_alert_dialog_set_details_label (EEL_ALERT_DIALOG (wedialog), details);
g_signal_connect (G_OBJECT (wedialog), "response",
2004-11-03 20:29:08 +00:00
G_CALLBACK (gtk_widget_destroy), NULL);
gtk_window_set_resizable (GTK_WINDOW (wedialog), FALSE);
gtk_widget_show (wedialog);
g_free (msg);
g_free (title);
g_free (details);
2004-11-03 20:29:08 +00:00
return FALSE;
}
return TRUE;
}
static gboolean
is_string_complete (gchar *str, GSList *list)
{
GSList *elem;
if (strlen (str) == 0)
return FALSE;
for (elem = list; elem; elem = g_slist_next (elem))
if (g_strrstr (str, elem->data) != NULL) {
2004-11-03 20:29:08 +00:00
return TRUE;
}
2004-11-03 20:29:08 +00:00
return FALSE;
}
static gchar*
read_everything (PasswordDialog *pdialog, gchar *needle, va_list ap)
2004-11-03 20:29:08 +00:00
{
GString *str = g_string_new ("");
GSList *list = NULL;
gchar*arg, *ptr, c;
2004-11-03 20:29:08 +00:00
list = g_slist_prepend (list, needle);
2004-11-03 20:29:08 +00:00
while ((arg = va_arg (ap, char*)) != NULL)
list = g_slist_prepend (list, arg);
2004-11-03 20:29:08 +00:00
va_end (ap);
2004-11-03 20:29:08 +00:00
while (!is_string_complete (str->str, list)) {
c = fgetc (pdialog->read_stream);
if (c != EOF)
2004-11-03 20:29:08 +00:00
g_string_append_c (str, c);
}
ptr = str->str;
g_string_free (str, FALSE);
2004-11-03 20:29:08 +00:00
return ptr;
}
static void
poll_backend (PasswordDialog *pdialog)
{
struct pollfd fd;
fd.fd = pdialog->read_fd;
fd.events = POLLIN || POLLPRI;
while (poll (&fd, 1, 100) <= 0) {
while (gtk_events_pending ())
gtk_main_iteration ();
}
}
static char *
read_from_backend_va (PasswordDialog *pdialog, gchar *needle, va_list ap)
{
poll_backend (pdialog);
return read_everything (pdialog, needle, ap);
}
2004-11-03 20:29:08 +00:00
static gchar*
read_from_backend (PasswordDialog *pdialog, gchar *needle, ...)
{
va_list ap;
va_start (ap, needle);
return read_from_backend_va (pdialog, needle, ap);
}
static void
write_to_backend (PasswordDialog *pdialog, char *str)
{
gint nread = 0;
int ret;
do {
ret = fputc (str [nread], pdialog->write_stream);
usleep (1000);
2004-11-03 20:29:08 +00:00
if (ret != EOF)
nread++;
/* ugly hack for redrawing UI */
if (nread % REDRAW_NCHARS == 0)
while (gtk_events_pending ())
gtk_main_iteration ();
} while (nread < strlen (str));
while (fflush (pdialog->write_stream) != 0);
2004-11-03 20:29:08 +00:00
}
static void
passdlg_set_busy (PasswordDialog *pdialog, gboolean busy)
{
GladeXML *dialog;
GtkWidget *toplevel;
2004-11-03 20:29:08 +00:00
GdkCursor *cursor = NULL;
GdkDisplay *display;
dialog = pdialog->xml;
toplevel = WID ("change-password");
display = gtk_widget_get_display (toplevel);
2004-11-03 20:29:08 +00:00
if (busy)
cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
gdk_window_set_cursor (toplevel->window, cursor);
2004-11-03 20:29:08 +00:00
gdk_display_flush (display);
if (busy)
gdk_cursor_unref (cursor);
}
static gint
update_password (PasswordDialog *pdialog)
2004-11-03 20:29:08 +00:00
{
GtkWidget *wopasswd, *wnpasswd, *wrnpasswd;
2004-11-03 20:29:08 +00:00
char *new_password;
char *retyped_password;
char *old_password;
gchar *s;
GladeXML *dialog;
dialog = pdialog->xml;
wopasswd = WID ("old-password");
wnpasswd = WID ("new-password");
wrnpasswd = WID ("retyped-password");
/* */
old_password = g_strdup_printf ("%s\n", gtk_entry_get_text (GTK_ENTRY (wopasswd)));
new_password = g_strdup_printf ("%s\n", gtk_entry_get_text (GTK_ENTRY (wnpasswd)));
retyped_password = g_strdup_printf ("%s\n", gtk_entry_get_text (GTK_ENTRY (wrnpasswd)));
/* Set the busy cursor as this can be a long process */
passdlg_set_busy (pdialog, TRUE);
s = read_from_backend (pdialog, "assword: ", NULL);
g_free (s);
write_to_backend (pdialog, old_password);
/* New password */
s = read_from_backend (pdialog, "assword: ", "failure", NULL);
if (g_strrstr (s, "failure") != NULL) {
g_free (s);
return -1;
}
g_free (s);
write_to_backend (pdialog, new_password);
/* Retype password */
s = read_from_backend (pdialog, "assword: ", NULL);
g_free (s);
write_to_backend (pdialog, retyped_password);
s = read_from_backend (pdialog, "successfully", "Bad:", "recovered", NULL);
if (g_strrstr (s, "recovered") != NULL) {
return -2;
} else if (g_strrstr (s, "Bad") != NULL) {
return -3;
}
return 0;
}
static void
spawn_passwd (PasswordDialog *pdialog)
{
char *args[2];
int p[2];
/* Prepare the execution environment of passwd */
args[0] = "/usr/bin/passwd";
args[1] = NULL;
pipe (p);
pdialog->backend_pid = forkpty (&pdialog->write_fd, NULL, NULL, NULL);
if (pdialog->backend_pid < 0) {
g_warning ("could not fork to backend");
gtk_main_quit ();
} else if (pdialog->backend_pid == 0) {
dup2 (p[1], 1);
dup2 (p[1], 2);
close (p[0]);
unsetenv("LC_ALL");
unsetenv("LC_MESSAGES");
unsetenv("LANG");
unsetenv("LANGUAGE");
execv (args[0], args);
exit (255);
} else {
close (p[1]);
pdialog->read_fd = p[0];
pdialog->timeout_id = g_timeout_add (4000, (GSourceFunc) wait_child, pdialog);
pdialog->read_stream = fdopen (pdialog->read_fd, "r");;
pdialog->write_stream = fdopen (pdialog->write_fd, "w");
setvbuf (pdialog->read_stream, NULL, _IONBF, 0);
fcntl (pdialog->read_fd, F_SETFL, 0);
}
}
static void
passdlg_button_clicked_cb (GtkDialog *widget, gint response_id, PasswordDialog *pdialog)
{
GladeXML *dialog;
GtkWidget *wmessage, *wbulb;
gchar *msg;
gint ret;
dialog = pdialog->xml;
wmessage = WID ("message");
wbulb = WID ("bulb");
2004-11-03 20:29:08 +00:00
if (response_id == GTK_RESPONSE_OK) {
spawn_passwd (pdialog);
2004-11-03 20:29:08 +00:00
ret = update_password (pdialog);
passdlg_set_busy (pdialog, FALSE);
2004-11-03 20:29:08 +00:00
/* No longer need the wait_child fallback, remove the timeout */
g_source_remove (pdialog->timeout_id);
2004-11-03 20:29:08 +00:00
if (ret == -1) {
msg = g_strdup_printf ("<b>%s</b>", _("Old password is incorrect, please retype it"));
gtk_label_set_markup (GTK_LABEL (wmessage), msg);
g_free (msg);
gtk_image_set_from_file (GTK_IMAGE (wbulb),
GNOMECC_DATA_DIR "/pixmaps/gnome-about-me-bulb-off.png");
}
/* This is the standard way of returning from the dialog with passwd
* If we return this way we can safely kill passwd as it has completed
* its task. In case of problems we still have the wait_child fallback
*/
fclose (pdialog->write_stream);
fclose (pdialog->read_stream);
close (pdialog->read_fd);
close (pdialog->write_fd);
kill (pdialog->backend_pid, 9);
if (ret == 0)
2004-11-03 20:29:08 +00:00
gtk_main_quit ();
} else {
gtk_main_quit ();
}
}
static gboolean
passdlg_check_password_timeout_cb (PasswordDialog *pdialog)
{
const gchar *password;
const gchar *retyped_password;
char *msg, *msgtmp;
gboolean good_password;
GtkWidget *wbulb, *wok, *wmessage;
GtkWidget *wnpassword, *wrnpassword;
GladeXML *dialog;
dialog = pdialog->xml;
wnpassword = WID ("new-password");
wrnpassword = WID ("retyped-password");
wmessage = WID ("message");
wbulb = WID ("bulb");
wok = WID ("ok");
password = gtk_entry_get_text (GTK_ENTRY (wnpassword));
retyped_password = gtk_entry_get_text (GTK_ENTRY (wrnpassword));
if (strlen (password) == 0 || strlen (retyped_password) == 0) {
gtk_image_set_from_file (GTK_IMAGE (wbulb),
GNOMECC_DATA_DIR "/pixmaps/gnome-about-me-bulb-off.png");
gtk_label_set_markup (GTK_LABEL (wmessage), NULL);
return FALSE;
}
if (strcmp (password, retyped_password) != 0) {
msg = g_strdup ("<b>Please type the password again, it is wrong.</b>");
good_password = FALSE;
} else {
msg = g_strdup ("<b>Push on the ok button to change the password</b>");
good_password = TRUE;
}
2004-11-03 20:29:08 +00:00
if (good_password && pdialog->good_password == FALSE) {
gtk_image_set_from_file (GTK_IMAGE (wbulb),
GNOMECC_DATA_DIR "/pixmaps/gnome-about-me-bulb-on.png");
gtk_widget_set_sensitive (wok, TRUE);
} else if (good_password == FALSE && pdialog->good_password == TRUE) {
gtk_image_set_from_file (GTK_IMAGE (wbulb),
GNOMECC_DATA_DIR "/pixmaps/gnome-about-me-bulb-off.png");
gtk_widget_set_sensitive (wok, FALSE);
}
2004-11-03 20:29:08 +00:00
pdialog->good_password = good_password;
2004-11-03 20:29:08 +00:00
gtk_label_set_markup (GTK_LABEL (wmessage), msg);
g_free (msg);
2004-11-03 20:29:08 +00:00
return FALSE;
}
2004-11-03 20:29:08 +00:00
static void
passdlg_check_password (GtkEntry *entry, PasswordDialog *pdialog)
{
if (pdialog->check_password_timeout_id) {
g_source_remove (pdialog->check_password_timeout_id);
2004-11-03 20:29:08 +00:00
}
pdialog->check_password_timeout_id =
g_timeout_add (300, (GSourceFunc) passdlg_check_password_timeout_cb, pdialog);
2004-11-03 20:29:08 +00:00
}
void
gnome_about_me_password (GtkWindow *parent)
2004-11-03 20:29:08 +00:00
{
PasswordDialog *pdialog;
GtkWidget *wpassdlg;
2004-11-03 20:29:08 +00:00
GladeXML *dialog;
pdialog = g_new0 (PasswordDialog, 1);
dialog = glade_xml_new (GNOMECC_DATA_DIR "/interfaces/gnome-about-me.glade", "change-password", NULL);
pdialog->xml = dialog;
wpassdlg = WID ("change-password");
g_signal_connect (G_OBJECT (wpassdlg), "response",
2004-11-03 20:29:08 +00:00
G_CALLBACK (passdlg_button_clicked_cb), pdialog);
pdialog->good_password = FALSE;
2004-11-03 20:29:08 +00:00
g_signal_connect (G_OBJECT (WID ("new-password")), "changed",
G_CALLBACK (passdlg_check_password), pdialog);
g_signal_connect (G_OBJECT (WID ("retyped-password")), "changed",
G_CALLBACK (passdlg_check_password), pdialog);
gtk_image_set_from_file (GTK_IMAGE (WID ("bulb")), GNOMECC_DATA_DIR "/pixmaps/gnome-about-me-bulb-off.png");
gtk_widget_set_sensitive (WID ("ok"), FALSE);
gtk_window_set_resizable (GTK_WINDOW (wpassdlg), FALSE);
gtk_window_set_transient_for (GTK_WINDOW (wpassdlg), GTK_WINDOW (parent));
gtk_widget_show_all (wpassdlg);
2004-11-03 20:29:08 +00:00
gtk_main ();
gtk_widget_destroy (wpassdlg);
2004-11-03 20:29:08 +00:00
g_free (pdialog);
}