测试标题

本文介绍Gksu库的实现原理与关键功能,包括如何处理用户认证、权限提升及与图形界面交互等核心流程。同时探讨了Gksu在获取用户密码、设置临时授权文件等方面的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开始

/*
 * Gksu -- a library providing access to su functionality
 * Copyright (C) 2004 Gustavo Noronha Silva
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <pty.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <errno.h>

#include <glibtop.h>
#include <glibtop/procstate.h>

#include <gdk/gdk.h>
#include <gdk/gdkx.h>

#define SN_API_NOT_YET_FROZEN
#include <libsn/sn.h>

#include <gtk/gtk.h>
#include <locale.h>

#include <gconf/gconf-client.h>
#include <gnome-keyring.h>

#include "defines.h"
#include "../config.h"

#include "libgksu.h"
#include "../libgksuui/gksuui-dialog.h"

static void
gksu_context_launch_complete (GksuContext *context);

GType
gksu_error_get_type (void)
{
  static GType etype = 0;
  if (etype == 0) {
    static const GEnumValue values[] = {
      { GKSU_ERROR_HELPER, "GKSU_ERROR_HELPER", "helper" },
      { GKSU_ERROR_NOCOMMAND, "GKSU_ERROR_NOCOMMAND", "nocommand" },
      { GKSU_ERROR_NOPASSWORD, "GKSU_ERROR_NOPASSWORD", "nopassword" },
      { GKSU_ERROR_FORK, "GKSU_ERROR_FORK", "fork" },
      { GKSU_ERROR_EXEC, "GKSU_ERROR_EXEC", "exec" },
      { GKSU_ERROR_PIPE, "GKSU_ERROR_PIPE", "pipe" },
      { GKSU_ERROR_PIPEREAD, "GKSU_ERROR_PIPEREAD", "piperead" },
      { GKSU_ERROR_WRONGPASS, "GKSU_ERROR_WRONGPASS", "wrongpass" },
      { GKSU_ERROR_CHILDFAILED, "GKSU_ERROR_CHILDFAILED", "childfailed" },
      { GKSU_ERROR_CANCELED, "GKSU_ERROR_CANCELED", "canceled" },
      { GKSU_ERROR_WRONGAUTOPASS, "GKSU_ERROR_WRONGAUTOPASS", "wrongautopass" },
      { 0, NULL, NULL }
    };
    etype = g_enum_register_static ("GksuError", values);
  }
  return etype;
}

static pid_t
test_lock(const char* fname)
{
   int FD = open(fname, 0);
   if(FD < 0) {
      if(errno == ENOENT) {
  // File does not exist
  return 0;
      } else {
  perror("open");
  return(-1);
      }
   }
   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = SEEK_SET;
   fl.l_start = 0;
   fl.l_len = 0;
   if (fcntl(FD, F_GETLK, &fl) < 0) {
      g_critical("fcntl error");
      close(FD);
      return(-1);
   }
   close(FD);
   // lock is available
   if(fl.l_type == F_UNLCK)
      return(0);
   // file is locked by another process
   return (fl.l_pid);
}

static int
get_lock(const char *File)
{
   int FD = open(File,O_RDWR | O_CREAT | O_TRUNC,0640);
   if (FD < 0)
   {
      // Read only .. cant have locking problems there.
      if (errno == EROFS)
      {
  g_warning(_("Not using locking for read only lock file %s"),File);
  return dup(0);       // Need something for the caller to close
      }

      // Feh.. We do this to distinguish the lock vs open case..
      errno = EPERM;
      return -1;
   }
   fcntl(FD,F_SETFD, FD_CLOEXEC);

   // Aquire a write lock
   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = SEEK_SET;
   fl.l_start = 0;
   fl.l_len = 0;
   if (fcntl(FD,F_SETLK,&fl) == -1)
   {
      if (errno == ENOLCK)
      {
  g_warning(_("Not using locking for nfs mounted lock file %s"), File);
  unlink(File);
  close(FD);
  return dup(0);       // Need something for the caller to close
      }

      int Tmp = errno;
      close(FD);
      errno = Tmp;
      return -1;
   }

   return FD;
}

/*
 * code 'stolen' from gnome-session's logout.c
 *
 * Written by Owen Taylor <otaylor@redhat.com>
 * Copyright (C) Red Hat
 */
typedef struct {
  GdkScreen    *screen;
  int           monitor;
  GdkRectangle  area;
  int           rowstride;
  GdkWindow    *root_window;
  GdkWindow    *draw_window;
  GdkPixbuf    *start_pb, *end_pb, *frame;
  guchar       *start_p, *end_p, *frame_p;
  GTimeVal      start_time;
  GdkGC        *gc;
} FadeoutData;

FadeoutData *fade_data = NULL;
static GList *fadeout_windows = NULL;

#define FADE_DURATION 500.0

int
gsm_screen_get_width (GdkScreen *screen,
        int        monitor)
{
 GdkRectangle geometry;

 gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

 return geometry.width;
}

int
gsm_screen_get_height (GdkScreen *screen,
         int        monitor)
{
 GdkRectangle geometry;

 gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

 return geometry.height;
}

int
gsm_screen_get_x (GdkScreen *screen,
    int        monitor)
{
 GdkRectangle geometry;

 gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

 return geometry.x;
}

int
gsm_screen_get_y (GdkScreen *screen,
    int        monitor)
{
 GdkRectangle geometry;

 gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

 return geometry.y;
}

static void
get_current_frame (FadeoutData *fadeout,
     double    sat)
{
  guchar *sp, *ep, *fp;
  int i, j, width, offset;

  width = fadeout->area.width * 3;
  offset = 0;

  for (i = 0; i < fadeout->area.height; i++)
    {
      sp = fadeout->start_p + offset;
      ep = fadeout->end_p   + offset;
      fp = fadeout->frame_p + offset;

      for (j = 0; j < width; j += 3)
 {
   guchar r = abs (*(sp++) - ep[0]);
   guchar g = abs (*(sp++) - ep[1]);
   guchar b = abs (*(sp++) - ep[2]);

   *(fp++) = *(ep++) + r * sat;
   *(fp++) = *(ep++) + g * sat;
   *(fp++) = *(ep++) + b * sat;
 }

      offset += fadeout->rowstride;
    }
}

static void
darken_pixbuf (GdkPixbuf *pb)
{
  int width, height, rowstride;
  int i, j;
  guchar *p, *pixels;

  width     = gdk_pixbuf_get_width (pb) * 3;
  height    = gdk_pixbuf_get_height (pb);
  rowstride = gdk_pixbuf_get_rowstride (pb);
  pixels    = gdk_pixbuf_get_pixels (pb);

  for (i = 0; i < height; i++)
    {
      p = pixels + (i * rowstride);
      for (j = 0; j < width; j++)
 p [j] >>= 1;
    }
}

static gboolean
fadeout_callback (FadeoutData *fadeout)
{
  GTimeVal current_time;
  double elapsed, percent;

  g_get_current_time (&current_time);
  elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
       (current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;

  if (elapsed < 0)
    {
      g_warning ("System clock seemed to go backwards?");
      elapsed = G_MAXDOUBLE;
    }

  if (elapsed > FADE_DURATION)
    {
      gdk_draw_pixbuf (fadeout->draw_window,
         fadeout->gc,
         fadeout->end_pb,
         0, 0,
         0, 0,
         fadeout->area.width,
         fadeout->area.height,
         GDK_RGB_DITHER_NONE,
         0, 0);

      return FALSE;
    }

  percent = elapsed / FADE_DURATION;

  get_current_frame (fadeout, 1.0 - percent);
  gdk_draw_pixbuf (fadeout->draw_window,
     fadeout->gc,
     fadeout->frame,
     0, 0,
     0, 0,
     fadeout->area.width,
     fadeout->area.height,
     GDK_RGB_DITHER_NONE,
     0, 0);

  gdk_flush ();

  return TRUE;
}

static void
hide_fadeout_windows (void)
{
  GList *l;

  for (l = fadeout_windows; l; l = l->next)
    {
      gdk_window_hide (GDK_WINDOW (l->data));
      g_object_unref (l->data);
    }

  g_list_free (fadeout_windows);
  fadeout_windows = NULL;
}

static gboolean
fadein_callback (FadeoutData *fadeout)
{
  GTimeVal current_time;
  double elapsed, percent;

  g_get_current_time (&current_time);
  elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
       (current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;

  if (elapsed < 0)
    {
      g_warning ("System clock seemed to go backwards?");
      elapsed = G_MAXDOUBLE;
    }

  if (elapsed > FADE_DURATION)
    {
      gdk_draw_pixbuf (fadeout->draw_window,
         fadeout->gc,
         fadeout->end_pb,
         0, 0,
         0, 0,
         fadeout->area.width,
         fadeout->area.height,
         GDK_RGB_DITHER_NONE,
         0, 0);

      g_object_unref (fadeout->gc);
      g_object_unref (fadeout->start_pb);
      g_object_unref (fadeout->end_pb);
      g_object_unref (fadeout->frame);

      g_free (fadeout);

      hide_fadeout_windows ();

      return FALSE;
    }

  percent = elapsed / FADE_DURATION;

  get_current_frame (fadeout, percent);
  gdk_draw_pixbuf (fadeout->draw_window,
     fadeout->gc,
     fadeout->frame,
     0, 0,
     0, 0,
     fadeout->area.width,
     fadeout->area.height,
     GDK_RGB_DITHER_NONE,
     0, 0);

  gdk_flush ();

  return TRUE;
}

static void
fadeout_screen (GdkScreen *screen,
  int        monitor)
{
  GdkWindowAttr attr;
  int attr_mask;
  GdkGCValues values;
  FadeoutData *fadeout;

  fadeout = g_new (FadeoutData, 1);

  fadeout->screen = screen;
  fadeout->monitor = monitor;

  fadeout->area.x = gsm_screen_get_x (screen, monitor);
  fadeout->area.y = gsm_screen_get_y (screen, monitor);
  fadeout->area.width = gsm_screen_get_width (screen, monitor);
  fadeout->area.height = gsm_screen_get_height (screen, monitor);

  fadeout->root_window = gdk_screen_get_root_window (screen);
  attr.window_type = GDK_WINDOW_CHILD;
  attr.x = fadeout->area.x;
  attr.y = fadeout->area.y;
  attr.width = fadeout->area.width;
  attr.height = fadeout->area.height;
  attr.wclass = GDK_INPUT_OUTPUT;
  attr.visual = gdk_screen_get_system_visual (fadeout->screen);
  attr.colormap = gdk_screen_get_default_colormap (fadeout->screen);
  attr.override_redirect = TRUE;
  attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR;

  fadeout->draw_window = gdk_window_new (fadeout->root_window, &attr, attr_mask);
  fadeout_windows = g_list_prepend (fadeout_windows, fadeout->draw_window);

  fadeout->start_pb = gdk_pixbuf_get_from_drawable (NULL,
          fadeout->root_window,
          NULL,
          fadeout->area.x,
          fadeout->area.y,
          0, 0,
          fadeout->area.width,
          fadeout->area.height);

  fadeout->end_pb = gdk_pixbuf_copy (fadeout->start_pb);
  darken_pixbuf (fadeout->end_pb);

  fadeout->frame = gdk_pixbuf_copy (fadeout->start_pb);
  fadeout->rowstride = gdk_pixbuf_get_rowstride (fadeout->start_pb);

  fadeout->start_p = gdk_pixbuf_get_pixels (fadeout->start_pb);
  fadeout->end_p   = gdk_pixbuf_get_pixels (fadeout->end_pb);
  fadeout->frame_p = gdk_pixbuf_get_pixels (fadeout->frame);

  values.subwindow_mode = GDK_INCLUDE_INFERIORS;

  fadeout->gc = gdk_gc_new_with_values (fadeout->root_window, &values, GDK_GC_SUBWINDOW);

  gdk_window_set_back_pixmap (fadeout->draw_window, NULL, FALSE);
  gdk_window_show (fadeout->draw_window);
  gdk_draw_pixbuf (fadeout->draw_window,
     fadeout->gc,
     fadeout->frame,
     0, 0,
     0, 0,
     fadeout->area.width,
     fadeout->area.height,
     GDK_RGB_DITHER_NONE,
     0, 0);

  g_get_current_time (&fadeout->start_time);
  g_idle_add ((GSourceFunc) fadeout_callback, fadeout);

  fade_data = fadeout;
}

/* End of 'stolen' code */

#define GRAB_TRIES 16
#define GRAB_WAIT 250 /* milliseconds */

typedef enum
  {
    FAILED_GRAB_MOUSE,
    FAILED_GRAB_KEYBOARD
  } FailedGrabWhat;

void
report_failed_grab (FailedGrabWhat what)
{
  GtkWidget *dialog;

  dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
    "message-type", GTK_MESSAGE_WARNING,
    "buttons", GTK_BUTTONS_CLOSE,
    NULL);

  switch (what)
    {
    case FAILED_GRAB_MOUSE:
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG(dialog),
         _("<b><big>Could not grab your mouse.</big></b>"
           "/n/n"
           "A malicious client may be eavesdropping "
           "on your session or you may have just clicked "
           "a menu or some application just decided to get "
           "focus."
           "/n/n"
           "Try again."));

      break;
    case FAILED_GRAB_KEYBOARD:
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG(dialog),
         _("<b><big>Could not grab your keyboard.</big></b>"
           "/n/n"
           "A malicious client may be eavesdropping "
           "on your session or you may have just clicked "
           "a menu or some application just decided to get "
           "focus."
           "/n/n"
           "Try again."));
      break;
    }

  gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
  gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_destroy (dialog);

  while (gtk_events_pending ())
    gtk_main_iteration ();

}

int
grab_keyboard_and_mouse (GtkWidget *dialog)
{
  GdkGrabStatus status;
  gint grab_tries = 0;
  gint lock = -1;

  gchar *fname = g_strdup_printf ("%s/.gksu.lock", getenv ("HOME"));
  pid_t pid = test_lock (fname);

  if (pid != 0)
    {
      g_warning ("Lock taken by pid: %i. Exiting.", pid);
      exit (0);
    }

  lock = get_lock(fname);
  if( lock < 0)
    g_warning ("Unable to create lock file.");
  g_free (fname);

  fadeout_screen (gdk_screen_get_default (), 0);
  gtk_widget_show_all (dialog);

  /* reset cursor */
  gdk_window_set_cursor(dialog->window, gdk_cursor_new(GDK_LEFT_PTR));

  for(;;)
    {
      status = gdk_pointer_grab ((GTK_WIDGET(dialog))->window, TRUE, 0, NULL,
     NULL, GDK_CURRENT_TIME);
      if (status == GDK_GRAB_SUCCESS)
 break;
      usleep (GRAB_WAIT * 1000);
      if (++grab_tries > GRAB_TRIES)
 {
   gtk_widget_hide (dialog);
   g_get_current_time (&fade_data->start_time);
   while (fadein_callback (fade_data) != FALSE);
   report_failed_grab (FAILED_GRAB_MOUSE);
   exit (1);
   break;
 }
    }

  for(;;)
    {
      status = gdk_keyboard_grab ((GTK_WIDGET(dialog))->window,
      FALSE, GDK_CURRENT_TIME);
      if (status == GDK_GRAB_SUCCESS)
 break;

      usleep(GRAB_WAIT * 1000);

      if (++grab_tries > GRAB_TRIES)
 {
   gtk_widget_hide (dialog);
   g_get_current_time (&fade_data->start_time);
   while (fadein_callback (fade_data) != FALSE);
   report_failed_grab (FAILED_GRAB_KEYBOARD);
   exit (1);
   break;
 }
    }

  /* we "raise" the window because there is a race here for
   * focus-follow-mouse and auto-raise WMs that may put the window
   * in the background and confuse users
   */
  gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);

  while (gtk_events_pending ())
    gtk_main_iteration ();

  return lock;
}

void
ungrab_keyboard_and_mouse (int lock)
{
  /* Ungrab */
  gdk_pointer_ungrab(GDK_CURRENT_TIME);
  gdk_keyboard_ungrab(GDK_CURRENT_TIME);
  gdk_flush();

  g_get_current_time (&fade_data->start_time);
  while (fadein_callback (fade_data) != FALSE);

  close(lock);
}

static gchar*
get_gnome_keyring_password (GksuContext *context)
{
  GnomeKeyringAttributeList *attributes;
  GnomeKeyringAttribute attribute;
  GnomeKeyringResult result;
  GList *list;

  attributes = gnome_keyring_attribute_list_new ();

  attribute.name = g_strdup ("user");
  attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
  attribute.value.string = g_strdup (gksu_context_get_user (context));
  g_array_append_val (attributes, attribute);

  attribute.name = g_strdup ("type");
  attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
  attribute.value.string = g_strdup ("local");
  g_array_append_val (attributes, attribute);

  attribute.name = g_strdup ("creator");
  attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
  attribute.value.string = g_strdup ("gksu");
  g_array_append_val (attributes, attribute);

  list = g_list_alloc();

  result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
       attributes,
       &list);
  gnome_keyring_attribute_list_free (attributes);
  if (
      (result == GNOME_KEYRING_RESULT_OK) &&
      (g_list_length(list) >= 1)
      )
    {
      GnomeKeyringFound *found = list->data;
      gint password_length = strlen (found->secret);
      gchar *password;

      password = g_locale_from_utf8 (found->secret,
         password_length,
         NULL, NULL, NULL);
      password_length = strlen (password);

      if (password[password_length-1] == '/n')
 password[password_length-1] = '/0';
      return password;
    }

  return NULL;
}

static void
keyring_create_item_cb (GnomeKeyringResult result,
                        guint32 id, gpointer keyring_loop)
{
  g_main_loop_quit (keyring_loop);
}

static void
set_gnome_keyring_password (GksuContext *context, gchar *password)
{
  GConfClient *gconf_client;
  gboolean save_to_keyring;

  gconf_client = context->gconf_client;
  save_to_keyring = gconf_client_get_bool (gconf_client, BASE_PATH"save-to-keyring", NULL);

  if (password && save_to_keyring)
    {
      static GMainLoop *keyring_loop = NULL;
      GnomeKeyringAttributeList *attributes;
      GnomeKeyringAttribute attribute;
      GnomeKeyringResult result;

      gchar *keyring_name;
      gchar *key_name;

      attributes = gnome_keyring_attribute_list_new ();

      attribute.name = g_strdup ("user");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup (gksu_context_get_user (context));
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("type");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("local");
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("creator");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("gksu");
      g_array_append_val (attributes, attribute);

      key_name = g_strdup_printf ("Local password for user %s",
      gksu_context_get_user (context));

      keyring_loop = g_main_loop_new (NULL, FALSE);

      keyring_name = gconf_client_get_string (gconf_client, BASE_PATH"save-keyring", NULL);
      if (keyring_name == NULL)
 keyring_name = g_strdup ("session");

      /* make sure the keyring exists; if an error occurs, use
         the session keyring */
      result = gnome_keyring_create_sync(keyring_name, NULL);
      if ((result != GNOME_KEYRING_RESULT_OK) &&
   (result != GNOME_KEYRING_RESULT_ALREADY_EXISTS))
 keyring_name = g_strdup ("session");

      gnome_keyring_item_create (keyring_name,
     GNOME_KEYRING_ITEM_GENERIC_SECRET,
     key_name,
     attributes,
     password,
     TRUE,
     keyring_create_item_cb,
     keyring_loop, NULL);
      gnome_keyring_attribute_list_free (attributes);
      g_free (keyring_name);
      g_main_loop_run (keyring_loop);
    }
}

static void
unset_gnome_keyring_password (GksuContext *context)
{
  GConfClient *gconf_client;
  gboolean save_to_keyring;

  GnomeKeyringAttributeList *attributes;
  GnomeKeyringAttribute attribute;
  GnomeKeyringResult result;
  GList *list;

  gconf_client = context->gconf_client;
  save_to_keyring = gconf_client_get_bool (gconf_client, BASE_PATH"save-to-keyring", NULL);

  if (save_to_keyring)
    {
      attributes = gnome_keyring_attribute_list_new ();

      attribute.name = g_strdup ("user");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup (gksu_context_get_user (context));
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("type");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("local");
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("creator");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("gksu");
      g_array_append_val (attributes, attribute);

      list = g_list_alloc();

      result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
           attributes,
           &list);
      gnome_keyring_attribute_list_free (attributes);
      if (
   (result == GNOME_KEYRING_RESULT_OK) &&
   (g_list_length(list) == 1)
   )
 {
   GnomeKeyringFound *found = list->data;

   gnome_keyring_item_delete_sync (found->keyring,
       found->item_id);
 }
    }
}

void
get_configuration_options (GksuContext *context)
{
  GConfClient *gconf_client = context->gconf_client;
  gboolean force_grab;

  context->grab = !gconf_client_get_bool (gconf_client, BASE_PATH "disable-grab",
       NULL);
  force_grab = gconf_client_get_bool (gconf_client, BASE_PATH "force-grab",
          NULL);
  if (force_grab)
    context->grab = TRUE;

  context->sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
           NULL);
}

/**
 * su_ask_password:
 * @context: a #GksuContext
 * @prompt: the prompt that should be used instead of "Password: "
 * @data: data that is passed by gksu_*_full
 * @error: a pointer to pointer #GError that will be filled with
 * data if an error happens.
 *
 * This is a convenience function to create a #GksuuiDialog and
 * request the password.
 *
 * Returns: a newly allocated gchar containing the password
 * or NULL if an error happens or the user cancels the action
 */
static gchar*
su_ask_password (GksuContext *context, gchar *prompt,
   gpointer data, GError **error)
{
  GtkWidget *dialog = NULL;
  gchar *msg;
  gchar *password = NULL, *tmp = NULL;
  int retvalue = 0;
  int lock = 0;
  GQuark gksu_quark;

  gksu_quark = g_quark_from_string (PACKAGE);

  if (context->grab)
    dialog = g_object_new (GKSUUI_TYPE_DIALOG,
      "type", GTK_WINDOW_POPUP,
      "sudo-mode", context->sudo_mode,
      NULL);
  else
    dialog = gksuui_dialog_new (context->sudo_mode);

  if (prompt)
    gksuui_dialog_set_prompt (GKSUUI_DIALOG(dialog), _(prompt));

  if (context->message)
    gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), context->message);
  else
    {
      gchar *command = NULL;
      if (context->description)
 command = context->description;
      else
 command = context->command;

      if (context->sudo_mode)
 {
   if (!strcmp(context->user, "root"))
     msg = g_strdup_printf (_("<b><big>Enter your password to perform"
         " administrative tasks</big></b>/n/n"
         "The application '%s' lets you "
         "modify essential parts of your "
         "system."),
       command);
   else
     msg = g_strdup_printf (_("<b><big>Enter your password to run "
         "the application '%s' as user %s"
         "</big></b>"),
       command, context->user);
 }
      else
 {
        if (strcmp(gksu_context_get_user (context), "root") == 0)
          msg = g_strdup_printf (_("<b><big>Enter the administrative password"
                                   "</big></b>/n/n"
                                   "The application '%s' lets you "
                                   "modify essential parts of your "
                                   "system."),
       command);
        else
          msg = g_strdup_printf (_("<b><big>Enter the password of %s to run "
                                   "the application '%s'"
                                   "</big></b>"),
       command, context->user);
      }

      gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), msg);
      g_free (msg);
    }

  if (context->alert)
    gksuui_dialog_set_alert (GKSUUI_DIALOG(dialog), context->alert);

  if (context->grab)
    lock = grab_keyboard_and_mouse (dialog);
  retvalue = gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_hide (dialog);
  if (context->grab)
    ungrab_keyboard_and_mouse (lock);

  while (gtk_events_pending ())
    gtk_main_iteration ();

  if (retvalue != GTK_RESPONSE_OK)
    {
      switch (retvalue)
 {
 case GTK_RESPONSE_CANCEL:
 case GTK_RESPONSE_DELETE_EVENT:
   g_set_error (error, gksu_quark,
         GKSU_ERROR_CANCELED,
         _("Password prompt canceled."));
   if (context->sn_context)
     gksu_context_launch_complete (context);
 }

      gtk_widget_destroy (dialog);
      while (gtk_events_pending ())
 gtk_main_iteration ();

      return NULL;
    }

  tmp = gksuui_dialog_get_password (GKSUUI_DIALOG(dialog));
  password = g_locale_from_utf8 (tmp, strlen (tmp), NULL, NULL, NULL);
  g_free (tmp);

  gtk_widget_destroy (dialog);
  while (gtk_events_pending ())
    gtk_main_iteration ();

  return password;
}

static void
cb_toggled_cb (GtkWidget *button, gpointer data)
{
  GConfClient *gconf_client;
  gchar *key;
  gboolean toggled;
  gchar *key_name;

  g_return_if_fail (data != NULL);

  key_name = (gchar*)data;

  gconf_client = gconf_client_get_default ();
  toggled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));

  key = g_strdup_printf (BASE_PATH "%s", key_name);

  if (!strcmp (key_name, "display-no-pass-info"))
    {
      /* the meaning of the key is the exact opposite of the meaning
  of the answer - when the check button is checked the key must
  be off
      */
      gconf_client_set_bool (gconf_client, key, !toggled, NULL);
    }
  else
    gconf_client_set_bool (gconf_client, key, toggled, NULL);

  g_object_unref (gconf_client);

  g_free (key);
}

void
no_pass (GksuContext *context, gpointer data)
{
  GtkWidget *dialog;
  GtkWidget *alignment;
  GtkWidget *check_button;

  gchar *command = NULL;

  if (context->description)
    command = context->description;
  else
    command = context->command;

  dialog = gtk_message_dialog_new_with_markup (NULL, 0,
            GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
            _("<b><big>Granted permissions without asking "
       "for password</big></b>"
       "/n/n"
       "The '%s' program was started with "
       "the privileges of the %s user without "
       "the need to ask for a password, due to "
       "your system's authentication mechanism "
       "setup."
       "/n/n"
       "It is possible that you are being allowed "
       "to run specific programs as user %s "
       "without the need for a password, or that "
       "the password is cached."
       "/n/n"
       "This is not a problem report; it's "
       "simply a notification to make sure "
       "you are aware of this."),
            command,
            context->user,
            context->user);

  alignment = gtk_alignment_new (0.5, 0.5, 0.6, 1);
  gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), alignment, TRUE, TRUE, 2);

  check_button = gtk_check_button_new_with_mnemonic (_("Do _not display this message again"));
  g_signal_connect (G_OBJECT(check_button), "toggled",
      G_CALLBACK(cb_toggled_cb), "display-no-pass-info");
  gtk_container_add (GTK_CONTAINER(alignment), check_button);

  gtk_widget_show_all (dialog);
  gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_destroy (GTK_WIDGET(dialog));

  while (gtk_events_pending ())
    gtk_main_iteration ();
}

static void
gksu_prompt_grab (GksuContext *context)
{
  GtkWidget *d;

  d = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_QUESTION,
       GTK_BUTTONS_YES_NO,
       _("<b>Would you like your screen to be /"grabbed/"/n"
         "while you enter the password?</b>"
         "/n/n"
         "This means all applications will be paused to avoid/n"
         "the eavesdropping of your password by a a malicious/n"
         "application while you type it."));

  if (gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_NO)
    context->grab = FALSE;
  else
    context->grab = TRUE;

  gtk_widget_destroy (d);
}

static void
nullify_password (gchar *pass)
{
  if (pass)
    {
      memset(pass, 0, strlen(pass));
      g_free (pass);
    }
  pass = NULL;
}

static gchar *
get_process_name (pid_t pid)
{
  static gboolean init;
  glibtop_proc_state buf;

  if (!init) {
    glibtop_init();
    init = TRUE;
  }

  glibtop_get_proc_state (&buf, pid);
  return strdup(buf.cmd);
}

static gchar *
get_xauth_token (GksuContext *context, gchar *display)
{
  gchar *xauth_bin = NULL;
  FILE *xauth_output;
  gchar *tmp = NULL;
  gchar *xauth = g_new0 (gchar, 256);

  /* find out where the xauth binary is located */
  if (g_file_test ("/usr/bin/xauth", G_FILE_TEST_IS_EXECUTABLE))
    xauth_bin = "/usr/bin/xauth";
  else if (g_file_test ("/usr/X11R6/bin/xauth", G_FILE_TEST_IS_EXECUTABLE))
    xauth_bin = "/usr/X11R6/bin/xauth";
  else
    {
      fprintf (stderr,
        "Failed to obtain xauth key: xauth binary not found "
        "at usual locations");

      return NULL;
    }

  /* get the authorization token */
  tmp = g_strdup_printf ("%s list %s | "
    "head -1 | awk '{ print $3 }'",
    xauth_bin,
    display);
  if ((xauth_output = popen (tmp, "r")) == NULL)
    {
      fprintf (stderr,
        "Failed to obtain xauth key: %s",
        strerror(errno));
      return NULL;
    }
  fread (xauth, sizeof(char), 255, xauth_output);
  pclose (xauth_output);
  g_free (tmp);

  if (context->debug)
    {
      fprintf(stderr,
       "xauth: -%s-/n"
       "display: -%s-/n",
       xauth, display);
    }

  return xauth;
}

/**
 * prepare_xauth:
 *
 * Sets up the variables with values for the $DISPLAY
 * environment variable and xauth-related stuff. Also
 * creates a temporary directory to hold a .Xauthority
 *
 * Returns: TRUE if it suceeds, FALSE if it fails.
 */
static int
prepare_xauth (GksuContext *context)
{
  gchar *display = NULL;
  gchar *xauth = NULL;

  display = g_strdup (getenv ("DISPLAY"));
  xauth = get_xauth_token (context, display);
  if (xauth == NULL)
    {
      g_free (display);
      return FALSE;
    }

  /* If xauth is the empty string, then try striping the
   * hostname part of the DISPLAY string for getting the
   * auth token; this is needed for ssh-forwarded usage
   */
  if (!strcmp ("", xauth))
    {
      gchar *cut_display = NULL;

      g_free (xauth);
      cut_display = g_strdup (g_strrstr (display, ":"));
      xauth = get_xauth_token (context, cut_display);

      g_free (display);
      display = cut_display;
    }

  context->xauth = xauth;
  context->display = display;

  if (context->debug)
    {
      fprintf(stderr,
       "final xauth: -%s-/n"
       "final display: -%s-/n",
       context->xauth, context->display);
    }

  return TRUE;
}

/* Write all of buf, even if write(2) is interrupted. */
static ssize_t
full_write (int d, const char *buf, size_t nbytes)
{
  ssize_t r, w = 0;

  /* Loop until nbytes of buf have been written. */
  while (w < nbytes) {
    /* Keep trying until write succeeds without interruption. */
    do {
      r = write(d, buf + w, nbytes - w);
    } while (r < 0 && errno == EINTR);

    if (r < 0) {
      return -1;
    }

    w += r;
  }

  return w;
}

static gboolean
copy (const char *fn, const char *dir)
{
  int in, out;
  int r;
  char *newfn;
  char buf[BUFSIZ] = "";

  newfn = g_strdup_printf("%s/.Xauthority", dir);

  out = open(newfn, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (out == -1)
    {
      if (errno == EEXIST)
 fprintf (stderr,
   "Impossible to create the .Xauthority file: a file "
   "already exists. This might be a security issue; "
   "please investigate.");
      else
 fprintf (stderr,
   "Error copying '%s' to '%s': %s",
   fn, dir, strerror(errno));

      return FALSE;
    }

  in = open(fn, O_RDONLY);
  if (in == -1)
    {
      fprintf (stderr,
        "Error copying '%s' to '%s': %s",
        fn, dir, strerror(errno));
      return FALSE;
    }

  while ((r = read(in, buf, BUFSIZ)) > 0)
    {
      if (full_write(out, buf, r) == -1)
 {
   fprintf (stderr,
     "Error copying '%s' to '%s': %s",
     fn, dir, strerror(errno));
   return FALSE;
 }
    }

  if (r == -1)
    {
      fprintf (stderr,
        "Error copying '%s' to '%s': %s",
        fn, dir, strerror(errno));
      return FALSE;
    }

  return TRUE;
}

static gboolean
sudo_prepare_xauth (GksuContext *context)
{
  gchar template[] = "/tmp/" PACKAGE "-XXXXXX";
  gboolean error_copying = FALSE;
  gchar *xauth = NULL;

  context->dir = g_strdup (mkdtemp(template));
  if (!context->dir)
    {
      fprintf (stderr, strerror(errno));
      return FALSE;
    }

  xauth = g_strdup(g_getenv ("XAUTHORITY"));
  if (xauth == NULL)
    xauth = g_strdup_printf ("%s/.Xauthority", g_get_home_dir());

  error_copying = !copy (xauth, context->dir);
  g_free (xauth);

  if (error_copying)
    return FALSE;

  return TRUE;
}

static void
sudo_reset_xauth (GksuContext *context, gchar *xauth,
    gchar *xauth_env)
{
  /* reset the env var as it was before or clean it  */
  if (xauth_env)
    setenv ("XAUTHORITY", xauth_env, TRUE);
  else
    unsetenv ("XAUTHORITY");

  if (context->debug)
    fprintf (stderr, "xauth: %s/nxauth_env: %s/ndir: %s/n",
      xauth, xauth_env, context->dir);

  unlink (xauth);
  rmdir (context->dir);

  g_free (xauth);
}

static void
startup_notification_initialize (GksuContext *context)
{
  SnDisplay *sn_display;
  sn_display = sn_display_new (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
          NULL, NULL);
  context->sn_context = sn_launcher_context_new (sn_display, gdk_screen_get_number (gdk_display_get_default_screen (gdk_display_get_default ())));
  sn_launcher_context_set_description (context->sn_context, _("Granting Rights"));
  sn_launcher_context_set_name (context->sn_context, g_get_prgname ());
}

/**
 * gksu_context_new
 *
 * This function should be used when creating a new #GksuContext to
 * pass to gksu_su_full or gksu_sudo_full. The #GksuContext must be
 * freed with gksu_context_free.
 *
 * Returns: a newly allocated #GksuContext
 */
GksuContext*
gksu_context_new ()
{
  GksuContext *context;

  context = g_new (GksuContext, 1);

  context->xauth = NULL;
  context->dir = NULL;
  context->display = NULL;

  context->gconf_client = gconf_client_get_default ();

  context->sudo_mode = FALSE;

  context->user = g_strdup ("root");
  context->command = NULL;

  context->login_shell = FALSE;
  context->keep_env = FALSE;

  context->description = NULL;
  context->message = NULL;
  context->alert = NULL;
  context->grab = TRUE;

  context->debug = FALSE;

  context->sn_context = NULL;
  context->sn_id = NULL;
 
  context->ref_count = 1;

  get_configuration_options (context);
  startup_notification_initialize (context);

  return context;
}

/**
 * gksu_context_set_user:
 * @context: the #GksuContext you want to modify
 * @username: the target username
 *
 * Sets up what user the command will be run as. The default
 * is root, but you can run the command as any user.
 *
 */
void
gksu_context_set_user (GksuContext *context, gchar *username)
{
  g_assert (username != NULL);

  if (context->user)
    g_free (context->user);
  context->user = g_strdup (username);
}

/**
 * gksu_context_get_user:
 * @context: the #GksuContext from which to grab the information
 *
 * Gets the user the command will be run as, as set
 * by gksu_context_set_user.
 *
 * Returns: a string with the user or NULL if not set.
 */
const gchar*
gksu_context_get_user (GksuContext *context)
{
  return context->user;
}

/**
 * gksu_context_set_command:
 * @context: the #GksuContext you want to modify
 * @command: the command that shall be ran
 *
 * Sets up what command will run with the target user.
 *
 */
void
gksu_context_set_command (GksuContext *context, gchar *command)
{
  g_assert (command != NULL);

  if (context->command)
    g_free (context->command);
  context->command = g_strdup (command);

  /* startup notification */
  sn_launcher_context_set_binary_name (context->sn_context,
           command);
}

/**
 * gksu_context_get_command:
 * @context: the #GksuContext from which to grab the information
 *
 * Gets the command that will be run, as set by
 * gksu_context_set_command.
 *
 * Returns: a string with the command or NULL if not set.
 */
const gchar*
gksu_context_get_command (GksuContext *context)
{
  return context->command;
}

/**
 * gksu_context_set_login_shell:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Should the shell in which the command will be run be
 * a login shell?
 */
void
gksu_context_set_login_shell (GksuContext *context, gboolean value)
{
  context->login_shell = value;
}

/**
 * gksu_context_get_login_shell:
 * @context: the #GksuContext from which to grab the information
 *
 * Finds out if the shell created by the underlying su process
 * will be a login shell.
 *
 * Returns: TRUE if the shell will be a login shell, FALSE otherwise.
 */
gboolean
gksu_context_get_login_shell (GksuContext *context)
{
  return context->login_shell;
}

/**
 * gksu_context_set_keep_env:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Should the environment be kept as it is? Defaults to
 * TRUE. Notice that setting this to FALSE may cause the
 * X authorization stuff to fail.
 */
void
gksu_context_set_keep_env (GksuContext *context, gboolean value)
{
  context->keep_env = value;
}

/**
 * gksu_context_get_keep_env:
 * @context: the #GksuContext from which to grab the information
 *
 * Finds out if the environment in which the program will be
 * run will be reset.
 *
 * Returns: TRUE if the environment is going to be kept,
 * FALSE otherwise.
 */
gboolean
gksu_context_get_keep_env (GksuContext *context)
{
  return context->keep_env;
}

/**
 * gksu_context_set_description:
 * @context: the #GksuContext you want to modify
 * @description: a string to set the description for
 *
 * Set the nice name for the action that is being run that the window
 * that asks for the password will have.  This is only meant to be
 * used if the default window is used, of course.
 */
void
gksu_context_set_description (GksuContext *context, gchar *description)
{
  if (context->description)
    g_free (context->description);
  context->description = g_strdup (description);
}

/**
 * gksu_context_get_description:
 * @context: the #GksuContext you want to get the description from.
 *
 * Get the description that the window will have when the
 * default function for requesting the password is
 * called.
 *
 * Returns: a string with the description or NULL if not set.
 */
gchar*
gksu_context_get_description (GksuContext *context)
{
  return context->description;
}

/**
 * gksu_context_set_message:
 * @context: the #GksuContext you want to modify
 * @message: a string to set the message for
 *
 * Set the message that the window that asks for the password will have.
 * This is only meant to be used if the default window is used, of course.
 */
void
gksu_context_set_message (GksuContext *context, gchar *message)
{
  if (context->message)
    g_free (context->message);
  context->message = g_strdup (message);
}

/**
 * gksu_context_get_message:
 * @context: the #GksuContext you want to get the message from.
 *
 * Get the message that the window will have when the
 * default function for requesting the password is
 * called.
 *
 * Returns: a string with the message or NULL if not set.
 */
gchar*
gksu_context_get_message (GksuContext *context)
{
  return context->message;
}

/**
 * gksu_context_set_alert:
 * @context: the #GksuContext you want to modify
 * @alert: a string to set the alert for
 *
 * Set the alert that the window that asks for the password will have.
 * This is only meant to be used if the default window is used, of course.
 * This alert should be used to display messages such as 'incorrect password',
 * for instance.
 */
void
gksu_context_set_alert (GksuContext *context, gchar *alert)
{
  if (context->alert)
    g_free (context->alert);
  context->alert = g_strdup (alert);
}

/**
 * gksu_context_get_alert:
 * @context: the #GksuContext you want to get the alert from.
 *
 * Get the alert that the window will have when the
 * default function for requesting the password is
 * called.
 *
 * Returns: a string with the alert or NULL if not set.
 */
gchar*
gksu_context_get_alert (GksuContext *context)
{
  return context->alert;
}

/**
 * gksu_context_set_debug:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Set up if debuging information should be printed.
 */
void
gksu_context_set_grab (GksuContext *context, gboolean value)
{
  context->grab = value;
}

/**
 * gksu_context_get_grab:
 * @context: the #GksuContext you want to ask whether a grab will be done.
 *
 * Returns TRUE if gksu has been asked to do a grab on keyboard and mouse
 * when asking for the password.
 *
 * Returns: TRUE if yes, FALSE otherwise.
 */
gboolean
gksu_context_get_grab (GksuContext *context)
{
  return context->grab;
}

/**
 * gksu_context_set_launcher_context:
 * @context: the #GksuContext you want to set the sn context in
 * @sn_context: the #SnLauncherContext you want to set
 *
 * Tell libgksu to use the given #SnLauncherContext for startup notification.
 * Currently the library will use this to set DESKTOP_STARTUP_ID in the
 * environment of the child and to issue initiate and complete events.
 * Notice that you don't need to use this function unless you want to
 * override gksu's default behavior on startup notification, since the
 * library will create its own context.
 *
 * Returns: the #SnLauncherContext which is set, or NULL if none was set
 */
void
gksu_context_set_launcher_context (GksuContext *context,
       SnLauncherContext *sn_context)
{
  if (context->sn_context)
    sn_launcher_context_unref (context->sn_context);
  context->sn_context = sn_context;
}

/**
 * gksu_context_get_launcher_context:
 * @context: the #GksuContext you want to get the sn context from
 *
 * Gets the current startup notification launcher context
 *
 * Returns: the #SnLauncherContext which is set, or NULL if none was set
 */
SnLauncherContext*
gksu_context_get_launcher_context (GksuContext *context)
{
  return context->sn_context;
}

/**
 * gksu_context_set_launcher_id:
 * @context: the #GksuContext you want to set the sn id in
 * @sn_context: the sn_id you want to set, as a #gchar
 */
void
gksu_context_set_launcher_id (GksuContext *context,
         gchar *sn_id)
{
  if (context->sn_id)
    g_free (context->sn_id);
  context->sn_id = g_strdup(sn_id);
}

/**
 * gksu_context_launch_initiate:
 * @context: the #GksuContext you want to initiate the launch for
 *
 * Initiates the launch, as far as Startup Notification is concerned;
 * This will only be used internally, usually.
 */
static void
gksu_context_launch_initiate (GksuContext *context)
{
  gchar *sid = NULL;
  guint32 launch_time = gdk_x11_display_get_user_time ((GdkDisplay*)GDK_DISPLAY());
  static gboolean initiated = FALSE;

  if (!initiated)
    initiated = TRUE;
  else
    return;

  sn_launcher_context_initiate (context->sn_context,
    g_get_prgname (),
    gksu_context_get_command (context),
    launch_time);

  sid = g_strdup_printf ("%s", sn_launcher_context_get_startup_id (context->sn_context));
  gksu_context_set_launcher_id (context, sid);

  if (context->debug)
    fprintf (stderr, "STARTUP_ID: %s/n", sid);
  setenv ("DESKTOP_STARTUP_ID", sid, TRUE);
  g_free(sid);
}

/**
 * gksu_context_launch_complete:
 * @context: the #GksuContext you want to complete the launch for
 *
 * Completes the launch, as far as Startup Notification is concerned;
 * This will only be used internally, usually.
 */
static void
gksu_context_launch_complete (GksuContext *context)
{
  sn_launcher_context_complete(context->sn_context);
}

/**
 * gksu_context_set_debug:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Set up if debuging information should be printed.
 */
void
gksu_context_set_debug (GksuContext *context, gboolean value)
{
  context->debug = value;
}

/**
 * gksu_context_get_debug:
 * @context: the #GksuContext from which to grab the information
 *
 * Finds out if the library is configured to print debuging
 * information.
 *
 * Returns: TRUE if it is, FALSE otherwise.
 */
gboolean
gksu_context_get_debug (GksuContext *context)
{
  return context->debug;
}

/**
 * gksu_context_free
 * @context: the #GksuContext to be freed.
 *
 * Frees the given #GksuContext.
 */
void
gksu_context_free (GksuContext *context)
{
  g_free (context->xauth);
  g_free (context->dir);
  g_free (context->display);

  g_object_unref (context->gconf_client);

  g_free (context->description);
  g_free (context->message);

  g_free (context->user);
  g_free (context->command);

  g_free (context);
}

/**
 * gksu_context_ref
 * @context: A #GksuContext struct.
 *
 * Increments the reference count of the given #GksuContext.
 */
GksuContext*
gksu_context_ref (GksuContext *context)
{
  context->ref_count++;
  return context;
}

/**
 * gksu_context_unref
 * @context: A #GksuContext struct.
 *
 * Decrements the reference count of the given #GksuContext struct,
 * freeing it if the reference count falls to zero.
 */
void
gksu_context_unref (GksuContext *context)
{
  if (--context->ref_count == 0)
    {
        gksu_context_free (context);
    }
}

GType
gksu_context_get_type (void)
{
  static GType type_gksu_context = 0;

  if (!type_gksu_context)
    type_gksu_context = g_boxed_type_register_static
      ("GksuContext",
       (GBoxedCopyFunc) gksu_context_ref,
       (GBoxedFreeFunc) gksu_context_unref);

  return type_gksu_context;
}

 


/**
 * gksu_su_full:
 * @context: a #GksuContext
 * @ask_pass: a #GksuAskPassFunc to be called when the lib determines
 * requesting a password is necessary; it may be NULL, in which case
 * the standard password request dialog will be used
 * @ask_pass_data: a #gpointer with user data to be passed to the
 * #GksuAskPasswordFunc
 * @pass_not_needed: a #GksuPassNotNeededFunc that will be called
 * when the command is being run without the need for requesting
 * a password; it will only be called if the display-no-pass-info
 * gconf key is enabled; NULL will have the standard dialog be shown
 * @pass_not_needed_data: a #gpointer with the user data to be passed to the
 * #GksuPasswordNotNeededFunc
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This could be considered one of the main functions in GKSu.
 * it is responsible for doing the 'user changing' magic calling
 * the #GksuAskPassFunc function to request a password if needed.
 * and the #GksuPassNotNeededFunc function if a password won't be
 * needed, so the application has the oportunity of warning the user
 * what it's doing.
 *
 * This function uses su as backend.
 *
 * Returns: TRUE if all went fine, FALSE if failed
 */
gboolean
gksu_su_full (GksuContext *context,
       GksuAskPassFunc ask_pass,
       gpointer ask_pass_data,
       GksuPassNotNeededFunc pass_not_needed,
       gpointer pass_not_needed_data,
       GError **error)
{
  GQuark gksu_quark;
  int i = 0;

  gchar auxcommand[] = PREFIX "/lib/" PACKAGE "/gksu-run-helper";

  int fdpty;
  pid_t pid;

  context->sudo_mode = FALSE;

  gksu_quark = g_quark_from_string (PACKAGE);

  if (!context->command)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_NOCOMMAND,
     _("gksu_run needs a command to be run, "
       "none was provided."));
      return FALSE;
    }

  if (!context->user)
    context->user = g_strdup ("root");

  if (!g_file_test (auxcommand, G_FILE_TEST_IS_EXECUTABLE))
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_HELPER,
     _("The gksu-run-helper command was not found or "
       "is not executable."));
      return FALSE;
    }

  if (!prepare_xauth (context))
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_XAUTH,
     _("Unable to copy the user's Xauthorization file."));
      return FALSE;
    }

  if (context->sn_context)
    gksu_context_launch_initiate (context);

  pid = forkpty(&fdpty, NULL, NULL, NULL);
  if (pid == 0)
    {
      gchar **cmd = g_malloc (sizeof(gchar*)*7);

      setsid();   // make us session leader
      cmd[i] = g_strdup ("/bin/su"); i++;
      if (context->login_shell)
 {
   cmd[i] = g_strdup ("-"); i++;
 }
      cmd[i] = g_strdup (context->user); i++;
      if (context->keep_env)
 {
   cmd[i] = g_strdup ("-p"); i++;
 }
      cmd[i] = g_strdup ("-c"); i++;

      /* needs to get X authorization prior to running the program */
      cmd[i] = g_strdup_printf ("%s /"%s/"", auxcommand,
    context->command); i++;

      cmd[i] = NULL;

      /* executes the command */
      if (execv (cmd[0], cmd) == -1)
 {
   fprintf (stderr,
     "Unable to run /bin/su: %s",
     strerror(errno));
 }

      for (i = 0 ; cmd[i] != NULL ; i++)
 g_free (cmd[i]);
      g_free(cmd);
    }
  else if (pid == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_FORK,
     _("Failed to fork new process: %s"),
     strerror(errno));
      return FALSE;
    }
  else
    {
      fd_set rfds;

      struct timeval tv;

      struct passwd *pwd = NULL;
      gint target_uid = -1;
      gint my_uid = 0;

      gchar buf[256] = {0};
      gint status;

      gchar *password = NULL;
      gchar *cmdline = NULL;
      gboolean password_needed = FALSE;
      gboolean used_gnome_keyring = FALSE;

      my_uid = getuid();
      pwd = getpwnam (context->user);
      if (pwd)
 target_uid = pwd->pw_uid;

      if (ask_pass == NULL)
 {
   ask_pass = su_ask_password;
 }

      if (pass_not_needed == NULL)
 {
   pass_not_needed = no_pass;
 }

      /* no need to ask for password if we're already root */
      if (my_uid != target_uid && my_uid)
 {
   gint count;
   struct termios tio;

   read (fdpty, buf, 255);
   if (context->debug)
     fprintf (stderr, "gksu_context_run: buf: -%s-/n", buf);

   /* make sure we notice that ECHO is turned off, if it gets
      turned off */
   tcgetattr (fdpty, &tio);
   for (count = 0; (tio.c_lflag & ECHO) && count < 15; count++)
     {
       usleep (1000);
       tcgetattr (fdpty, &tio);
     }

   if (!(tio.c_lflag & ECHO))
     {
       gboolean prompt_grab;
       prompt_grab = gconf_client_get_bool (context->gconf_client, BASE_PATH "prompt",
         NULL);

       if (prompt_grab)
  gksu_prompt_grab (context);

       /* try to get the password from the GNOME Keyring first */
       password = get_gnome_keyring_password (context);
       if (password == NULL)
  {
    password = ask_pass (context, buf, ask_pass_data, error);
    if (context->debug)
      {
        fprintf (stderr, "no password on keyring/n");
        if (password == NULL)
   fprintf (stderr, "no password from ask_pass!/n");
      }
  }
       else
  {
    if (context->debug)
      fprintf (stderr, "password from keyring found/n");
    used_gnome_keyring = TRUE;
  }
       if (password == NULL || (error && (*error)))
  {
    if (context->debug)
      fprintf (stderr, "gksu_su_full: problem getting password - getting out/n");
    if (context->debug && error)
      fprintf (stderr, "error: %s/n", (*error)->message);
    nullify_password (password);
    return TRUE;
  }

       write (fdpty, password, strlen(password) + 1);
       write (fdpty, "/n", 1);
       password_needed = TRUE;
     }
 }

      if (context->debug)
 fprintf (stderr, "DEBUG (run:after-pass) buf: -%s-/n", buf);
      if (strncmp (buf, "gksu", 4) && strncmp (buf, "su", 2))
 {
   /* drop the /n echoed on password entry if su did request
      a password */
   if (password_needed)
     read (fdpty, buf, 255);
   if (context->debug)
     fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-/n", buf);
   read (fdpty, buf, 255);
   if (context->debug)
     fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-/n", buf);
 }

      FD_ZERO (&rfds);
      FD_SET (fdpty, &rfds);
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      int loop_count = 0;
      while (TRUE)
 {
   int retval = 0;

   if (!strncmp (buf, "su", 2))
     {
       gchar **strings;

       if (password)
  {
    nullify_password (password);
    unset_gnome_keyring_password (context);
  }

       strings = g_strsplit (buf, ":", 2);
       if (strings[1] && !strncmp (strings[1], " Authentication failure", 23))
  {
    if (used_gnome_keyring)
      g_set_error (error, gksu_quark,
     GKSU_ERROR_WRONGAUTOPASS,
     _("Wrong password got from keyring."));
    else
      g_set_error (error, gksu_quark,
     GKSU_ERROR_WRONGPASS,
     _("Wrong password."));
  }
       g_strfreev (strings);

       if (context->debug)
  fprintf (stderr, "DEBUG (auth_failed) buf: -%s-/n", buf);

       break;
     }
   else if (!strncmp (buf, "gksu: waiting", 13))
     {
       gchar *line;

       if (password)
  {
    set_gnome_keyring_password (context, password);
    nullify_password (password);
  }

       if (context->debug)
  fprintf (stderr, "DEBUG (gksu: waiting) buf: -%s-/n", buf);

       line = g_strdup_printf ("gksu-run: %s/n", context->display);
       write (fdpty, line, strlen(line));
       g_free (line);

       line = g_strdup_printf ("gksu-run: %s/n", context->sn_id);
       write (fdpty, line, strlen(line));
       g_free (line);

       line = g_strdup_printf ("gksu-run: %s/n", context->xauth);
       write (fdpty, line, strlen(line));
       g_free (line);

       tcdrain (fdpty);

       bzero (buf, 256);
       read (fdpty, buf, 255);

       break;
     }

   retval = select (fdpty + 1, &rfds, NULL, NULL, &tv);
   if ((loop_count > 50) || (!retval))
     {
       gchar *emsg = NULL;
       gchar *converted_str = NULL;
       GError *converr = NULL;

       if (password)
  nullify_password (password);

       converted_str = g_locale_to_utf8 (buf, -1, NULL, NULL, &converr);
       if (converr)
  {
    g_warning (_("Failed to communicate with "
          "gksu-run-helper./n/n"
          "Received:/n"
          " %s/n"
          "While expecting:/n"
          " %s"), buf, "gksu: waiting");
    emsg = g_strdup_printf (_("Failed to communicate with "
         "gksu-run-helper./n/n"
         "Received bad string "
         "while expecting:/n"
         " %s"), "gksu: waiting");
    g_error_free (converr);
  }
       else
  emsg = g_strdup_printf (_("Failed to communicate with "
       "gksu-run-helper./n/n"
       "Received:/n"
       " %s/n"
       "While expecting:/n"
       " %s"), converted_str, "gksu: waiting");
       g_free (converted_str);

       g_set_error (error, gksu_quark, GKSU_ERROR_HELPER, emsg);
       g_free (emsg);

       if (context->debug)
  fprintf (stderr, "DEBUG (failed!) buf: -%s-/n", buf);

       return FALSE;
     }
   else if (retval == -1)
     {
       if (context->debug)
  fprintf (stderr, "DEBUG (select failed!) buf: %s/n", buf);
       return FALSE;
     }

   read (fdpty, buf, 255);
   if (context->debug)
     fprintf (stderr, "DEBUG (run:after-pass) buf: -%s-/n", buf);
   loop_count++;
 }

      if (!password_needed || used_gnome_keyring)
 {
   gboolean should_display;

   should_display = gconf_client_get_bool (context->gconf_client,
        BASE_PATH "display-no-pass-info", NULL);

   /* configuration tells us to show this message */
   if (should_display)
     {
       if (context->debug)
  fprintf (stderr, "Calling pass_not_needed window.../n");
       pass_not_needed (context, pass_not_needed_data);
       /* make sure it is displayed */
       while (gtk_events_pending ())
  gtk_main_iteration ();
     }
 }

      cmdline = g_strdup("bin/su");
      /* wait for the child process to end or become something other
  than su */
      while ((!waitpid (pid, &status, WNOHANG)) &&
      (g_str_has_suffix(cmdline, "bin/su")))
 {
   if (cmdline)
     g_free (cmdline);
   cmdline = get_process_name (pid);
   usleep(100000);
 }

      if (context->sn_context)
 gksu_context_launch_complete (context);

      bzero(buf, 256);
      while (read (fdpty, buf, 255) > 0)
 {
   fprintf (stderr, "%s", buf);
   bzero(buf, 256);
 }

      if (!WIFEXITED(status))
 waitpid(pid, &status, 0);

      if (WEXITSTATUS(status))
 {
   if(cmdline)
     {
       /* su already exec()ed something else, don't report
        * exit status errors in that case
        */
       if (!g_str_has_suffix (cmdline, "su"))
  {
    g_free (cmdline);
    return FALSE;
  }
       g_free (cmdline);
     }

   if (error == NULL)
     g_set_error (error, gksu_quark,
    GKSU_ERROR_CHILDFAILED,
    _("su terminated with %d status"),
    WEXITSTATUS(status));
 }
    }

  if (error)
    return FALSE;

  return TRUE;
}

/**
 * gksu_su
 * @command_line: the command line that will be executed as other user
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function is a wrapper for gksu_su_run_full. It will call it
 * without giving the callback functions, which leads to the standard
 * ones being called. A simple #GksuContext is created to hold the
 * user name and the command.
 *
 * Returns: TRUE if all went well, FALSE if an error happend.
 */
gboolean
gksu_su (gchar *command_line, GError **error)
{
  GksuContext *context = gksu_context_new ();
  gboolean retval;

  context->command = g_strdup (command_line);
  context->user = g_strdup ("root");
  retval = gksu_su_full (context,
    NULL, NULL,
    NULL, NULL,
    error);
  gksu_context_free (context);
  return retval;
}

static void
read_line (int fd, gchar *buffer, int n)
{
  gint counter = 0;
  gchar tmp[2] = {0};

  for (; counter < (n - 1); counter++)
    {
      read (fd, tmp, 1);
      if (tmp[0] == '/n')
 break;
      buffer[counter] = tmp[0];
    }
  buffer[counter+1] = '/0';
}

/**
 * gksu_sudo_full:
 * @context: a #GksuContext
 * @ask_pass: a #GksuAskPassFunc to be called when the lib determines
 * requesting a password is necessary; it may be NULL, in which case
 * the standard password request dialog will be used
 * @ask_pass_data: a #gpointer with user data to be passed to the
 * #GksuAskPasswordFunc
 * @pass_not_needed: a #GksuPassNotNeededFunc that will be called
 * when the command is being run without the need for requesting
 * a password; it will only be called if the display-no-pass-info
 * gconf key is enabled; NULL will have the standard dialog be shown
 * @pass_not_needed_data: a #gpointer with the user data to be passed to the
 * #GksuPasswordNotNeededFunc
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This could be considered one of the main functions in GKSu.
 * it is responsible for doing the 'user changing' magic calling
 * the #GksuAskPassFunc function to request a password if needed.
 * and the #GksuPassNotNeededFunc function if a password won't be
 * needed, so the application has the oportunity of warning the user
 * what it's doing.
 *
 * This function uses the sudo backend.
 *
 * Returns: TRUE if all went fine, FALSE if failed
 */
gboolean
gksu_sudo_full (GksuContext *context,
  GksuAskPassFunc ask_pass,
  gpointer ask_pass_data,
  GksuPassNotNeededFunc pass_not_needed,
  gpointer pass_not_needed_data,
  GError **error)
{
  char **cmd;
  char buffer[256] = {0};
  int argcount = 8;
  int i, j;

  GQuark gksu_quark;

  gchar *xauth = NULL,
    *xauth_env = NULL;

  pid_t pid;
  int status;
  FILE *infile, *outfile;
  int parent_pipe[2]; /* For talking to the parent */
  int child_pipe[2]; /* For talking to the child */

  context->sudo_mode = TRUE;

  gksu_quark = g_quark_from_string (PACKAGE);

  if (!context->command)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_NOCOMMAND,
     _("gksu_sudo_run needs a command to be run, "
       "none was provided."));
      return FALSE;
    }

  if (!context->user)
    context->user = g_strdup ("root");

  if (ask_pass == NULL)
    {
      if (context->debug)
 fprintf (stderr, "No ask_pass set, using default!/n");
      ask_pass = su_ask_password;
    }

  if (pass_not_needed == NULL)
    {
      pass_not_needed = no_pass;
    }

  /*
     FIXME: need to set GError in a more detailed way
  */
  if (!sudo_prepare_xauth (context))
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_XAUTH,
     _("Unable to copy the user's Xauthorization file."));
      return FALSE;
    }

  /* sets XAUTHORITY */
  xauth = g_strdup_printf ("%s/.Xauthority", context->dir);
  xauth_env = getenv ("XAUTHORITY");
  setenv ("XAUTHORITY", xauth, TRUE);
  if (context->debug)
    fprintf (stderr, "xauth: %s/n", xauth);

  /* set startup id */
  if (context->sn_context)
    gksu_context_launch_initiate (context);

  cmd = g_new (gchar *, argcount + 1);

  argcount = 0;

  /* sudo binary */
  cmd[argcount] = g_strdup("/usr/bin/sudo");
  argcount++;

  if (!context->keep_env)
    {
      /* Make sudo set $HOME */
      cmd[argcount] = g_strdup("-H");
      argcount++;
    }

  /* Make sudo read from stdin */
  cmd[argcount] = g_strdup("-S");
  argcount++;

  /* Make sudo use next arg as prompt */
  cmd[argcount] = g_strdup("-p");
  argcount++;

  /* prompt */
  cmd[argcount] = g_strdup("GNOME_SUDO_PASS");
  argcount++;

  /* Make sudo use the selected user */
  cmd[argcount] = g_strdup("-u");
  argcount++;

  /* user */
  cmd[argcount] = g_strdup(context->user);
  argcount++;

  /* sudo does not understand this if we do not use -H
     weird.
  */
  if (!context->keep_env)
    {
      /* Make sudo stop processing options */
      cmd[argcount] = g_strdup("--");
      argcount++;
    }

  {
    gchar *tmp_arg = g_malloc (sizeof(gchar)*1);
    gboolean inside_quotes = FALSE;

    tmp_arg[0] = '/0';

    for (i = j = 0; ; i++)
      {
 if ((context->command[i] == '/'') && (context->command[i-1] != '//'))
   {
     i = i + 1;
     inside_quotes = !inside_quotes;
   }

 if ((context->command[i] == ' ' && inside_quotes == FALSE)
     || context->command[i] == '/0')
   {
     tmp_arg = g_realloc (tmp_arg, sizeof(gchar)*(j+1));
     tmp_arg[j] = '/0';
     cmd = g_realloc (cmd, sizeof(gchar*) * (argcount + 1));
     cmd[argcount] = g_strdup (tmp_arg);

     g_free (tmp_arg);

     argcount = argcount + 1;
     j = 0;

     if (context->command[i] == '/0')
       break;

     tmp_arg = g_malloc (sizeof(gchar)*1);
     tmp_arg[0] = '/0';
   }
 else
   {
     if (context->command[i] == '//' && context->command[i+1] != '//')
       i = i + 1;
     tmp_arg = g_realloc (tmp_arg, sizeof(gchar)*(j+1));
     tmp_arg[j] = context->command[i];
     j = j + 1;
   }
      }
  }
  cmd = g_realloc (cmd, sizeof(gchar*) * (argcount + 1));
  cmd[argcount] = NULL;

  if (context->debug)
    {
      for (i = 0; cmd[i] != NULL; i++)
 fprintf (stderr, "cmd[%d]: %s/n", i, cmd[i]);
    }

  if ((pipe(parent_pipe)) == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
     _("Error creating pipe: %s"),
     strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }

  if ((pipe(child_pipe)) == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
     _("Error creating pipe: %s"),
     strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }

  pid = fork();
  if (pid == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_FORK,
     _("Failed to fork new process: %s"),
     strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }
  else if (pid == 0)
    {
      // Child
      setsid();   // make us session leader
      close(child_pipe[1]);
      dup2(child_pipe[0], STDIN_FILENO);
      dup2(parent_pipe[1], STDERR_FILENO);

      execv(cmd[0], cmd);

      g_set_error (error, gksu_quark, GKSU_ERROR_EXEC,
     _("Failed to exec new process: %s"),
     strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }
  else
    {
      gint counter = 0;
      gchar *cmdline = NULL;

      // Parent
      close(parent_pipe[1]);

      infile = fdopen(parent_pipe[0], "r");
      if (!infile)
 {
   g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
         _("Error opening pipe: %s"),
         strerror(errno));
   sudo_reset_xauth (context, xauth, xauth_env);
   return FALSE;
 }

      outfile = fdopen(child_pipe[1], "w");
      if (!outfile)
 {
   g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
         _("Error opening pipe: %s"),
         strerror(errno));
   sudo_reset_xauth (context, xauth, xauth_env);
   return FALSE;
 }

      /*
 we are expecting to receive a GNOME_SUDO_PASS
 if we don't there are two possibilities: an error
 or a password is not needed
      */
      fcntl (parent_pipe[0], F_SETFL, O_NONBLOCK);

      { /* no matter if we can read, since we're using
    O_NONBLOCK; this is just to avoid the prompt
    showing up after the read */
 fd_set rfds;
 struct timeval tv;

 FD_ZERO(&rfds);
 FD_SET(parent_pipe[0], &rfds);
 tv.tv_sec = 1;
 tv.tv_usec = 0;

 select (parent_pipe[0] + 1, &rfds, NULL, NULL, &tv);
      }

      /* Try hard to find the prompt; it may happen that we're
       * seeing sudo's lecture, or that some pam module is spitting
       * debugging stuff at the screen
       */
      for (counter = 0; counter < 50; counter++)
 {
   if (strncmp (buffer, "GNOME_SUDO_PASS", 15) == 0)
     break;

   read_line (parent_pipe[0], buffer, 256);

   if (context->debug)
     fprintf (stderr, "buffer: -%s-/n", buffer);
 }

      if (context->debug)
 fprintf (stderr, "brute force GNOME_SUDO_PASS ended.../n");

      if (strncmp(buffer, "GNOME_SUDO_PASS", 15) == 0)
 {
   gchar *password = NULL;
   gboolean prompt_grab;

   if (context->debug)
     fprintf (stderr, "Yeah, we're in.../n");

   prompt_grab = gconf_client_get_bool (context->gconf_client, BASE_PATH "prompt",
         NULL);
   if (prompt_grab)
     gksu_prompt_grab (context);

   password = ask_pass (context, _("Password: "),
          ask_pass_data, error);
   if (password == NULL || (*error))
     {
       nullify_password (password);
       return FALSE;
     }

   usleep (1000);

   fprintf (outfile, "%s/n", password);
   fclose (outfile);

   nullify_password (password);

   /* turn NONBLOCK off */
   fcntl(parent_pipe[0], F_SETFL, fcntl(parent_pipe[0], F_GETFL) & ~O_NONBLOCK);
   /* ignore the first newline that comes right after sudo receives
      the password */
   fgets (buffer, 255, infile);
   /* this is the status we are interessted in */
   fgets (buffer, 255, infile);
 }
      else
 {
   gboolean should_display;
   if (context->debug)
     fprintf (stderr, "No password prompt found; we'll assume we don't need a password./n");

   should_display = gconf_client_get_bool (context->gconf_client,
        BASE_PATH "display-no-pass-info", NULL);

   /* configuration tells us to show this message */
   if (should_display)
     {
       if (context->debug)
  fprintf (stderr, "Calling pass_not_needed window.../n");
       pass_not_needed (context, pass_not_needed_data);
       /* make sure it is displayed */
       while (gtk_events_pending ())
  gtk_main_iteration ();
     }

   fprintf (stderr, "%s", buffer);
 }

      if (!strcmp (buffer, "Sorry, try again./n"))
 g_set_error (error, gksu_quark, GKSU_ERROR_WRONGPASS,
       _("Wrong password."));
      else if (!strncmp (buffer, "Sorry, user ", 12))
 g_set_error (error, gksu_quark, GKSU_ERROR_NOT_ALLOWED,
       _("The underlying authorization mechanism (sudo) "
         "does not allow you to run this program. Contact "
         "the system administrator."));
      else
 {
   gchar *haystack = buffer;
   gchar *needle;

   needle = g_strstr_len (haystack, strlen (haystack), " ");
   if (needle && (needle + 1))
     {
       needle += 1;
       if (!strncmp (needle, "is not in", 9))
  g_set_error (error, gksu_quark, GKSU_ERROR_NOT_ALLOWED,
        _("The underlying authorization mechanism (sudo) "
          "does not allow you to run this program. Contact "
          "the system administrator."));
     }
 }

      cmdline = g_strdup("sudo");
      /* wait for the child process to end or become something other
  then sudo */
      while ((!waitpid (pid, &status, WNOHANG)) &&
      (g_str_has_suffix(cmdline, "sudo")))
 {
   if (cmdline)
     g_free (cmdline);
   cmdline = get_process_name (pid);
   usleep(100000);
 }

      if (context->sn_context)
 gksu_context_launch_complete (context);

      while (read (parent_pipe[0], buffer, 255) > 0)
 {
   fprintf (stderr, "%s", buffer);
   bzero(buffer, 256);
 }

      if (!WIFEXITED(status))
 waitpid(pid, &status, 0);
      sudo_reset_xauth (context, xauth, xauth_env);

      if (WEXITSTATUS(status))
 {
   if(cmdline)
     {
       /* sudo already exec()ed something else, don't report
        * exit status errors in that case
        */
       if (!g_str_has_suffix (cmdline, "sudo"))
  {
    g_free (cmdline);
    return FALSE;
  }
       g_free (cmdline);
     }
   if (error == NULL)
     g_set_error (error, gksu_quark,
    GKSU_ERROR_CHILDFAILED,
    _("sudo terminated with %d status"),
    WEXITSTATUS(status));
 }
    }

  /* if error is set we have found an error condition */
  if (error)
    return FALSE;

  return TRUE;
}

/**
 * gksu_sudo
 * @command_line: the command line that will be executed as other user
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function is a wrapper for gksu_sudo_run_full. It will call it
 * without giving the callback functions, which leads to the standard
 * ones being called. A simple #GksuContext is created to hold the
 * user name and the command.
 *
 * Returns: TRUE if all went well, FALSE if an error happend.
 */
gboolean
gksu_sudo (gchar *command_line,
    GError **error)
{
  GksuContext *context = gksu_context_new ();
  gboolean retval;

  context->command = g_strdup (command_line);
  context->user = g_strdup ("root");
  retval = gksu_sudo_full (context,
      NULL, NULL,
      NULL, NULL,
      error);
  gksu_context_free (context);

  return retval;
}

/**
 * gksu_run_full:
 * @context: a #GksuContext
 * @ask_pass: a #GksuAskPassFunc to be called when the lib determines
 * requesting a password is necessary; it may be NULL, in which case
 * the standard password request dialog will be used
 * @ask_pass_data: a #gpointer with user data to be passed to the
 * #GksuAskPasswordFunc
 * @pass_not_needed: a #GksuPassNotNeededFunc that will be called
 * when the command is being run without the need for requesting
 * a password; it will only be called if the display-no-pass-info
 * gconf key is enabled; NULL will have the standard dialog be shown
 * @pass_not_needed_data: a #gpointer with the user data to be passed to the
 * #GksuPasswordNotNeededFunc
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This function is a wrapper for gksu_sudo_full/gksu_su_full. It will
 * call one of them, depending on the GConf key that defines whether
 * the default behavior for gksu is su or sudo mode. This is the
 * recommended way of using the library functionality.
 *
 * Returns: TRUE if all went fine, FALSE if failed
 */
gboolean
gksu_run_full (GksuContext *context,
        GksuAskPassFunc ask_pass,
        gpointer ask_pass_data,
        GksuPassNotNeededFunc pass_not_needed,
        gpointer pass_not_needed_data,
        GError **error)
{
  GConfClient *gconf_client;
  gboolean sudo_mode;

  gconf_client = gconf_client_get_default ();
  sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
         NULL);
  g_object_unref (gconf_client);

  if (sudo_mode)
    return gksu_sudo_full (context, ask_pass, ask_pass_data,
      pass_not_needed, pass_not_needed_data,
      error);

  return gksu_su_full (context, ask_pass, ask_pass_data,
         pass_not_needed, pass_not_needed_data,
         error);
}

/**
 * gksu_run
 * @command_line: the command line that will be executed as other user
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function is a wrapper for gksu_sudo/gksu_su. It will call one
 * of them, depending on the GConf key that defines whether the default
 * behavior for gksu is su or sudo mode. This is the recommended way of
 * using the library functionality.
 *
 * Returns: FALSE if all went well, TRUE if an error happend.
 */
gboolean
gksu_run (gchar *command_line,
   GError **error)
{
  GConfClient *gconf_client;
  gboolean sudo_mode;

  gconf_client = gconf_client_get_default ();
  sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
         NULL);
  g_object_unref (gconf_client);

  if (sudo_mode)
    return gksu_sudo (command_line, error);

  return gksu_su (command_line, error);
}

/**
 * gksu_ask_password_full:
 * @context: a #GksuContext
 * @prompt: a prompt different from Password:
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This function uses the gksu infra-structure to request for a
 * password, but instead of passing it to su or sudo to run a command
 * it simply returns the password.
 *
 * Returns: a newly allocated string with the password;
 */
gchar*
gksu_ask_password_full (GksuContext *context, gchar *prompt,
   GError **error)
{
  gchar *ret_value = su_ask_password (context, _(prompt), NULL, error);
  if (context->sn_context)
    gksu_context_launch_complete (context);
  return ret_value;
}

/**
 * gksu_ask_password
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function uses the gksu infra-structure to request for a
 * password, but instead of passing it to su or sudo to run a command
 * it simply returns the password. This is just a convenience wrapper
 * for gksu_ask_password_full.
 *
 * Returns: a newly allocated string with the password;
 */
gchar*
gksu_ask_password (GError **error)
{
  GksuContext *context = gksu_context_new ();
  gchar* retval;

  context->user = g_strdup ("root");
  retval = gksu_ask_password_full (context, NULL, error);
  gksu_context_free (context);

  return retval;
}

结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值