gboolean
passwd_change_password (PasswdHandler *passwd_handler,
                        const char    *new_password,
                        PasswdCallback cb,
                        const gpointer user_data)
{
        GError *error = NULL;

        passwd_handler->changing_password = TRUE;

        passwd_handler->new_password = new_password;
        passwd_handler->chpasswd_cb = cb;
        passwd_handler->chpasswd_cb_data = user_data;

        /* Stop passwd if an error occured and it is still running */
        if (passwd_handler->backend_state == PASSWD_STATE_ERR) {

                /* Stop passwd, free resources */
                stop_passwd (passwd_handler);
        }

        /* Check that the backend is still running, or that an error
         * has occured but it has not yet exited */
        if (passwd_handler->backend_pid == -1) {
                /* If it is not, re-run authentication */

                /* Spawn backend */
                stop_passwd (passwd_handler);

                if (!spawn_passwd (passwd_handler, &error)) {
                        g_warning ("%s", error->message);
                        g_error_free (error);

                        return FALSE;
                }

                /* Add current and new passwords to queue */
                authenticate (passwd_handler);
                update_password (passwd_handler);
        } else {
                /* Only add new passwords to queue */
                update_password (passwd_handler);
        }

        /* Pop new password through the backend.
         * If user has no password, popping the queue would output current
         * password, while 'passwd' is waiting for the new one. So wait for
         * io_watch_stdout() to remove current password from the queue,
         * and output the new one for us.
         */
        if (passwd_handler->current_password)
                io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);

        /* Our IO watcher should now handle the rest */

        return TRUE;
}
/* Start backend and handle errors
 * If backend is already running, stop it
 * If an error occurs, show error dialog */
static gboolean
passdlg_spawn_passwd (PasswordDialog *pdialog)
{
	GError	*error = NULL;
	gchar	*details;

	/* If backend is running, stop it */
	stop_passwd (pdialog);

	/* Spawn backend */
	if (!spawn_passwd (pdialog, &error)) {
		GtkWidget *parent = GTK_WIDGET (gtk_builder_get_object (pdialog->ui, "change-password"));

		/* translators: Unable to launch <program>: <error message> */
		details = g_strdup_printf (_("Unable to launch %s: %s"),
					   "/usr/bin/passwd", error->message);

		passdlg_error_dialog (GTK_WINDOW (parent),
				      _("Unable to launch backend"),
				      _("A system error has occurred"),
				      details);

		g_free (details);
		g_error_free (error);

		return FALSE;
	}

	return TRUE;
}
/* Main */
void
mate_about_me_password (GtkWindow *parent)
{
	PasswordDialog	*pdialog;
	GtkBuilder		*dialog;
	GtkWidget		*wpassdlg;

	gint			result;
	gboolean		response;

	/* Initialize dialog */
	pdialog = g_new0 (PasswordDialog, 1);
	passdlg_init (pdialog, parent);

	dialog = pdialog->ui;
	wpassdlg = WID ("change-password");

	/* Go! */
	gtk_widget_show_all (wpassdlg);

	do {
		result = gtk_dialog_run (GTK_DIALOG (wpassdlg));
		response = passdlg_process_response (pdialog, result);
	} while (response);

	/* Clean up */
	stop_passwd (pdialog);
	gtk_widget_destroy (wpassdlg);
	g_queue_free (pdialog->backend_stdin_queue);
	g_object_unref (dialog);
	g_free (pdialog);
}
void
passwd_destroy (PasswdHandler *passwd_handler)
{
        g_queue_free (passwd_handler->backend_stdin_queue);
        stop_passwd (passwd_handler);
        g_free (passwd_handler);
}
/* Called when the "Change password" dialog-button is clicked
 * Returns: TRUE if we want to keep the dialog running, FALSE otherwise */
static gboolean
passdlg_process_response (PasswordDialog *pdialog, gint response_id)
{

	if (response_id == GTK_RESPONSE_OK) {
		/* Set busy as this can be a long process */
		passdlg_set_busy (pdialog, TRUE);

		/* Stop passwd if an error occured and it is still running */
		if (pdialog->backend_state == PASSWD_STATE_ERR) {

			/* Stop passwd, free resources */
			stop_passwd (pdialog);
		}

		/* Check that the backend is still running, or that an error
		 * hass occured but it has not yet exited */
		if (pdialog->backend_pid == -1) {
			/* If it is not, re-run authentication */

			/* Spawn backend */
			if (!passdlg_spawn_passwd (pdialog)) {
				return TRUE;
			}

			/* Add current and new passwords to queue */
			authenticate (pdialog);
			update_password (pdialog);
		} else {
			/* Only add new passwords to queue */
			update_password (pdialog);

			/* Pop new password through the backend */
			io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin);
		}

		/* Our IO watcher should now handle the rest */

		/* Keep the dialog running */
		return TRUE;
	}

	return FALSE;
}
void
passwd_authenticate (PasswdHandler *passwd_handler,
                     const char    *current_password,
                     PasswdCallback cb,
                     const gpointer user_data)
{
        GError *error = NULL;

        /* Don't stop if we've already started chaging password */
        if (passwd_handler->changing_password)
                return;

        /* Clear data from possible previous attempts to change password */
        passwd_handler->new_password = NULL;
        passwd_handler->chpasswd_cb = NULL;
        passwd_handler->chpasswd_cb_data = NULL;
        g_queue_foreach (passwd_handler->backend_stdin_queue, (GFunc) g_free, NULL);
        g_queue_clear (passwd_handler->backend_stdin_queue);

        passwd_handler->current_password = current_password;
        passwd_handler->auth_cb = cb;
        passwd_handler->auth_cb_data = user_data;

        /* Spawn backend */
        stop_passwd (passwd_handler);

        if (!spawn_passwd (passwd_handler, &error)) {
                g_warning ("%s", error->message);
                g_error_free (error);

                return;
        }

        authenticate (passwd_handler);

        /* Our IO watcher should now handle the rest */
}
/* Spawn passwd backend
 * Returns: TRUE on success, FALSE otherwise and sets error appropriately */
static gboolean
spawn_passwd (PasswordDialog *pdialog, GError **error)
{
	gchar	*argv[2];
	gchar	*envp[1];
	gint	my_stdin, my_stdout, my_stderr;

	argv[0] = "/usr/bin/passwd";	/* Is it safe to rely on a hard-coded path? */
	argv[1] = NULL;

	envp[0] = NULL;					/* If we pass an empty array as the environment,
									 * will the childs environment be empty, and the
									 * locales set to the C default? From the manual:
									 * "If envp is NULL, the child inherits its
									 * parent'senvironment."
									 * If I'm wrong here, we somehow have to set
									 * the locales here.
									 */

	if (!g_spawn_async_with_pipes (NULL,						/* Working directory */
								   argv,						/* Argument vector */
								   envp,						/* Environment */
								   G_SPAWN_DO_NOT_REAP_CHILD,	/* Flags */
								   NULL,						/* Child setup */
								   NULL,						/* Data to child setup */
								   &pdialog->backend_pid,		/* PID */
								   &my_stdin,						/* Stdin */
								   &my_stdout,						/* Stdout */
								   &my_stderr,						/* Stderr */
								   error)) {					/* GError */

		/* An error occured */
		free_passwd_resources (pdialog);

		return FALSE;
	}

	/* 2>&1 */
	if (dup2 (my_stderr, my_stdout) == -1) {
		/* Failed! */
		g_set_error (error,
					 PASSDLG_ERROR,
					 PASSDLG_ERROR_BACKEND,
					 strerror (errno));

		/* Clean up */
		stop_passwd (pdialog);

		return FALSE;
	}

	/* Open IO Channels */
	pdialog->backend_stdin = g_io_channel_unix_new (my_stdin);
	pdialog->backend_stdout = g_io_channel_unix_new (my_stdout);

	/* Set raw encoding */
	/* Set nonblocking mode */
	if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
		g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
		g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
		g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {

		/* Clean up */
		stop_passwd (pdialog);
		return FALSE;
	}

	/* Turn off buffering */
	g_io_channel_set_buffered (pdialog->backend_stdin, FALSE);
	g_io_channel_set_buffered (pdialog->backend_stdout, FALSE);

	/* Add IO Channel watcher */
	pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout,
													   G_IO_IN | G_IO_PRI,
													   (GIOFunc) io_watch_stdout, pdialog);

	/* Add child watcher */
	pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog);

	/* Success! */

	return TRUE;
}
/* Spawn passwd backend
 * Returns: TRUE on success, FALSE otherwise and sets error appropriately */
static gboolean
spawn_passwd (PasswdHandler *passwd_handler, GError **error)
{
        gchar   *argv[2];
        gchar  **envp;
        gint    my_stdin, my_stdout, my_stderr;

        argv[0] = "/usr/bin/passwd";    /* Is it safe to rely on a hard-coded path? */
        argv[1] = NULL;

        envp = g_get_environ ();
        envp = g_environ_setenv (envp, "LC_ALL", "C", TRUE);

        if (!g_spawn_async_with_pipes (NULL,                            /* Working directory */
                                       argv,                            /* Argument vector */
                                       envp,                            /* Environment */
                                       G_SPAWN_DO_NOT_REAP_CHILD,       /* Flags */
                                       ignore_sigpipe,                  /* Child setup */
                                       NULL,                            /* Data to child setup */
                                       &passwd_handler->backend_pid,    /* PID */
                                       &my_stdin,                       /* Stdin */
                                       &my_stdout,                      /* Stdout */
                                       &my_stderr,                      /* Stderr */
                                       error)) {                        /* GError */

                /* An error occured */
                free_passwd_resources (passwd_handler);

                g_strfreev (envp);

                return FALSE;
        }

        g_strfreev (envp);

        /* 2>&1 */
        if (dup2 (my_stderr, my_stdout) == -1) {
                /* Failed! */
                g_set_error_literal (error,
                                     PASSWD_ERROR,
                                     PASSWD_ERROR_BACKEND,
                                     strerror (errno));

                /* Clean up */
                stop_passwd (passwd_handler);

                return FALSE;
        }

        /* Open IO Channels */
        passwd_handler->backend_stdin = g_io_channel_unix_new (my_stdin);
        passwd_handler->backend_stdout = g_io_channel_unix_new (my_stdout);

        /* Set raw encoding */
        /* Set nonblocking mode */
        if (g_io_channel_set_encoding (passwd_handler->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
                g_io_channel_set_encoding (passwd_handler->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
                g_io_channel_set_flags (passwd_handler->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
                g_io_channel_set_flags (passwd_handler->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {

                /* Clean up */
                stop_passwd (passwd_handler);
                return FALSE;
        }

        /* Turn off buffering */
        g_io_channel_set_buffered (passwd_handler->backend_stdin, FALSE);
        g_io_channel_set_buffered (passwd_handler->backend_stdout, FALSE);

        /* Add IO Channel watcher */
        passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
                                                                  G_IO_IN | G_IO_PRI,
                                                                  (GIOFunc) io_watch_stdout, passwd_handler);

        /* Add child watcher */
        passwd_handler->backend_child_watch_id = g_child_watch_add (passwd_handler->backend_pid, (GChildWatchFunc) child_watch_cb, passwd_handler);

        /* Success! */

        return TRUE;
}