Search packages:      
 

Sourcecode and documentation for gnome-volume-manager version 2.24.0-0ubuntu1
show bar | Show file versions

manager.c

00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
00002 /*
00003  * src/manager.c - GNOME Volume Manager
00004  *
00005  * Jeffrey Stedfast <fejj@novell.com>
00006  * Robert Love <rml@novell.com>
00007  *
00008  * gnome-volume-manager is a simple policy engine that implements a state
00009  * machine in response to events from HAL.  Responding to these events,
00010  * gnome-volume-manager implements automount, autorun, autoplay, automatic
00011  * photo management, and so on.
00012  *
00013  * Licensed under the GNU GPL v2.  See COPYING.
00014  *
00015  * (C) Copyright 2005-2008 Novell, Inc.
00016  */
00017 
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #include "config.h"
00021 #endif
00022 
00023 #include <stdio.h>
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <dirent.h>
00027 #include <signal.h>
00028 #include <unistd.h>
00029 #include <utmp.h>
00030 
00031 #include <locale.h>
00032 
00033 #ifdef HAVE_CODESET
00034 #include <langinfo.h>
00035 #endif
00036 
00037 #ifdef ENABLE_NOTIFY
00038 #include <sys/statvfs.h>
00039 #include <libnotify/notify.h>
00040 #endif
00041 
00042 #include <gnome.h>
00043 #include <glib/gi18n.h>
00044 #include <gconf/gconf-client.h>
00045 #include <gdk/gdkx.h>
00046 #include <dbus/dbus.h>
00047 #include <dbus/dbus-glib.h>
00048 #include <dbus/dbus-glib-lowlevel.h>
00049 #include <libhal.h>
00050 
00051 #include "gvm.h"
00052 
00053 #ifdef GVM_DEBUG
00054 # define dbg(fmt,arg...) fprintf(stderr, "%s/%d: " fmt,__FILE__,__LINE__,##arg)
00055 #else
00056 # define dbg(fmt,arg...) do { } while(0)
00057 #endif
00058 
00059 #define warn(fmt,arg...) g_warning("%s/%d: " fmt,__FILE__,__LINE__,##arg)
00060 
00061 #define NAUTILUS_COMMAND "nautilus -n --no-desktop %m"
00062 
00063 static struct gvm_configuration config;
00064 static DBusConnection *dbus_connection = NULL;
00065 static char *gnome_mount = NULL;
00066 static LibHalContext *hal_ctx;
00067 
00068 #ifdef ENABLE_AUTOMOUNT
00069 struct _MountPolicy {
00070       char *udi;
00071       int apply;
00072 };
00073 
00074 /* Table of UDI's for volumes being mounted by g-v-m that we need to apply policy to */
00075 static GHashTable *mount_table = NULL;
00076 static GHashTable *device_table = NULL;
00077 
00078 /* List of UDI's of all volumes mounted during the lifetime of the program */
00079 static GSList *mounted_volumes = NULL;
00080 #endif /* ENABLE_AUTOMOUNT */
00081 
00082 /* Table of active dialogs indexed by the udi of the device in question */
00083 static GHashTable *dialogs = NULL;
00084 
00085 /* List of UDIs of all mounted volumes being watched for low-disk notification */
00086 static GHashTable *statfs_mounts = NULL;
00087 
00088 /* Timeout for the callback to do statfs () on mounted volumes */
00089 static guint statfs_id = 0;
00090 
00091 
00092 typedef struct {
00093         gboolean notified;
00094         double last_notified;
00095       char *udi;
00096 } statfs_mount_info;
00097 
00098 #ifdef ENABLE_NOTIFY
00099 static void statfs_mount_info_add (const char *udi);
00100 static void statfs_mount_info_remove (const char *udi);
00101 static void statfs_mount_info_free (statfs_mount_info *info);
00102 static gboolean gvm_statfs_check_space (const char *udi, statfs_mount_info *info, gpointer user_data);
00103 #endif
00104 
00105 static gboolean gvm_dbus_init (void);
00106 static LibHalContext *gvm_hal_init (void);
00107 
00108 static gboolean gvm_user_is_local (void);
00109 static gboolean gvm_user_is_active (void);
00110 
00111 
00112 typedef enum {
00113       TYPE_BOOL,
00114       TYPE_STRING,
00115       TYPE_FLOAT
00116 } type_t;
00117 
00118 enum {
00119       FILEMANAGER,
00120       
00121 #ifdef ENABLE_AUTOMOUNT
00122       /* Storage */
00123       AUTOBROWSE,
00124       AUTOBURN,
00125       AUTOBURN_AUDIO_CD_COMMAND,
00126       AUTOBURN_DATA_CD_COMMAND,
00127       AUTOMOUNT_DRIVES,
00128       AUTOMOUNT_MEDIA,
00129       AUTOOPEN,
00130       AUTOOPEN_PATH,
00131       AUTORUN,
00132       AUTORUN_PATH,
00133       
00134       /* Multimedia */
00135       AUTOPLAY_CDA,
00136       AUTOPLAY_CDA_COMMAND,
00137       AUTOPLAY_DVD,
00138       AUTOPLAY_DVD_COMMAND,
00139       AUTOPLAY_VCD,
00140       AUTOPLAY_VCD_COMMAND,
00141       AUTOIPOD,
00142       AUTOIPOD_COMMAND,
00143 #endif /* ENABLE_AUTOMOUNT */
00144       
00145       /* Cameras */
00146       AUTOPHOTO,
00147       AUTOPHOTO_COMMAND,
00148       AUTOVIDEOCAM,
00149       AUTOVIDEOCAM_COMMAND,
00150       AUTOWEBCAM,
00151       AUTOWEBCAM_COMMAND,
00152       
00153       /* PDAs */
00154       AUTOPILOT,
00155       AUTOPILOT_COMMAND,
00156       AUTOPOCKETPC,
00157       AUTOPOCKETPC_COMMAND,
00158       
00159       /* Printers/Scanners */
00160       AUTOPRINTER,
00161       AUTOPRINTER_COMMAND,
00162       AUTOSCANNER,
00163       AUTOSCANNER_COMMAND,
00164       
00165       /* Input Devices */
00166       AUTOKEYBOARD,
00167       AUTOKEYBOARD_COMMAND,
00168       AUTOMOUSE,
00169       AUTOMOUSE_COMMAND,
00170       AUTOTABLET,
00171       AUTOTABLET_COMMAND,
00172       
00173       /* Notification */
00174       PERCENT_THRESHOLD,
00175       PERCENT_FREED,
00176       PERCENT_USED
00177 };
00178 
00179 static struct {
00180       char *key;
00181       type_t type;
00182       void *var;
00183 } gvm_settings[] = {
00184       { GCONF_ROOT "filemanager",               TYPE_STRING, &config.filemanager               },
00185 #ifdef ENABLE_AUTOMOUNT
00186       /* Storage */
00187       { GCONF_ROOT "autobrowse",                TYPE_BOOL,   &config.autobrowse                },
00188       { GCONF_ROOT "autoburn",                  TYPE_BOOL,   &config.autoburn                  },
00189       { GCONF_ROOT "autoburn_audio_cd_command", TYPE_STRING, &config.autoburn_audio_cd_command },
00190       { GCONF_ROOT "autoburn_data_cd_command",  TYPE_STRING, &config.autoburn_data_cd_command  },
00191       { GCONF_ROOT "automount_drives",          TYPE_BOOL,   &config.automount_drives          },
00192       { GCONF_ROOT "automount_media",           TYPE_BOOL,   &config.automount_media           },
00193       { GCONF_ROOT "autoopen",                  TYPE_BOOL,   &config.autoopen                  },
00194       { GCONF_ROOT "autoopen_path",             TYPE_STRING, &config.autoopen_path             },
00195       { GCONF_ROOT "autorun",                   TYPE_BOOL,   &config.autorun                   },
00196       { GCONF_ROOT "autorun_path",              TYPE_STRING, &config.autorun_path              },
00197       
00198       /* Multimedia */
00199       { GCONF_ROOT "autoplay_cda",              TYPE_BOOL,   &config.autoplay_cda              },
00200       { GCONF_ROOT "autoplay_cda_command",      TYPE_STRING, &config.autoplay_cda_command      },
00201       { GCONF_ROOT "autoplay_dvd",              TYPE_BOOL,   &config.autoplay_dvd              },
00202       { GCONF_ROOT "autoplay_dvd_command",      TYPE_STRING, &config.autoplay_dvd_command      },
00203       { GCONF_ROOT "autoplay_vcd",              TYPE_BOOL,   &config.autoplay_vcd              },
00204       { GCONF_ROOT "autoplay_vcd_command",      TYPE_STRING, &config.autoplay_vcd_command      },
00205       { GCONF_ROOT "autoipod",                  TYPE_BOOL,   &config.autoipod                  },
00206       { GCONF_ROOT "autoipod_command",          TYPE_STRING, &config.autoipod_command          },
00207 #endif /* ENABLE_AUTOMOUNT */
00208       
00209       /* Cameras */
00210       { GCONF_ROOT "autophoto",                 TYPE_BOOL,   &config.autophoto                 },
00211       { GCONF_ROOT "autophoto_command",         TYPE_STRING, &config.autophoto_command         },
00212       { GCONF_ROOT "autovideocam",              TYPE_BOOL,   &config.autovideocam              },
00213       { GCONF_ROOT "autovideocam_command",      TYPE_STRING, &config.autovideocam_command      },
00214       { GCONF_ROOT "autowebcam",                TYPE_BOOL,   &config.autowebcam                },
00215       { GCONF_ROOT "autowebcam_command",        TYPE_STRING, &config.autowebcam_command        },
00216       
00217       /* PDAs */
00218       { GCONF_ROOT "autopalmsync",              TYPE_BOOL,   &config.autopilot                 },
00219       { GCONF_ROOT "autopalmsync_command",      TYPE_STRING, &config.autopilot_command         },
00220       { GCONF_ROOT "autopocketpc",              TYPE_BOOL,   &config.autopocketpc              },
00221       { GCONF_ROOT "autopocketpc_command",      TYPE_STRING, &config.autopocketpc_command      },
00222       
00223       /* Printers/Scanners */
00224       { GCONF_ROOT "autoprinter",               TYPE_BOOL,   &config.autoprinter               },
00225       { GCONF_ROOT "autoprinter_command",       TYPE_STRING, &config.autoprinter_command       },
00226       { GCONF_ROOT "autoscanner",               TYPE_BOOL,   &config.autoscanner               },
00227       { GCONF_ROOT "autoscanner_command",       TYPE_STRING, &config.autoscanner_command       },
00228       
00229       /* Input Devices */
00230       { GCONF_ROOT "autokeyboard",              TYPE_BOOL,   &config.autokeyboard              },
00231       { GCONF_ROOT "autokeyboard_command",      TYPE_STRING, &config.autokeyboard_command      },
00232       { GCONF_ROOT "automouse",                 TYPE_BOOL,   &config.automouse                 },
00233       { GCONF_ROOT "automouse_command",         TYPE_STRING, &config.automouse_command         },
00234       { GCONF_ROOT "autotablet",                TYPE_BOOL,   &config.autotablet                },
00235       { GCONF_ROOT "autotablet_command",        TYPE_STRING, &config.autotablet_command        },
00236       
00237       /* Notification */
00238       { GCONF_ROOT "percent_threshold",         TYPE_FLOAT,  &config.percent_threshold         },
00239       { GCONF_ROOT "percent_used",              TYPE_FLOAT,  &config.percent_used              },
00240 };
00241 
00242 static GHashTable *gvm_settings_hash = NULL;
00243 
00244 
00245 struct _GvmPromptButton {
00246       const char *label;
00247       const char *stock;
00248       int response_id;
00249 };
00250 
00251 enum {
00252       GVM_RESPONSE_NONE,
00253       GVM_RESPONSE_RUN,
00254       GVM_RESPONSE_OPEN,
00255       GVM_RESPONSE_PLAY,
00256       GVM_RESPONSE_BROWSE,
00257       GVM_RESPONSE_SYNC_MUSIC,
00258       GVM_RESPONSE_IMPORT_PHOTOS,
00259       GVM_RESPONSE_WRITE_AUDIO_CD,
00260       GVM_RESPONSE_WRITE_DATA_CD,
00261 };
00262 
00263 static struct _GvmPromptButton GVM_BUTTONS_AUTORUN[] = {
00264       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00265       { N_("_Allow Auto-Run"), NULL, GVM_RESPONSE_RUN },
00266 };
00267 
00268 static struct _GvmPromptButton GVM_BUTTONS_AUTOOPEN[] = {
00269       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00270       { N_("_Open"), NULL, GVM_RESPONSE_OPEN },
00271 };
00272 
00273 static struct _GvmPromptButton GVM_BUTTONS_CAMERA[] = {
00274       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00275       { N_("Import _Photos"), NULL, GVM_RESPONSE_IMPORT_PHOTOS },
00276 };
00277 
00278 static struct _GvmPromptButton GVM_BUTTONS_STORAGE_CAMERA[] = {
00279       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00280       { N_("_Open Folder"), NULL, GVM_RESPONSE_BROWSE },
00281       { N_("Import _Photos"), NULL, GVM_RESPONSE_IMPORT_PHOTOS },
00282 };
00283 
00284 static struct _GvmPromptButton GVM_BUTTONS_IPOD_PHOTO[] = {
00285       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00286       { N_("Import _Photos"), NULL, GVM_RESPONSE_IMPORT_PHOTOS },
00287       { N_("Manage _Music"), NULL, GVM_RESPONSE_SYNC_MUSIC },
00288 };
00289 
00290 static struct _GvmPromptButton GVM_BUTTONS_MIXED_CD[] = {
00291       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00292       { N_("_Browse Files"), NULL, GVM_RESPONSE_BROWSE },
00293       { N_("_Play CD"), NULL, GVM_RESPONSE_PLAY },
00294 };
00295 
00296 static struct _GvmPromptButton GVM_BUTTONS_WRITE_CD[] = {
00297       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00298       { N_("Make _Audio CD"), NULL, GVM_RESPONSE_WRITE_AUDIO_CD },
00299       { N_("Make _Data CD"), NULL, GVM_RESPONSE_WRITE_DATA_CD },
00300 };
00301 
00302 static struct _GvmPromptButton GVM_BUTTONS_WRITE_DVD[] = {
00303       { N_("Ig_nore"), NULL, GTK_RESPONSE_CANCEL },
00304       { N_("Make _DVD"), NULL, GVM_RESPONSE_WRITE_DATA_CD },
00305 };
00306 
00307 typedef enum {
00308       GVM_PROMPT_AUTORUN,
00309       GVM_PROMPT_AUTOOPEN,
00310       GVM_PROMPT_IMPORT_CAMERA,
00311       GVM_PROMPT_IMPORT_STORAGE_CAMERA,
00312       GVM_PROMPT_IMPORT_PHOTOS,
00313       GVM_PROMPT_IPOD_PHOTO,
00314       GVM_PROMPT_CDA_EXTRA,
00315       GVM_PROMPT_WRITE_CDR,
00316       GVM_PROMPT_WRITE_DVD,
00317 } GvmPrompt;
00318 
00319 typedef struct _GvmPromptCtx GvmPromptCtx;
00320 typedef void (* GvmPromptCallback) (GvmPromptCtx *ctx, int action);
00321 
00322 struct _GvmPromptCtx {
00323       GvmPrompt prompt;
00324       
00325       GvmPromptCallback callback;
00326       
00327       char *udi;
00328       char *device;
00329       char *mount_point;
00330       char *path;
00331 };
00332 
00333 static struct {
00334       GtkDialogFlags flags;
00335       
00336       const char *icon;
00337       
00338       const char *help_uri;
00339       
00340       struct _GvmPromptButton *buttons;
00341       int n_buttons;
00342       
00343       int default_response;
00344       
00345       const char *title;
00346       const char *primary;
00347       const char *secondary;
00348       int secondary_has_args;
00349       int secondary_has_mockup;
00350       
00351       const char *ask_again_key;
00352       const char *ask_again_label;
00353 } gvm_prompts[] = {
00354       { 0, "gnome-fs-executable", NULL,
00355         GVM_BUTTONS_AUTORUN, G_N_ELEMENTS (GVM_BUTTONS_AUTORUN),
00356         GVM_RESPONSE_RUN,
00357         N_("Auto-Run Confirmation"),
00358         N_("Auto-run capability detected."),
00359         N_("Would you like to allow <b>'${0}'</b> to run?"), TRUE, TRUE,
00360         NULL, NULL },
00361       { 0, "gnome-fs-executable", NULL,
00362         GVM_BUTTONS_AUTOOPEN, G_N_ELEMENTS (GVM_BUTTONS_AUTOOPEN),
00363         GVM_RESPONSE_RUN,
00364         N_("Auto-Open Confirmation"),
00365         N_("Auto-Open capability detected."),
00366         N_("Would you like to open <b>'${0}'</b>?"), TRUE, TRUE,
00367         NULL, NULL },
00368       { 0, "camera-photo", NULL,
00369         GVM_BUTTONS_CAMERA, G_N_ELEMENTS (GVM_BUTTONS_CAMERA),
00370         GVM_RESPONSE_IMPORT_PHOTOS,
00371         N_("Camera Import"),
00372         N_("A camera has been detected."),
00373         N_("There are photos on the camera. Would you like to add these pictures to your album?"), FALSE, FALSE,
00374         GCONF_ROOT "prompts/camera_import_photos", N_("_Always perform this action") },
00375       { 0, "camera-photo", NULL,
00376         GVM_BUTTONS_STORAGE_CAMERA, G_N_ELEMENTS (GVM_BUTTONS_STORAGE_CAMERA),
00377         GVM_RESPONSE_IMPORT_PHOTOS,
00378         N_("Camera Import"),
00379         N_("A camera has been detected."),
00380         N_("There are photos on the camera. Would you like to add these pictures to your album?"), FALSE, FALSE,
00381         GCONF_ROOT "prompts/storage_camera_import_photos", N_("_Always perform this action") },
00382       { 0, "camera-photo", NULL,
00383         GVM_BUTTONS_CAMERA, G_N_ELEMENTS (GVM_BUTTONS_CAMERA),
00384         GVM_RESPONSE_IMPORT_PHOTOS,
00385         N_("Photo Import"),
00386         N_("A photo card has been detected."),
00387         N_("There are photos on the card. Would you like to add these pictures to your album?"), FALSE, FALSE,
00388         GCONF_ROOT "prompts/device_import_photos", N_("_Always perform this action") },
00389       { 0, "gnome-dev-ipod", NULL,
00390         GVM_BUTTONS_IPOD_PHOTO, G_N_ELEMENTS (GVM_BUTTONS_IPOD_PHOTO),
00391         GVM_RESPONSE_SYNC_MUSIC,
00392         N_("Photos and Music"),
00393         N_("Photos were found on your music device."),
00394         N_("Would you like to import the photos or manage its music?"), FALSE, FALSE,
00395         GCONF_ROOT "prompts/ipod_photo", N_("_Always perform this action") },
00396       { 0, "gnome-dev-cdrom-audio", NULL,
00397         GVM_BUTTONS_MIXED_CD, G_N_ELEMENTS (GVM_BUTTONS_MIXED_CD),
00398         GVM_RESPONSE_PLAY,
00399         N_("Mixed Audio and Data CD"),
00400         N_("The CD in the drive contains both music and files."),
00401         N_("Would you like to listen to music or browse the files?"), FALSE, FALSE,
00402         GCONF_ROOT "prompts/cd_mixed", N_("_Always perform this action") },
00403       { 0, "gnome-dev-cdrom", NULL,
00404         GVM_BUTTONS_WRITE_CD, G_N_ELEMENTS (GVM_BUTTONS_WRITE_CD),
00405         GVM_RESPONSE_WRITE_DATA_CD,
00406         N_("Blank CD Inserted"),
00407         N_("You have inserted a blank disc."),
00408         N_("What would you like to do?"), FALSE, FALSE,
00409         NULL, NULL },
00410       { 0, "gnome-dev-disc-dvdr", NULL,
00411         GVM_BUTTONS_WRITE_DVD, G_N_ELEMENTS (GVM_BUTTONS_WRITE_DVD),
00412         GVM_RESPONSE_WRITE_DATA_CD,
00413         N_("Blank DVD Inserted"),
00414         N_("You have inserted a blank disc."),
00415         N_("What would you like to do?"), FALSE, FALSE,
00416         NULL, NULL },
00417 };
00418 
00419 
00420 static GvmPromptCtx *
00421 gvm_prompt_ctx_new (GvmPrompt prompt, GvmPromptCallback callback, const char *udi,
00422                 const char *device, const char *mount_point, const char *path)
00423 {
00424       GvmPromptCtx *ctx;
00425       
00426       ctx = g_malloc (sizeof (GvmPromptCtx));
00427       ctx->prompt = prompt;
00428       ctx->callback = callback;
00429       ctx->udi = g_strdup (udi);
00430       ctx->device = g_strdup (device);
00431       ctx->mount_point = g_strdup (mount_point);
00432       ctx->path = g_strdup (path);
00433       
00434       return ctx;
00435 }
00436 
00437 static void
00438 gvm_prompt_ctx_free (GvmPromptCtx *ctx)
00439 {
00440       g_free (ctx->udi);
00441       g_free (ctx->device);
00442       g_free (ctx->mount_point);
00443       g_free (ctx->path);
00444       g_free (ctx);
00445 }
00446 
00447 static void
00448 prompt_response_cb (GtkWidget *dialog, int response, GvmPromptCtx *ctx)
00449 {
00450       GtkToggleButton *checkbox;
00451       GConfClient *gconf;
00452       GError *err = NULL;
00453       
00454       if (response == GTK_RESPONSE_HELP) {
00455             g_signal_stop_emission_by_name (dialog, "response");
00456             gnome_url_show (gvm_prompts[ctx->prompt].help_uri, &err);
00457             if (err) {
00458                   warn ("Unable to run help uri: %s", err->message);
00459                   g_error_free (err);
00460             }
00461             
00462             return;
00463       }
00464       
00465       checkbox = g_object_get_data ((GObject *) dialog, "checkbox");
00466       if (checkbox && gtk_toggle_button_get_active (checkbox) && response != GTK_RESPONSE_CANCEL) {
00467             gconf = gconf_client_get_default ();
00468             gconf_client_set_int (gconf, gvm_prompts[ctx->prompt].ask_again_key, response, NULL);
00469             g_object_unref (gconf);
00470       }
00471       
00472       g_hash_table_remove (dialogs, ctx->udi);
00473       ctx->callback (ctx, response);
00474       gtk_widget_destroy (dialog);
00475       gvm_prompt_ctx_free (ctx);
00476 }
00477 
00478 static char *
00479 argv_expand (const char *format, int argc, char **argv)
00480 {
00481       const char *start, *inptr;
00482       GString *string;
00483       char *str;
00484       int i;
00485       
00486       string = g_string_new ("");
00487       start = inptr = format;
00488       
00489       while (*inptr) {
00490             while (*inptr) {
00491                   if (inptr[0] == '$' && inptr[1] == '{' && inptr[2] >= '0' && inptr[2] <= '9')
00492                         break;
00493                   inptr++;
00494             }
00495             
00496             if (*inptr == '\0')
00497                   break;
00498             
00499             g_string_append_len (string, start, inptr - start);
00500             
00501             start = inptr;
00502             inptr += 2;
00503             i = strtol (inptr, &str, 10);
00504             if (*str == '}' && i < argc) {
00505                   start = inptr = str + 1;
00506                   g_string_append (string, argv[i]);
00507             }
00508       }
00509       
00510       g_string_append (string, start);
00511       
00512       str = string->str;
00513       g_string_free (string, FALSE);
00514       
00515       return str;
00516 }
00517 
00518 static void
00519 gvm_prompt (GvmPromptCtx *ctx, int argc, char **argv)
00520 {
00521       GtkWidget *dialog, *hbox, *vbox, *image, *label, *check = NULL;
00522       GvmPrompt prompt = ctx->prompt;
00523       GConfClient *gconf;
00524       GError *err = NULL;
00525       const char *text;
00526       int response, i;
00527       char *buf;
00528       
00529       gconf = gconf_client_get_default ();
00530       
00531       /* don't prompt the user again if she's already chosen a default action and has asked to not be prompted again */
00532       if (gvm_prompts[prompt].ask_again_key) {
00533             response = gconf_client_get_int (gconf, gvm_prompts[prompt].ask_again_key, &err);
00534             if (response > GVM_RESPONSE_NONE && err == NULL) {
00535                   ctx->callback (ctx, response);
00536                   return;
00537             }
00538             
00539             if (err != NULL)
00540                   g_error_free (err);
00541       }
00542       
00543       dialog = gtk_dialog_new ();
00544       gtk_widget_ensure_style (dialog);
00545       gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
00546       
00547       gtk_container_set_border_width ((GtkContainer *) ((GtkDialog *) dialog)->vbox, 0);
00548       gtk_container_set_border_width ((GtkContainer *) ((GtkDialog *) dialog)->action_area, 12);
00549       
00550       if (gvm_prompts[prompt].title)
00551             gtk_window_set_title ((GtkWindow *) dialog, _(gvm_prompts[prompt].title));
00552       
00553       if (gvm_prompts[prompt].flags & GTK_DIALOG_MODAL)
00554             gtk_window_set_modal ((GtkWindow *) dialog, TRUE);
00555       
00556       if (gvm_prompts[prompt].help_uri)
00557             gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_HELP, GTK_RESPONSE_HELP);
00558       
00559       if (gvm_prompts[prompt].buttons) {
00560             for (i = 0; i < gvm_prompts[prompt].n_buttons; i++) {
00561                   const char *name;
00562                   
00563                   name = gvm_prompts[prompt].buttons[i].stock ?
00564                         gvm_prompts[prompt].buttons[i].stock :
00565                         _(gvm_prompts[prompt].buttons[i].label);
00566                   
00567                   gtk_dialog_add_button ((GtkDialog *) dialog, name, gvm_prompts[prompt].buttons[i].response_id);
00568             }
00569             
00570             if (gvm_prompts[prompt].default_response != GVM_RESPONSE_NONE)
00571                   gtk_dialog_set_default_response ((GtkDialog *) dialog, gvm_prompts[prompt].default_response);
00572       } else {
00573             gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_OK, GTK_RESPONSE_OK);
00574       }
00575       
00576       hbox = gtk_hbox_new (FALSE, 0);
00577       gtk_container_set_border_width ((GtkContainer *) hbox, 12);
00578       
00579       /* set the icon */
00580       image = gtk_image_new_from_icon_name (gvm_prompts[prompt].icon, GTK_ICON_SIZE_DIALOG);
00581       
00582       gtk_misc_set_alignment ((GtkMisc *) image, 0.0, 0.0);
00583       gtk_box_pack_start ((GtkBox *) hbox, image, FALSE, FALSE, 12);
00584       gtk_widget_show (image);
00585       
00586       vbox = gtk_vbox_new (FALSE, 0);
00587       gtk_box_pack_start ((GtkBox *) hbox, vbox, FALSE, FALSE, 0);
00588       
00589       /* build the primary text */
00590       buf = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>", _(gvm_prompts[prompt].primary));
00591       
00592       label = gtk_label_new (NULL);
00593       gtk_misc_set_alignment ((GtkMisc *) label, 0.0, 0.5);
00594       gtk_label_set_line_wrap ((GtkLabel *) label, FALSE);
00595       gtk_label_set_markup ((GtkLabel *) label, buf);
00596       g_free (buf);
00597       
00598       gtk_box_pack_start ((GtkBox *) vbox, label, FALSE, FALSE, 0);
00599       gtk_widget_show (label);
00600       
00601       /* build the secondary text */
00602       buf = NULL;
00603       if (gvm_prompts[prompt].secondary_has_args) {
00604             text = buf = argv_expand (_(gvm_prompts[prompt].secondary), argc, argv);
00605       } else {
00606             text = _(gvm_prompts[prompt].secondary);
00607       }
00608       
00609       label = gtk_label_new (NULL);
00610       gtk_misc_set_alignment ((GtkMisc *) label, 0.0, 0.5);
00611       /*gtk_label_set_selectable ((GtkLabel *) label, TRUE);*/
00612       gtk_label_set_line_wrap ((GtkLabel *) label, TRUE);
00613       if (gvm_prompts[prompt].secondary_has_mockup)
00614             gtk_label_set_markup ((GtkLabel *) label, text);
00615       else
00616             gtk_label_set_text ((GtkLabel *) label, text);
00617       g_free (buf);
00618       
00619       gtk_box_pack_start ((GtkBox *) vbox, label, FALSE, FALSE, 6);
00620       gtk_widget_show (label);
00621       
00622       gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dialog)->vbox, hbox, FALSE, FALSE, 0);
00623       
00624       /* conditionally add a checkbox to never bother the user again */
00625       if (gvm_prompts[prompt].ask_again_key && gvm_prompts[prompt].ask_again_label) {
00626             check = gtk_check_button_new_with_mnemonic (_(gvm_prompts[prompt].ask_again_label));
00627             gtk_container_set_border_width ((GtkContainer *) check, 0);
00628             gtk_box_pack_start ((GtkBox *) vbox, check, FALSE, FALSE, 0);
00629             g_object_set_data ((GObject *) dialog, "checkbox", check);
00630             gtk_widget_show (check);
00631       }
00632       
00633       gtk_widget_show (vbox);
00634       gtk_widget_show (hbox);
00635       
00636       g_object_unref (gconf);
00637       
00638       g_hash_table_insert (dialogs, ctx->udi, dialog);
00639       
00640       g_signal_connect (dialog, "response", G_CALLBACK (prompt_response_cb), ctx);
00641       
00642       gtk_widget_show (dialog);
00643 }
00644 
00645 static void
00646 to_be_or_not_to_be (void)
00647 {
00648       /*
00649        * If all of the options that control our policy are disabled, then we
00650        * have no point in living.  Save the user some memory and exit.
00651        */
00652       /* you used to say live and let live... */
00653       gboolean live = FALSE;
00654       size_t i;
00655       
00656       /* ...but in this ever changing world in which we live in... */
00657       for (i = 0; i < G_N_ELEMENTS (gvm_settings) && !live; i++) {
00658             if (gvm_settings[i].type == TYPE_BOOL)
00659                   live = *((int *) gvm_settings[i].var);
00660       }
00661       
00662       /* makes you give it a cry... */
00663       if (!live) {
00664             dbg ("daemon exit: live and let die\n");
00665             exit (EXIT_SUCCESS);
00666       }
00667 }
00668 
00669 /*
00670  * gvm_load_config - synchronize gconf => config structure
00671  */
00672 static void
00673 gvm_load_config (void)
00674 {
00675       size_t i;
00676       
00677       gvm_settings_hash = g_hash_table_new (g_str_hash, g_str_equal);
00678       
00679       for (i = 0; i < G_N_ELEMENTS (gvm_settings); i++) {
00680             g_hash_table_insert (gvm_settings_hash, gvm_settings[i].key, GINT_TO_POINTER (i + 1));
00681             if (gvm_settings[i].type == TYPE_STRING) {
00682                   *((char **) gvm_settings[i].var) =
00683                         gconf_client_get_string (config.client, gvm_settings[i].key, NULL);
00684                   dbg ("setting[%d]: string: %s = %s\n", i, strrchr (gvm_settings[i].key, '/') + 1,
00685                        *((char **) gvm_settings[i].var) ? *((char **) gvm_settings[i].var): "NULL");
00686             } else if (gvm_settings[i].type == TYPE_BOOL) {
00687                   *((int *) gvm_settings[i].var) =
00688                         gconf_client_get_bool (config.client, gvm_settings[i].key, NULL);
00689                   dbg ("setting[%d]: bool: %s = %d\n", i, strrchr (gvm_settings[i].key, '/') + 1,
00690                        *((int *) gvm_settings[i].var));
00691             } else if (gvm_settings[i].type == TYPE_FLOAT) {
00692                   *((double *) gvm_settings[i].var) =
00693                         gconf_client_get_float (config.client, gvm_settings[i].key, NULL);
00694                   if (*((double *) gvm_settings[i].var) >= 1.0)
00695                         *((double *) gvm_settings[i].var) = 1.0;
00696                   else if (*((double *) gvm_settings[i].var) <= 0.0)
00697                         *((double *) gvm_settings[i].var) = 0.0;
00698                   dbg ("settings[%d]: float: %s = %f\n", i, strrchr (gvm_settings[i].key, '/') + 1,
00699                        *((double *) gvm_settings[i].var));
00700             } else {
00701                   g_assert_not_reached ();
00702             }
00703       }
00704       
00705       to_be_or_not_to_be ();
00706 }
00707 
00708 /*
00709  * gvm_config_changed - gconf_client_notify_add () call back to reload config
00710  */
00711 static void
00712 gvm_config_changed (GConfClient *client GNUC_UNUSED,
00713                 guint id GNUC_UNUSED,
00714                 GConfEntry *entry,
00715                 gpointer data GNUC_UNUSED)
00716 {
00717       GConfValue *value;
00718       gpointer result;
00719       int which;
00720       
00721       g_return_if_fail (gconf_entry_get_key (entry) != NULL);
00722       
00723       if (!(value = gconf_entry_get_value (entry)))
00724             return;
00725       
00726       if (!(result = g_hash_table_lookup (gvm_settings_hash, entry->key)))
00727             return;
00728       
00729       which = GPOINTER_TO_INT (result) - 1;
00730       
00731       if (gvm_settings[which].type == TYPE_STRING) {
00732             g_free (*((char **) gvm_settings[which].var));
00733             *((char **) gvm_settings[which].var) = g_strdup (gconf_value_get_string (value));
00734             dbg ("setting changed: string: %s = %s\n", strrchr (gvm_settings[which].key, '/') + 1,
00735                  *((char **) gvm_settings[which].var));
00736       } else if (gvm_settings[which].type == TYPE_BOOL) {
00737             *((int *) gvm_settings[which].var) = gconf_value_get_bool (value);
00738             dbg ("setting changed: bool: %s = %d\n", strrchr (gvm_settings[which].key, '/') + 1,
00739                  *((int *) gvm_settings[which].var));
00740       } else if (gvm_settings[which].type == TYPE_FLOAT) {
00741             *((double *) gvm_settings[which].var) = gconf_client_get_float (config.client, gvm_settings[which].key, NULL);
00742             if (*((double *) gvm_settings[which].var) >= 1.0)
00743                   *((double *) gvm_settings[which].var) = 1.0;
00744             else if (*((double *) gvm_settings[which].var) <= 0.0)
00745                   *((double *) gvm_settings[which].var) = 0.0;
00746             dbg ("settings[%d]: float: %s = %f\n", which, strrchr (gvm_settings[which].key, '/') + 1,
00747                  *((double *) gvm_settings[which].var));
00748       } else {
00749             g_assert_not_reached ();
00750       }
00751       
00752       to_be_or_not_to_be ();
00753 }
00754 
00755 /*
00756  * gvm_init_config - initialize gconf client and load config data
00757  */
00758 static void
00759 gvm_init_config (void)
00760 {
00761       config.client = gconf_client_get_default ();
00762 
00763       gconf_client_add_dir (config.client, GCONF_ROOT_SANS_SLASH,
00764                         GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
00765 
00766       gvm_load_config ();
00767 
00768       gconf_client_notify_add (config.client, GCONF_ROOT_SANS_SLASH,
00769                          gvm_config_changed, NULL, NULL, NULL);
00770 }
00771 
00772 /*
00773  * gvm_run_command - run the given command, replacing %d with the device node,
00774  * %h with the HAL UDI and %m with the given path.
00775  *
00776  * Returns TRUE if successful or FALSE otherwise.
00777  */
00778 static gboolean
00779 gvm_run_command (const char *command, const char *udi, const char *device, const char *mount_point)
00780 {
00781       char *path, *mp = NULL, *dev = NULL;
00782       const char *inptr, *start;
00783       GError *error = NULL;
00784       GString *exec;
00785       char *argv[4];
00786       
00787       g_assert (udi != NULL);
00788       
00789       exec = g_string_new (NULL);
00790       
00791       /* perform s/%d/device/, s/%m/mount_point/ and s/%h/udi/ */
00792       start = inptr = command;
00793       while ((inptr = strchr (inptr, '%')) != NULL) {
00794             g_string_append_len (exec, start, inptr - start);
00795             inptr++;
00796             switch (*inptr) {
00797             case 'd':
00798                   g_string_append (exec, device ? device : "");
00799                   break;
00800             case 'm':
00801                   if (mount_point == NULL && libhal_device_query_capability (hal_ctx, udi, "volume", NULL)) {
00802                         mp = libhal_device_get_property_string (hal_ctx, udi, "volume.mount_point", NULL);
00803                         mount_point = mp;
00804                   }
00805                   
00806                   if (mount_point) {
00807                         path = g_shell_quote (mount_point);
00808                         g_string_append (exec, path);
00809                         g_free (path);
00810                   } else {
00811                         g_string_append (exec, "\"\"");
00812                   }
00813                   break;
00814             case 'h':
00815                   g_string_append (exec, udi);
00816                   break;
00817             case '%':
00818                   g_string_append_c (exec, '%');
00819                   break;
00820             default:
00821                   g_string_append_c (exec, '%');
00822                   if (*inptr)
00823                         g_string_append_c (exec, *inptr);
00824                   break;
00825             }
00826             
00827             if (*inptr)
00828                   inptr++;
00829             start = inptr;
00830       }
00831       g_string_append (exec, start);
00832       
00833       libhal_free_string (mp);
00834       libhal_free_string (dev);
00835       
00836       argv[0] = "/bin/sh";
00837       argv[1] = "-c";
00838       argv[2] = exec->str;
00839       argv[3] = NULL;
00840       
00841       dbg ("executing command: %s\n", exec->str);
00842       if (!g_spawn_async (g_get_home_dir (), argv, NULL, 0, NULL, NULL, NULL, &error)) {
00843             warn ("failed to exec %s: %s", exec->str, error->message);
00844             g_string_free (exec, TRUE);
00845             g_error_free (error);
00846             return FALSE;
00847       }
00848       
00849       g_string_free (exec, TRUE);
00850       
00851       return TRUE;
00852 }
00853 
00854 static gboolean
00855 gvm_check_dir (const char *dirname, const char *name, gboolean check_contents)
00856 {
00857       gboolean exists = FALSE;
00858       struct dirent *dent;
00859       char *path = NULL;
00860       struct stat st;
00861       DIR *dir;
00862       
00863       if (!(dir = opendir (dirname)))
00864             return FALSE;
00865       
00866       while ((dent = readdir (dir))) {
00867             if (!g_ascii_strcasecmp (dent->d_name, name)) {
00868                   path = g_build_filename (dirname, dent->d_name, NULL);
00869                   if (stat (path, &st) == 0 && S_ISDIR (st.st_mode))
00870                         exists = TRUE;
00871                   break;
00872             }
00873       }
00874       
00875       closedir (dir);
00876       
00877       if (exists && check_contents) {
00878             exists = FALSE;
00879             if ((dir = opendir (path))) {
00880                   while ((dent = readdir (dir))) {
00881                         if (!strcmp (dent->d_name, "..")
00882                             || !strcmp (dent->d_name, "."))
00883                               continue;
00884                         
00885                         exists = TRUE;
00886                         break;
00887                   }
00888                   
00889                   closedir (dir);
00890             }
00891       }
00892       
00893       g_free (path);
00894       
00895       return exists;
00896 }
00897 
00898 #ifdef ENABLE_AUTOMOUNT
00899 /*
00900  * gvm_check_dvd - is this a Video DVD?  If so, do something about it.
00901  *
00902  * Returns TRUE if this was a Video DVD and FALSE otherwise.
00903  */
00904 static gboolean
00905 gvm_check_dvd (const char *udi, const char *device, const char *mount_point)
00906 {
00907       gboolean is_dvd;
00908       
00909       is_dvd = gvm_check_dir (mount_point, "video_ts", FALSE);
00910       
00911       if (is_dvd && config.autoplay_dvd)
00912             gvm_run_command (config.autoplay_dvd_command, udi, device, mount_point);
00913       
00914       return is_dvd;
00915 }
00916 
00917 /*
00918  * gvm_check_vcd - is this a Video CD?  If so, do something about it.
00919  *
00920  * Returns TRUE if this was a Video CD and FALSE otherwise.
00921  */
00922 static gboolean
00923 gvm_check_vcd (const char *udi, const char *device, const char *mount_point)
00924 {
00925       gboolean is_vcd;
00926       
00927       is_vcd = gvm_check_dir (mount_point, "vcd", FALSE);
00928       
00929       if (is_vcd && config.autoplay_vcd)
00930             gvm_run_command (config.autoplay_vcd_command, udi, device, mount_point);
00931       
00932       return is_vcd;
00933 }
00934 #endif /* ENABLE_AUTOMOUNT */
00935 
00936 /*
00937  * gvm_udi_is_storage_camera - checks if the udi is a mass-storage camera device
00938  *
00939  * Returns TRUE if the device is a camera or FALSE otherwise.
00940  */
00941 static gboolean
00942 gvm_udi_is_storage_camera (const char *udi)
00943 {
00944       char *physical_device = NULL;
00945       char *storage_device = NULL;
00946       gboolean is_camera = FALSE;
00947       DBusError error;
00948       
00949       dbus_error_init (&error);
00950       if (!(storage_device = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", &error))) {
00951             warn ("cannot get block.storage_device property: %s", error.message);
00952             if (dbus_error_is_set (&error))
00953                   dbus_error_free (&error);
00954             return FALSE;
00955       }
00956       
00957       if (!(physical_device = libhal_device_get_property_string (hal_ctx, storage_device, "storage.physical_device", &error))) {
00958             warn ("cannot get storage.physical_device property: %s", error.message);
00959             if (dbus_error_is_set (&error))
00960                   dbus_error_free (&error);
00961             goto out;
00962       }
00963       
00964       if ((is_camera = libhal_device_query_capability (hal_ctx, physical_device, "camera", NULL)))
00965             dbg ("Camera detected: %s\n", udi);
00966       
00967  out:
00968       
00969       libhal_free_string (storage_device);
00970       libhal_free_string (physical_device);
00971       
00972       return is_camera;
00973 }
00974 
00975 /*
00976  * gvm_udi_is_camera - checks if the udi is a supported non-mass-storage camera device
00977  *
00978  * Returns TRUE if the device is a camera with libgphoto2 support or FALSE otherwise.
00979  */
00980 static gboolean
00981 gvm_udi_is_camera (const char *udi)
00982 {
00983       gboolean is_camera = FALSE;
00984       char *access_method;
00985       char *driver;
00986       
00987 #if 0
00988       /* Note: this query is not necessary atm because our only caller already has this info */
00989       if (!libhal_device_query_capability (hal_ctx, udi, "camera", NULL))
00990             return FALSE;
00991 #endif
00992       
00993       if (!(access_method = libhal_device_get_property_string (hal_ctx, udi, "camera.access_method", NULL)))
00994             return FALSE;
00995       
00996       if (!strcmp (access_method, "storage")) {
00997             /* we only want to match non-storage cameras */
00998             is_camera = FALSE;
00999             goto done;
01000       } else if (!strcmp (access_method, "ptp")) {
01001             /* ptp cameras are supported by libgphoto2 always */
01002             is_camera = TRUE;
01003             goto done;
01004       } else if (!strcmp (access_method, "libgphoto2")) {
01005             /* user has an old install of the libgphoto2 fdi files */
01006             warn ("Please update your libgphoto2 package as the fdi files are outdated!\n");
01007             if ((driver = libhal_device_get_property_string (hal_ctx, udi, "info.linux.driver", NULL))) {
01008                   /* the old fdi files marked everything as access_method="libgphoto2" so check that
01009                    * this is a non-mass-storage camera */
01010                   if (!strcmp (driver, "usb-storage"))
01011                         is_camera = FALSE;
01012             } else {
01013                   is_camera = TRUE;
01014             }
01015             
01016             libhal_free_string (driver);
01017             
01018             goto done;
01019       }
01020       
01021       dbg ("Non Mass-Storage Camera detected: %s\n", udi);
01022       
01023       is_camera = libhal_device_get_property_bool (hal_ctx, udi, "camera.libgphoto2.support", NULL);
01024       
01025  done:
01026       
01027       libhal_free_string (access_method);
01028       
01029       return is_camera;
01030 }
01031 
01032 #if 0
01033 /*
01034  * gvm_udi_is_ipod - checks if the udi is the mountable volume of an iPod
01035  *
01036  * Returns TRUE if the device is an iPod or FALSE otherwise.
01037  */
01038 static gboolean
01039 gvm_udi_is_ipod (const char *udi)
01040 {
01041       char *storage_device = NULL;
01042       gboolean is_ipod = FALSE;
01043       char *product = NULL;
01044       DBusError error;
01045       
01046       dbus_error_init (&error);
01047       if (!(storage_device = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", &error))) {
01048             warn ("cannot get block.storage_device property: %s", error.message);
01049             if (dbus_error_is_set (&error))
01050                   dbus_error_free (&error);
01051             return FALSE;
01052       }
01053       
01054       if (!(product = libhal_device_get_property_string (hal_ctx, storage_device, "info.product", &error))) {
01055             warn ("cannot get info.product property: %s", error.message);
01056             if (dbus_error_is_set (&error))
01057                   dbus_error_free (&error);
01058             goto out;
01059       }
01060       
01061       if ((is_ipod = !strcmp (product, "iPod")))
01062             dbg ("iPod detected: %s\n", udi);
01063       else
01064             dbg ("not an iPod: %s\n", udi);
01065       
01066  out:
01067       
01068       libhal_free_string (storage_device);
01069       libhal_free_string (product);
01070       
01071       return is_ipod;
01072 }
01073 #endif
01074 
01075 #ifdef ENABLE_AUTOMOUNT
01076 /*
01077  * gvm_udi_is_portable_media_player - checks if the udi is the mountable volume of a media player
01078  *
01079  * Returns TRUE if the device is a portable media player or FALSE otherwise.
01080  */
01081 static gboolean
01082 gvm_udi_is_portable_media_player (const char *udi, gboolean *is_ipod)
01083 {
01084       char *storage_device = NULL;
01085       gboolean is_player = FALSE;
01086       char *product = NULL;
01087       DBusError error;
01088       
01089       if (is_ipod)
01090             *is_ipod = FALSE;
01091       
01092       dbus_error_init (&error);
01093       if (!(storage_device = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", &error))) {
01094             warn ("cannot get block.storage_device property: %s", error.message);
01095             if (dbus_error_is_set (&error))
01096                   dbus_error_free (&error);
01097             return FALSE;
01098       }
01099       
01100       if (is_ipod && (product = libhal_device_get_property_string (hal_ctx, storage_device, "info.product", NULL))) {
01101             *is_ipod = !strcmp (product, "iPod");
01102             libhal_free_string (product);
01103       }
01104       
01105       if ((is_player = libhal_device_query_capability (hal_ctx, storage_device, "portable_audio_player", NULL)))
01106             dbg ("%s detected: %s\n", is_ipod && *is_ipod ? "iPod" : "Generic music player", udi);
01107       
01108       libhal_free_string (storage_device);
01109       
01110       return is_player;
01111 }
01112 #endif /* ENABLE_AUTOMOUNT */
01113 
01114 /*
01115  * gvm_run_camera - launch the camera application
01116  */
01117 static void
01118 gvm_run_camera (const char *udi, const char *device, const char *mount_point)
01119 {
01120       if (config.autophoto_command != NULL)
01121             gvm_run_command (config.autophoto_command, udi, device, mount_point);
01122 }
01123 
01124 /*
01125  * gvm_check_photos - check if this device has a dcim directory
01126  *
01127  * Returns TRUE if there were photos on this device, FALSE otherwise
01128  */
01129 static gboolean
01130 gvm_check_photos (const char *udi, const char *device, const char *mount_point)
01131 {
01132       DBusError error;
01133       
01134       if (!gvm_check_dir (mount_point, "dcim", TRUE))
01135             return FALSE;
01136       
01137       dbg ("Photos detected: %s\n", mount_point);
01138       
01139       /* add the "content.photos" capability to this device */
01140       dbus_error_init (&error);
01141       if (!libhal_device_add_capability (hal_ctx, udi, "content.photos", &error)) {
01142             warn ("failed to set content.photos on %s: %s", device, error.message);
01143             dbus_error_free (&error);
01144       }
01145       
01146       return TRUE;
01147 }
01148 
01149 /*
01150  * gvm_run_portable_media_player - launch the ipod application
01151  */
01152 static void
01153 gvm_run_portable_media_player (const char *udi, const char *device, const char *mount_point)
01154 {
01155       if (config.autoipod_command != NULL)
01156             gvm_run_command (config.autoipod_command, udi, device, mount_point);
01157 }
01158 
01159 /*
01160  * gvm_run_pilot - sync the pda palm pilot
01161  */
01162 static void
01163 gvm_run_pilot (const char *udi)
01164 {
01165       DBusError error;
01166       char *device;
01167       
01168       if (config.autopilot_command == NULL)
01169             return;
01170       
01171       dbus_error_init (&error);
01172       /* pda.palm.hotsync_interface should be the same as serial.device, but just in case... */
01173       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "pda.palm.hotsync_interface", &error))) {
01174             warn ("cannot get pda.palm.hotsync_interface property: %s", error.message);
01175             if (dbus_error_is_set (&error))
01176                   dbus_error_free (&error);
01177             return;
01178       }
01179       
01180       gvm_run_command (config.autopilot_command, udi, device, NULL);
01181       libhal_free_string (device);
01182 }
01183 
01184 /*
01185  * gvm_run_pocketpc - sync the pda palm pilot
01186  */
01187 static void
01188 gvm_run_pocketpc (const char *udi)
01189 {
01190       DBusError error;
01191       char *device;
01192       
01193       if (config.autopocketpc_command == NULL)
01194             return;
01195       
01196       dbus_error_init (&error);
01197       /* pda.palm.hotsync_interface should be the same as serial.device, but just in case... */
01198       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "pda.pocketpc.hotsync_interface", &error))) {
01199             warn ("cannot get pda.pocketpc.hotsync_interface property: %s", error.message);
01200             if (dbus_error_is_set (&error))
01201                   dbus_error_free (&error);
01202             return;
01203       }
01204       
01205       gvm_run_command (config.autopocketpc_command, udi, device, NULL);
01206       libhal_free_string (device);
01207 }
01208 
01209 /*
01210  * gvm_run_printer - launch the printer application
01211  */
01212 static void
01213 gvm_run_printer (const char *udi)
01214 {
01215       DBusError error;
01216       char *device;
01217       
01218       if (config.autoprinter_command == NULL)
01219             return;
01220       
01221       dbus_error_init (&error);
01222       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "printer.device", &error))) {
01223             warn ("cannot get printer.device property: %s", error.message);
01224             if (dbus_error_is_set (&error))
01225                   dbus_error_free (&error);
01226             return;
01227       }
01228       
01229       gvm_run_command (config.autoprinter_command, udi, device, NULL);
01230       libhal_free_string (device);
01231 }
01232 
01233 /*
01234  * gvm_run_scanner - launch the scanner application
01235  */
01236 static void
01237 gvm_run_scanner (const char *udi)
01238 {
01239       char *device, *parent;
01240       DBusError error;
01241       
01242       if (config.autoscanner_command == NULL)
01243             return;
01244       
01245       dbus_error_init (&error);
01246       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "scanner.device", &error))) {
01247             /* check parent's linux.device_file */
01248             if ((parent = libhal_device_get_property_string (hal_ctx, udi, "info.parent", NULL))) {
01249                   if (dbus_error_is_set (&error))
01250                         dbus_error_free (&error);
01251                   dbus_error_init (&error);
01252                         device = libhal_device_get_property_string (hal_ctx, parent, "linux.device_file", &error);
01253                   libhal_free_string (parent);
01254             }
01255             
01256             if (!device) {
01257                   warn ("cannot get scanner.device property: %s", error.message);
01258                   if (dbus_error_is_set (&error))
01259                         dbus_error_free (&error);
01260                   return;
01261             }
01262       }
01263       
01264       gvm_run_command (config.autoscanner_command, udi, device, NULL);
01265       libhal_free_string (device);
01266 }
01267 
01268 /*
01269  * gvm_run_webcam - launch the webcam application
01270  */
01271 static void
01272 gvm_run_webcam (const char *udi)
01273 {
01274       DBusError error;
01275       char *device;
01276       
01277       if (config.autowebcam_command == NULL)
01278             return;
01279       
01280       dbus_error_init (&error);
01281       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "video4linux.device", &error))) {
01282             warn ("cannot get video4linux.device property: %s", error.message);
01283             if (dbus_error_is_set (&error))
01284                   dbus_error_free (&error);
01285             return;
01286       }
01287       
01288       gvm_run_command (config.autowebcam_command, udi, device, NULL);
01289       libhal_free_string (device);
01290 }
01291 
01292 static void
01293 import_photos_cb (GvmPromptCtx *ctx, int action)
01294 {
01295       if (action == GVM_RESPONSE_IMPORT_PHOTOS)
01296             gvm_run_camera (ctx->udi, ctx->device, ctx->mount_point);
01297 }
01298 
01299 static void
01300 gvm_run_filemanager (const char *udi, const char *device, const char *mount_point)
01301 {
01302       const char *command = NAUTILUS_COMMAND;
01303       
01304       if (config.filemanager && *config.filemanager)
01305             command = config.filemanager;
01306       
01307       gvm_run_command (command, udi, device, mount_point);
01308 }
01309 
01310 #ifdef ENABLE_AUTOMOUNT
01311 static gboolean
01312 is_exe (const char *path)
01313 {
01314       size_t len;
01315       
01316       return (len = strlen (path)) > 4 && !g_ascii_strcasecmp (path + len - 4, ".exe");
01317 }
01318 
01319 static void
01320 autorun_cb (GvmPromptCtx *ctx, int action)
01321 {
01322       gboolean autobrowse = TRUE;
01323       GError *error = NULL;
01324       const char *prog;
01325       GPtrArray *args;
01326       struct stat st;
01327       char **argv;
01328       int argc, i;
01329       
01330       if (action == GVM_RESPONSE_RUN || action == GVM_RESPONSE_OPEN) {
01331             if (!g_shell_parse_argv (ctx->path, &argc, &argv, NULL)) {
01332                   argv = g_malloc (sizeof (char *) * 2);
01333                   argv[0] = g_strdup (ctx->path);
01334                   argv[1] = NULL;
01335                   argc = 1;
01336             }
01337             
01338             if (stat (argv[0], &st) == -1) {
01339                   g_strfreev (argv);
01340                   goto autobrowse;
01341             }
01342             
01343             if (S_ISREG (st.st_mode) &&
01344                 (access (argv[0], R_OK | X_OK) == 0 || is_exe (argv[0]))) {
01345                   /* argv[0] is a program, attempt to run it */
01346                   args = g_ptr_array_new ();
01347                   
01348                   /* If the extension is .exe and the user has wine
01349                    * installed, try running the application under wine
01350                    * so that it Just Works(tm) for Grandma. */
01351                   if (is_exe (argv[0]) && (prog = g_find_program_in_path ("wine")))
01352                         g_ptr_array_add (args, (char *) prog);
01353                   
01354                   for (i = 0; i < argc; i++)
01355                         g_ptr_array_add (args, argv[i]);
01356                   g_ptr_array_add (args, NULL);
01357                   
01358                   g_spawn_async (ctx->mount_point, (char **) args->pdata, NULL,
01359                                0, NULL, NULL, NULL, &error);
01360                   
01361                   g_ptr_array_free (args, TRUE);
01362                   
01363                   if (error)
01364                         warn ("failed to exec %s: %s", ctx->path, error->message);
01365                   else
01366                         autobrowse = FALSE;
01367             } else if (argc > 1) {
01368                   /* ctx->path is a command-line, but argv[0] is not executable. */
01369                   warn ("failed to exec %s: %s", ctx->path, g_strerror (EACCES));
01370             } else if ((prog = g_find_program_in_path ("gnome-open"))) {
01371                   /* just open the specified path using gnome-open */
01372                   args = g_ptr_array_new ();
01373                   g_ptr_array_add (args, (char *) prog);
01374                   g_ptr_array_add (args, argv[0]);
01375                   g_ptr_array_add (args, NULL);
01376                   
01377                   g_spawn_async (ctx->mount_point, (char **) args->pdata, NULL,
01378                                0, NULL, NULL, NULL, &error);
01379                   
01380                   g_ptr_array_free (args, TRUE);
01381                   
01382                   if (error)
01383                         warn ("failed to open %s: %s", ctx->path, error->message);
01384                   else
01385                         autobrowse = FALSE;
01386             }
01387             
01388             g_strfreev (argv);
01389       }
01390       
01391 autobrowse:
01392       
01393       if (config.autobrowse && autobrowse)
01394             gvm_run_filemanager (ctx->udi, ctx->device, ctx->mount_point);
01395 }
01396 
01397 static char *
01398 canon_path (char *path)
01399 {
01400       register char *inptr, *outptr;
01401       
01402       inptr = outptr = path;
01403       
01404       while (*inptr != '\0') {
01405             if (inptr[0] == '.' && (inptr[1] == '/' || (inptr[1] == '.' && inptr[2] == '/'))) {
01406                   if (inptr[1] == '.') {
01407                         /* ../ */
01408                         inptr += 3;
01409                         
01410                         if (outptr > path) {
01411                               outptr--;
01412                               while (outptr > path && outptr[-1] != '/')
01413                                     outptr--;
01414                               if (outptr == path && *outptr == '/')
01415                                     outptr++;
01416                         }
01417                   } else {
01418                         /* ./ */
01419                         inptr += 2;
01420                   }
01421                   
01422                   while (*inptr == '/')
01423                         inptr++;
01424             }
01425             
01426             while (*inptr && *inptr != '/')
01427                   *outptr++ = *inptr++;
01428             
01429             if (*inptr == '/')
01430                   *outptr++ = *inptr++;
01431             
01432             while (*inptr == '/')
01433                   inptr++;
01434       }
01435       
01436       *outptr = '\0';
01437       
01438       return path;
01439 }
01440 
01441 static char *
01442 unix_path (const char *mount_point, const char *rel_path)
01443 {
01444       const char *start, *inptr;
01445       gboolean checkstat = TRUE;
01446       struct dirent *dent;
01447       struct stat st;
01448       GString *path;
01449       size_t len;
01450       char *str;
01451       DIR *dir;
01452       
01453       path = g_string_new (mount_point);
01454       
01455       inptr = rel_path;
01456       while (*inptr) {
01457             start = inptr;
01458             
01459             while (*inptr && *inptr != '\\')
01460                   inptr++;
01461             
01462             if (checkstat) {
01463                   len = path->len;
01464                   
01465                   g_string_append_c (path, G_DIR_SEPARATOR);
01466                   g_string_append_len (path, start, inptr - start);
01467                   
01468                   if (stat (path->str, &st) == -1) {
01469                         /* find the correct capitalization of the path component */
01470                         g_string_truncate (path, len);
01471                         
01472                         len = inptr - start;
01473                         
01474                         if ((dir = opendir (path->str))) {
01475                               while ((dent = readdir (dir))) {
01476                                     if (!g_ascii_strncasecmp (dent->d_name, start, len))
01477                                           break;
01478                               }
01479                               
01480                               g_string_append_c (path, G_DIR_SEPARATOR);
01481                               
01482                               if (dent == NULL) {
01483                                     /* path component not found */
01484                                     g_string_append_len (path, start, len);
01485                                     checkstat = FALSE;
01486                               } else
01487                                     g_string_append (path, dent->d_name);
01488                               
01489                               closedir (dir);
01490                         } else {
01491                               g_string_append_c (path, G_DIR_SEPARATOR);
01492                               g_string_append_len (path, start, len);
01493                               checkstat = FALSE;
01494                         }
01495                   }
01496             } else {
01497                   /* some previous path component was not found */
01498                   g_string_append_c (path, G_DIR_SEPARATOR);
01499                   g_string_append_len (path, start, inptr - start);
01500             }
01501             
01502             if (*inptr == '\0')
01503                   break;
01504             
01505             inptr++;
01506       }
01507       
01508       str = path->str;
01509       g_string_free (path, FALSE);
01510       
01511       return canon_path (str);
01512 }
01513 
01514 static gboolean
01515 readline (FILE *fp, GString *linebuf)
01516 {
01517       gboolean eoln = FALSE;
01518       gboolean rv = FALSE;
01519       char buf[256];
01520       size_t n;
01521       
01522       g_string_truncate (linebuf, 0);
01523       
01524       while (!eoln && fgets (buf, sizeof (buf), fp)) {
01525             n = strlen (buf);
01526             rv = TRUE;
01527             
01528             if (buf[n - 1] == '\n') {
01529                   eoln = TRUE;
01530                   buf[--n] = '\0';
01531                   if (n > 0 && buf[n - 1] == '\r')
01532                         buf[--n] = '\0';
01533             }
01534             
01535             g_string_append_len (linebuf, buf, n);
01536       }
01537       
01538       return rv;
01539 }
01540 
01541 static char *
01542 autorun_inf_get (const char *path, const char *section, const char *key)
01543 {
01544       register char *inptr;
01545       char *value = NULL;
01546       GString *linebuf;
01547       gboolean rv;
01548       size_t len;
01549       FILE *fp;
01550       
01551       if (!(fp = fopen (path, "rt")))
01552             return NULL;
01553       
01554       len = strlen (key);
01555       linebuf = g_string_new ("");
01556       
01557       /* find the .inf section requested */
01558       while ((rv = readline (fp, linebuf))) {
01559             g_strchomp (linebuf->str);
01560             if (!g_ascii_strcasecmp (linebuf->str, section))
01561                   break;
01562       }
01563       
01564       if (rv) {
01565             /* section found, read until EOF or until a new section is found */
01566             while ((rv = readline (fp, linebuf))) {
01567                   if (linebuf->str[0] == '[') {
01568                         /* beginning of a new section, key not found */
01569                         break;
01570                   }
01571                   
01572                   if (g_ascii_strncasecmp (linebuf->str, key, len) != 0) {
01573                         /* these are not the droids you are looking for... */
01574                         continue;
01575                   }
01576                   
01577                   /* make sure the next non-lwsp char is an '=' */
01578                   inptr = linebuf->str + len;
01579                   while (*inptr == ' ' || *inptr == '\t')
01580                         inptr++;
01581                   
01582                   if (*inptr == '\0') {
01583                         /* correct key, but no value? */
01584                         break;
01585                   }
01586                   
01587                   if (*inptr == '=') {
01588                         /* extract the value */
01589                         inptr++;
01590                         
01591                         while (*inptr == ' ' || *inptr == '\t')
01592                               inptr++;
01593                         
01594                         if (*inptr) {
01595                               value = g_strdup (inptr);
01596                               g_strchug (value);
01597                         }
01598                         
01599                         break;
01600                   }
01601             }
01602       }
01603       
01604       g_string_free (linebuf, TRUE);
01605       fclose (fp);
01606       
01607       return value;
01608 }
01609 
01610 static gboolean
01611 gvm_autorun_prompt (GvmPrompt prompt, const char *udi, const char *device,
01612                 const char *mount_point, char *value)
01613 {
01614       register char *inptr;
01615       char *argv[1], *path;
01616       GvmPromptCtx *ctx;
01617       GString *cmd;
01618       char c;
01619       
01620       if (((value[0] >= 'A' && value[0] <= 'Z')
01621            || (value[0] >= 'a' && value[0] <= 'z'))
01622           && value[1] == ':' && value[2] == '\\') {
01623             /* the autorun.inf file references the
01624              * autorun program by drive (stupid,
01625              * but common). */
01626             path = value + 3;
01627       } else {
01628             path = value;
01629       }
01630       
01631       /* advance to the lwsp between the path and first argument (if any) */
01632       inptr = path;
01633       while (*inptr && *inptr != ' ' && *inptr != '\t')
01634             inptr++;
01635       
01636       c = *inptr;
01637       *inptr++ = '\0';
01638       
01639       path = unix_path (mount_point, path);
01640       if (strncmp (path, mount_point, strlen (mount_point)) != 0) {
01641             /* not allowed */
01642             g_free (value);
01643             g_free (path);
01644             return FALSE;
01645       }
01646       
01647       cmd = g_string_new (path);
01648       
01649       if (c != '\0') {
01650             g_string_append_c (cmd, ' ');
01651             g_string_append (cmd, inptr);
01652       }
01653       
01654       g_free (value);
01655       
01656       argv[0] = path;
01657       ctx = gvm_prompt_ctx_new (prompt, autorun_cb, udi, device, mount_point, cmd->str);
01658       gvm_prompt (ctx, 1, argv);
01659       g_string_free (cmd, TRUE);
01660       g_free (path);
01661       
01662       return TRUE;
01663 }
01664 
01665 static gboolean
01666 gvm_autorun_inf (const char *udi, const char *device, const char *mount_point)
01667 {
01668       char *value, *path = NULL;
01669       struct dirent *dent;
01670       DIR *dir;
01671       
01672       if ((dir = opendir (mount_point))) {
01673             while ((dent = readdir (dir))) {
01674                   if (!g_ascii_strcasecmp (dent->d_name, "autorun.inf")) {
01675                         path = g_build_filename (mount_point, dent->d_name, NULL);
01676                         break;
01677                   }
01678             }
01679             
01680             closedir (dir);
01681       }
01682       
01683       if (!path)
01684             return FALSE;
01685       
01686       value = autorun_inf_get (path, "[autorun]", "open");
01687       g_free (path);
01688       
01689       if (!value)
01690             return FALSE;
01691       
01692       return gvm_autorun_prompt (GVM_PROMPT_AUTORUN, udi, device, mount_point, value);
01693 }
01694 
01695 static gboolean
01696 gvm_autoopen (FILE *fp, const char *udi, const char *device, const char *mount_point)
01697 {
01698       GString *str;
01699       char *value;
01700       
01701       str = g_string_new ("");
01702       if (!readline (fp, str)) {
01703             g_string_free (str, TRUE);
01704             return FALSE;
01705       }
01706       
01707       value = str->str;
01708       g_strstrip (value);
01709       g_string_free (str, FALSE);
01710       
01711       if (value[0] == '\0') {
01712             /* nothing to do */
01713             g_free (value);
01714             return FALSE;
01715       }
01716       
01717       return gvm_autorun_prompt (GVM_PROMPT_AUTOOPEN, udi, device, mount_point, value);
01718 }
01719 #endif /* ENABLE_AUTOMOUNT */
01720 
01721 /*
01722  * gvm_autorun - automatically execute stuff
01723  *
01724  * we currently autorun: autorun files, video DVD's, and digital photos
01725  */
01726 static void
01727 gvm_autorun (const char *udi, const char *device, const char *mount_point)
01728 {
01729 #ifdef ENABLE_AUTOMOUNT
01730       char **autopath, *argv[1], *path;
01731       gboolean handled = FALSE;
01732 #endif
01733       GvmPromptCtx *ctx;
01734       
01735 #ifdef ENABLE_AUTOMOUNT
01736       if (gvm_check_dvd (udi, device, mount_point))
01737             return;
01738       
01739       if (gvm_check_vcd (udi, device, mount_point))
01740             return;
01741 #endif /* ENABLE_AUTOMOUNT */
01742       
01743       if (config.autophoto && gvm_check_photos (udi, device, mount_point)) {
01744             ctx = gvm_prompt_ctx_new (GVM_PROMPT_IMPORT_PHOTOS, import_photos_cb, udi, device, mount_point, NULL);
01745             gvm_prompt (ctx, 0, NULL);
01746             return;
01747       }
01748       
01749 #ifdef ENABLE_AUTOMOUNT
01750       if (config.autorun && config.autorun_path) {
01751             struct stat st;
01752             int i;
01753             
01754             autopath = g_strsplit (config.autorun_path, ":", -1);
01755             
01756             for (i = 0; autopath[i]; i++) {
01757                   path = g_strdup_printf ("%s/%s", mount_point, autopath[i]);
01758                   if (access (path, F_OK | R_OK | X_OK) != 0 || stat (path, &st) == -1
01759                       || !S_ISREG (st.st_mode)) {
01760                         g_free (path);
01761                         continue;
01762                   }
01763                   
01764                   argv[0] = path;
01765                   ctx = gvm_prompt_ctx_new (GVM_PROMPT_AUTORUN, autorun_cb, udi, device, mount_point, path);
01766                   gvm_prompt (ctx, 1, argv);
01767                   handled = TRUE;
01768                   g_free (path);
01769                   break;
01770             }
01771             
01772             g_strfreev (autopath);
01773             
01774             if (!handled)
01775                   handled = gvm_autorun_inf (udi, device, mount_point);
01776             
01777             if (handled)
01778                   return;
01779       }
01780       
01781       if (config.autoopen && config.autoopen_path) {
01782             FILE *fp;
01783             int i;
01784             
01785             autopath = g_strsplit (config.autoopen_path, ":", -1);
01786             
01787             for (i = 0; autopath[i]; i++) {
01788                   path = g_strdup_printf ("%s/%s", mount_point, autopath[i]);
01789                   if (!(fp = fopen (path, "rt"))) {
01790                         g_free (path);
01791                         continue;
01792                   }
01793                   
01794                   g_free (path);
01795                   
01796                   handled = gvm_autoopen (fp, udi, device, mount_point);
01797                   fclose (fp);
01798                   break;
01799             }
01800             
01801             g_strfreev (autopath);
01802             
01803             if (handled)
01804                   return;
01805       }
01806       
01807       if (config.autobrowse)
01808             gvm_run_filemanager (udi, device, mount_point);
01809 #endif /* ENABLE_AUTOMOUNT */
01810 }
01811 
01812 #ifdef ENABLE_AUTOMOUNT
01813 static gboolean
01814 gvm_udi_is_subfs_mount (const char *udi)
01815 {
01816       int subfs = FALSE;
01817       char **callouts;
01818       int i;
01819       
01820       if ((callouts = libhal_device_get_property_strlist (hal_ctx, udi, "info.callouts.add", NULL))) {
01821             for (i = 0; callouts[i] != NULL; i++) {
01822                   if (!strcmp (callouts[i], "hald-block-subfs")) {
01823                         dbg ("subfs to handle mounting of %s; skipping\n", udi);
01824                         subfs = TRUE;
01825                         break;
01826                   }
01827             }
01828             
01829             libhal_free_string_array (callouts);
01830       }
01831       
01832       return subfs;
01833 }
01834 #endif
01835 
01836 static gboolean
01837 gvm_storage_device_is_cdrom (const char *storage)
01838 {
01839       gboolean is_cdrom = FALSE;
01840       char *media_type;
01841       
01842       if ((media_type = libhal_device_get_property_string (hal_ctx, storage, "storage.drive_type", NULL))) {
01843             is_cdrom = !strcmp (media_type, "cdrom");
01844             libhal_free_string (media_type);
01845       }
01846       
01847       return is_cdrom;
01848 }
01849 
01850 static gboolean
01851 gvm_udi_is_cdrom (const char *udi)
01852 {
01853       gboolean is_cdrom;
01854       char *storage;
01855       
01856       if (!(storage = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", NULL)))
01857             return FALSE;
01858       
01859       is_cdrom = gvm_storage_device_is_cdrom (storage);
01860       libhal_free_string (storage);
01861       
01862       return is_cdrom;
01863 }
01864 
01865 static void
01866 import_storage_camera_cb (GvmPromptCtx *ctx, int action)
01867 {
01868       switch (action) {
01869       case GVM_RESPONSE_IMPORT_PHOTOS:
01870             gvm_run_camera (ctx->udi, ctx->device, ctx->mount_point);
01871             break;
01872       case GVM_RESPONSE_BROWSE:
01873             gvm_run_filemanager (ctx->udi, ctx->device, ctx->mount_point);
01874             break;
01875       default:
01876             break;
01877       }
01878 }
01879 
01880 #ifdef ENABLE_AUTOMOUNT
01881 static void
01882 ipod_photo_cb (GvmPromptCtx *ctx, int action)
01883 {
01884       switch (action) {
01885       case GVM_RESPONSE_IMPORT_PHOTOS:
01886             gvm_run_camera (ctx->udi, ctx->device, ctx->path);
01887             break;
01888       case GVM_RESPONSE_SYNC_MUSIC:
01889             gvm_run_portable_media_player (ctx->udi, ctx->device, ctx->mount_point);
01890             break;
01891       default:
01892             break;
01893       }
01894 }
01895 #endif
01896 
01897 /*
01898  * gvm_device_mounted - called once a device has been
01899  * mounted. Launches any user-specified applications that require the
01900  * device to be mounted first (Mass-Storage cameras, iPods, CDs, DVDs,
01901  * etc)
01902  *
01903  */
01904 static void
01905 gvm_device_mounted (const char *udi)
01906 {
01907       char *mount_point = NULL;
01908 #ifdef ENABLE_AUTOMOUNT
01909       gboolean is_ipod = FALSE;
01910 #endif
01911       char *device = NULL;
01912       GvmPromptCtx *ctx;
01913       DBusError error;
01914       
01915       dbus_error_init (&error);
01916       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "block.device", &error))) {
01917             warn ("cannot get block.device: %s", error.message);
01918             if (dbus_error_is_set (&error))
01919                   dbus_error_free (&error);
01920             goto out;
01921       }
01922       
01923       if (!(mount_point = libhal_device_get_property_string (hal_ctx, udi, "volume.mount_point", &error))) {
01924             warn ("cannot get volume.mount_point: %s", error.message);
01925             if (dbus_error_is_set (&error))
01926                   dbus_error_free (&error);
01927             goto out;
01928       }
01929       
01930 #ifdef ENABLE_AUTOMOUNT
01931       /* this is where the magic happens */
01932       if (config.autophoto && gvm_udi_is_storage_camera (udi)) {
01933             ctx = gvm_prompt_ctx_new (GVM_PROMPT_IMPORT_STORAGE_CAMERA, import_storage_camera_cb,
01934                                 udi, device, mount_point, NULL);
01935             gvm_prompt (ctx, 0, NULL);
01936       } else if (config.autoipod && gvm_udi_is_portable_media_player (udi, &is_ipod)) {
01937             char *ipod_control = NULL;
01938             const char *photo_dir;
01939             
01940             if (is_ipod) {
01941                   ipod_control = g_build_filename (mount_point, "iPod_Control", NULL);
01942                   photo_dir = ipod_control;
01943             } else {
01944                   photo_dir = mount_point;
01945             }
01946             
01947             if (gvm_check_photos (udi, device, photo_dir)) {
01948                   /* we have ourselves an iPod Photo - need to prompt what to do */
01949                   ctx = gvm_prompt_ctx_new (GVM_PROMPT_IPOD_PHOTO, ipod_photo_cb,
01950                                       udi, device, mount_point, photo_dir);
01951                   gvm_prompt (ctx, 0, NULL);
01952             } else {
01953                   gvm_run_portable_media_player (udi, device, mount_point);
01954             }
01955             
01956             g_free (ipod_control);
01957       } else {
01958             gvm_autorun (udi, device, mount_point);
01959       }
01960 #else
01961       /* launch f-spot or whatever if the device is a camera */
01962       if (config.autophoto && gvm_udi_is_storage_camera (udi)) {
01963             ctx = gvm_prompt_ctx_new (GVM_PROMPT_IMPORT_STORAGE_CAMERA, import_storage_camera_cb,
01964                                 udi, device, mount_point, NULL);
01965             gvm_prompt (ctx, 0, NULL);
01966       } else {
01967             gvm_autorun (udi, device, mount_point);
01968       }
01969 #endif /* ENABLE_AUTOMOUNT */
01970       
01971  out:
01972       
01973       libhal_free_string (mount_point);
01974       libhal_free_string (device);
01975 }
01976 
01977 
01978 #ifdef ENABLE_AUTOMOUNT
01979 enum {
01980       MOUNT_CODEPAGE   = (1 << 0),
01981       MOUNT_DATA       = (1 << 1),
01982       MOUNT_DIRSYNC    = (1 << 2),
01983       MOUNT_DMASK      = (1 << 3),
01984       MOUNT_FMASK      = (1 << 4),
01985       MOUNT_FLUSH      = (1 << 5),
01986       MOUNT_IOCHARSET  = (1 << 6),
01987       MOUNT_MODE       = (1 << 7),
01988       MOUNT_NOATIME    = (1 << 8),
01989       MOUNT_NODIRATIME = (1 << 9),
01990       MOUNT_NOEXEC     = (1 << 10),
01991       MOUNT_QUIET      = (1 << 11),
01992       MOUNT_READ_ONLY  = (1 << 12),
01993       MOUNT_SHORTNAME  = (1 << 13),
01994       MOUNT_SYNC       = (1 << 14),
01995       MOUNT_UID        = (1 << 15),
01996       MOUNT_UMASK      = (1 << 16),
01997       MOUNT_UTF8       = (1 << 17),
01998 };
01999 
02000 static struct {
02001       const char *name;
02002       guint32 flag;
02003 } mount_options[] = {
02004       { "codepage=",  MOUNT_CODEPAGE   },  /* vfat */
02005       { "data=",      MOUNT_DATA       },  /* ext3 */
02006       { "dirsync",    MOUNT_DIRSYNC    },
02007       { "dmask=",     MOUNT_DMASK      },  /* vfat, ntfs */
02008       { "fmask=",     MOUNT_FMASK      },  /* vfat, ntfs */
02009       { "flush",      MOUNT_FLUSH      },  /* vfat */
02010       { "iocharset=", MOUNT_IOCHARSET  },  /* vfat, iso9660 */
02011       { "mode=",      MOUNT_MODE       },  /* iso9660 */
02012       { "noatime",    MOUNT_NOATIME    },
02013       { "nodiratime", MOUNT_NODIRATIME },
02014       { "noexec",     MOUNT_NOEXEC     },
02015       { "quiet",      MOUNT_QUIET      },
02016       { "ro",         MOUNT_READ_ONLY  },
02017       { "shortname=", MOUNT_SHORTNAME  },  /* vfat */
02018       { "sync",       MOUNT_SYNC       },
02019       { "uid=",       MOUNT_UID        },  /* vfat, ntfs, udf, iso9660 */
02020       { "umask=",     MOUNT_UMASK      },  /* vfat, ntfs, udf */
02021       { "utf8",       MOUNT_UTF8       },  /* vfat, iso9660 */
02022 };
02023 
02024 
02025 static const char *
02026 canonicalize_codeset (char *str)
02027 {
02028       register char *s = str;
02029       register char *d = str;
02030       
02031       while (*s) {
02032             if (*s >= 'A' && *s <= 'Z')
02033                   *d++ = *s++ + 0x20;
02034             else if (*s != '-' && *s != '_')
02035                   *d++ = *s++;
02036             else
02037                   s++;
02038       }
02039       
02040       *d = '\0';
02041       
02042       return str;
02043 }
02044 
02045 static struct {
02046       const char *codeset;
02047       const char *iocharset;
02048 } iocharset_mapping[] = {
02049       { "tis620",      "cp874"      },
02050       
02051       { "shiftjis*",   "cp932"      },
02052       { "sjis",        "cp932"      },
02053       
02054       { "gb18030",     "cp936"      },
02055       { "gbk",         "cp936"      },
02056       { "gb2312",      "cp936"      },
02057       
02058       { "euckr",       "cp949"      },
02059       
02060       { "big5*",       "cp950"      },
02061       { "euctw",       "cp950"      },
02062       
02063       /*{ "cp1251",      "cp1251"     },*/
02064       
02065       { "eucjp",       "euc-jp"     },
02066       
02067       { "iso88591",    "iso8859-1"  },
02068       { "iso88592",    "iso8859-2"  },
02069       { "iso88593",    "iso8859-3"  },
02070       { "iso88594",    "iso8859-4"  },
02071       { "iso88595",    "iso8859-5"  },
02072       { "iso88596",    "iso8859-6"  },
02073       { "iso88597",    "iso8859-7"  },
02074       { "iso88599",    "iso8859-9"  },
02075       { "iso885913",   "iso8859-13" },
02076       { "iso885914",   "iso8859-14" },
02077       { "iso885915",   "iso8859-15" },
02078       
02079       { "koi8r",       "koi8-r"     },
02080       { "koi8u",       "koi8-u"     },
02081       
02082       /*{ "utf8",       "utf8"       },*/
02083 };
02084 
02085 static const char *
02086 gvm_iocharset (void)
02087 {
02088       static const char *iocharset = NULL;
02089       char *locale, *codeset, *inptr;
02090       static int initialized = FALSE;
02091       const char *wildcard;
02092       size_t i, n;
02093       
02094       if (initialized)
02095             return iocharset;
02096       
02097       initialized = TRUE;
02098       
02099       locale = setlocale (LC_ALL, NULL);
02100       if (!locale || !strcmp (locale, "C") || !strcmp (locale, "POSIX")) {
02101             /* The locale "C"  or  "POSIX"  is  a  portable  locale;  its
02102              * LC_CTYPE  part  corresponds  to  the 7-bit ASCII character
02103              * set.
02104              */
02105             
02106             iocharset = NULL;
02107       } else {
02108 #ifdef HAVE_CODESET
02109             codeset = g_strdup (nl_langinfo (CODESET));
02110             canonicalize_codeset (codeset);
02111 #else
02112             /* A locale name is typically of  the  form  language[_terri-
02113              * tory][.codeset][@modifier],  where  language is an ISO 639
02114              * language code, territory is an ISO 3166 country code,  and
02115              * codeset  is  a  character  set or encoding identifier like
02116              * ISO-8859-1 or UTF-8.
02117              */
02118             if (!(codeset = strchr (locale, '.'))) {
02119                   /* charset unknown */
02120                   return NULL;
02121             }
02122             
02123             codeset++;
02124             
02125             /* ; is a hack for debian systems and / is a hack for Solaris systems */
02126             inptr = codeset;
02127             while (*inptr && !strchr ("@;/", *inptr))
02128                   inptr++;
02129             
02130             codeset = g_strndup (codeset, inptr - codeset);
02131             canonicalize_codeset (codeset);
02132 #endif
02133             
02134             for (i = 0; i < G_N_ELEMENTS (iocharset_mapping); i++) {
02135                   if ((wildcard = strchr (iocharset_mapping[i].codeset, '*'))) {
02136                         n = wildcard - iocharset_mapping[i].codeset;
02137                         if (!strncmp (codeset, iocharset_mapping[i].codeset, n)) {
02138                               iocharset = iocharset_mapping[i].iocharset;
02139                               break;
02140                         }
02141                   } else if (!strcmp (codeset, iocharset_mapping[i].codeset)) {
02142                         iocharset = iocharset_mapping[i].iocharset;
02143                         break;
02144                   }
02145             }
02146             
02147             if (!iocharset) {
02148                   iocharset = codeset;
02149                   codeset = NULL;
02150             }
02151             
02152             g_free (codeset);
02153       }
02154       
02155       return iocharset;
02156 }
02157 
02158 
02159 static gboolean
02160 gvm_mount_options (GPtrArray *options, guint32 opts, const char *type, const char *where)
02161 {
02162       char *option, *key, *tmp, *p;
02163       GSList *list, *l, *n;
02164       GConfClient *gconf;
02165       const char *dir;
02166       
02167       if (!strncmp (where, "/org/freedesktop/Hal/", 21)) {
02168             /* flatten the UDI */
02169             dir = p = tmp = g_strdup (where);
02170             while (*p != '\0') {
02171                   if (*p == '/')
02172                         *p = '_';
02173                   p++;
02174             }
02175       } else {
02176             dir = where;
02177             tmp = NULL;
02178       }
02179       
02180       key = g_strdup_printf ("/system/storage/%s/%s/mount_options", type, dir);
02181       g_free (tmp);
02182       
02183       gconf = gconf_client_get_default ();
02184       list = gconf_client_get_list (gconf, key, GCONF_VALUE_STRING, NULL);
02185       g_object_unref (gconf);
02186       g_free (key);
02187       
02188       if (list == NULL) {
02189             fprintf (stderr, "no mount options found for %s::%s\n", type, where);
02190             return FALSE;
02191       }
02192       
02193       for (l = list; l != NULL; l = n) {
02194             option = l->data;
02195             n = l->next;
02196             
02197             g_ptr_array_add (options, option);
02198             
02199             g_slist_free_1 (l);
02200       }
02201       
02202       if (opts & MOUNT_UID) {
02203             option = g_strdup_printf ("uid=%u", getuid ());
02204             g_ptr_array_add (options, option);
02205       }
02206       
02207       return TRUE;
02208 }
02209 
02210 
02211 /*
02212  * gvm_device_mount - mount the given device.
02213  *
02214  * @return TRUE iff the mount was succesful
02215  */
02216 static gboolean
02217 gvm_device_mount (const char *udi, gboolean interactive)
02218 {
02219       struct _MountPolicy *policy;
02220       
02221       dbg ("mounting %s...\n", udi);
02222       
02223       if (!gnome_mount || access (gnome_mount, F_OK | R_OK | X_OK) != 0) {
02224             g_free (gnome_mount);
02225             gnome_mount = g_find_program_in_path ("gnome-mount");
02226       }
02227       
02228       if (gnome_mount != NULL) {
02229             gboolean retval;
02230             char *command;
02231             
02232             policy = g_new (struct _MountPolicy, 1);
02233             policy->udi = g_strdup (udi);
02234             policy->apply = interactive;
02235             
02236             g_hash_table_insert (mount_table, policy->udi, policy);
02237             
02238             if (!interactive) {
02239                   command = g_strdup_printf ("%s --no-ui --hal-udi=%%h", gnome_mount);
02240             } else {
02241                   command = g_strdup_printf ("%s --hal-udi=%%h", gnome_mount);
02242             }
02243             
02244             retval = gvm_run_command (command, udi, NULL, NULL);
02245             g_free (command);
02246             
02247             if (!retval) {
02248                   g_hash_table_remove (mount_table, policy->udi);
02249                   g_free (policy->udi);
02250                   g_free (policy);
02251             }
02252             
02253             return retval;
02254       } else {
02255             char *mount_point, *fstype, *drive, **moptions, fmask_opt[12], *charset_opt = NULL;
02256             DBusMessage *dmesg, *reply;
02257             gboolean freev = FALSE;
02258             GPtrArray *options;
02259             guint32 opts = 0;
02260             DBusError error;
02261             size_t i, j;
02262             
02263             if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
02264                                               "org.freedesktop.Hal.Device.Volume",
02265                                               "Mount"))) {
02266                   dbg ("mount failed for %s: could not create dbus message\n", udi);
02267                   return FALSE;
02268             }
02269             
02270             if ((moptions = libhal_device_get_property_strlist (hal_ctx, udi, "volume.mount.valid_options", NULL))) {
02271                   for (i = 0; moptions[i]; i++) {
02272                         for (j = 0; j < G_N_ELEMENTS (mount_options); j++) {
02273                               if (!strcmp (moptions[i], mount_options[j].name))
02274                                     opts |= mount_options[j].flag;
02275                         }
02276                   }
02277                   
02278                   libhal_free_string_array (moptions);
02279             }
02280             
02281             options = g_ptr_array_new ();
02282             
02283             /* check volume-specific mount options */
02284             if (gvm_mount_options (options, opts, "volumes", udi)) {
02285                   freev = TRUE;
02286                   goto mount;
02287             }
02288             
02289             /* check drive specific mount options */
02290             if ((drive = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", NULL))) {
02291                   if (gvm_mount_options (options, opts, "drives", drive)) {
02292                         libhal_free_string (drive);
02293                         freev = TRUE;
02294                         goto mount;
02295                   }
02296                   libhal_free_string (drive);
02297             }
02298             
02299             if ((fstype = libhal_device_get_property_string (hal_ctx, udi, "volume.fstype", NULL))) {
02300                   const char *iocharset;
02301                   char uid[32];
02302                   mode_t mask;
02303                   
02304                   /* fall back to using fstype-specific mount options */
02305                   if (gvm_mount_options (options, opts, "default_options", fstype)) {
02306                         libhal_free_string (fstype);
02307                         freev = TRUE;
02308                         goto mount;
02309                   } else if (gvm_mount_options (options, opts, "default_options", "*")) {
02310                         libhal_free_string (fstype);
02311                         freev = TRUE;
02312                         goto mount;
02313                   }
02314                   
02315                   /* take our best guess at what the user would want */
02316                   if (!strcmp (fstype, "vfat")) {
02317                         if (opts & MOUNT_NOEXEC)
02318                               g_ptr_array_add (options, "noexec");
02319                         
02320                         /* preferably mount flush (opportunistic syncing) if available, else
02321                          * mount sync for "small" volumes (that are likely to be thumb drives)
02322                          */
02323                         if (opts & MOUNT_FLUSH) {
02324                               g_ptr_array_add (options, "flush");
02325                         } else if (opts & MOUNT_SYNC) {
02326                               dbus_uint64_t size;
02327                               
02328                               size = libhal_device_get_property_uint64 (hal_ctx, udi, "volume.size", NULL);
02329                               if (size <= (512 * 1024 * 1024))
02330                                     g_ptr_array_add (options, "sync");
02331                         }
02332                         
02333                         if (opts & MOUNT_FMASK) {
02334                               mask = umask (0);
02335                               snprintf (fmask_opt, sizeof (fmask_opt), "fmask=%#o", mask | 0111);
02336                               g_ptr_array_add (options, fmask_opt);
02337                               umask (mask);
02338                         }
02339                         
02340                         if (opts & MOUNT_SHORTNAME)
02341                               g_ptr_array_add (options, "shortname=lower");
02342                   } else if (!strcmp (fstype, "iso9660")) {
02343                         /* only care about uid= and iocharset= */
02344                   } else if (!strcmp (fstype, "udf")) {
02345                         /* also care about uid= and iocharset= */
02346                         if (opts & MOUNT_NOATIME)
02347                               g_ptr_array_add (options, "noatime");
02348                   }
02349                   
02350                   if (opts & (MOUNT_IOCHARSET|MOUNT_UTF8)) {
02351                         if ((iocharset = gvm_iocharset ())) {
02352                               if ((opts & MOUNT_UTF8) && !strcmp (iocharset, "utf8")) {
02353                                     g_ptr_array_add (options, "utf8");
02354                               } else if (opts & MOUNT_IOCHARSET) {
02355                                     charset_opt = g_strdup_printf ("iocharset=%s", iocharset);
02356                                     g_ptr_array_add (options, charset_opt);
02357                               }
02358                         }
02359                   }
02360                   
02361                   if (opts & MOUNT_UID) {
02362                         snprintf (uid, sizeof (uid) - 1, "uid=%u", getuid ());
02363                         g_ptr_array_add (options, uid);
02364                   }
02365                   
02366                   libhal_free_string (fstype);
02367             }
02368             
02369       mount:
02370             
02371             mount_point = "";
02372             fstype = "";
02373             
02374             if (!dbus_message_append_args (dmesg, DBUS_TYPE_STRING, &mount_point, DBUS_TYPE_STRING, &fstype,
02375                                      DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options->pdata, options->len,
02376                                      DBUS_TYPE_INVALID)) {
02377                   dbg ("mount failed for %s: could not append args to dbus message\n", udi);
02378                   dbus_message_unref (dmesg);
02379                   return FALSE;
02380             }
02381             
02382             if (freev) {
02383                   for (i = 0; i < options->len; i++)
02384                         g_free (options->pdata[i]);
02385             }
02386             
02387             g_ptr_array_free (options, TRUE);
02388             g_free (charset_opt);
02389             
02390             policy = g_new (struct _MountPolicy, 1);
02391             policy->udi = g_strdup (udi);
02392             policy->apply = interactive;
02393             
02394             g_hash_table_insert (mount_table, policy->udi, policy);
02395             
02396             dbus_error_init (&error);
02397             if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
02398                   dbg ("mount failed for %s: %s\n", udi, error.message);
02399                   g_hash_table_remove (mount_table, policy->udi);
02400                   dbus_message_unref (dmesg);
02401                   dbus_error_free (&error);
02402                   g_free (policy->udi);
02403                   g_free (policy);
02404                   return FALSE;
02405             }
02406             
02407             dbg ("mount queued for %s\n", udi);
02408             
02409             dbus_message_unref (dmesg);
02410             dbus_message_unref (reply);
02411             
02412             return TRUE;
02413       }
02414 }
02415 
02416 
02417 /*
02418  * gvm_device_unmount - unmount the given device.
02419  *
02420  * @return TRUE iff the unmount was succesful
02421  */
02422 static gboolean
02423 gvm_device_unmount (const char *udi)
02424 {
02425       DBusMessage *dmesg, *reply;
02426       char **options = NULL;
02427       DBusError error;
02428       gboolean retval;
02429       char *command;
02430       
02431       dbg ("unmounting %s...\n", udi);
02432       
02433       if (!gnome_mount || access (gnome_mount, F_OK | R_OK | X_OK) != 0) {
02434             g_free (gnome_mount);
02435             gnome_mount = g_find_program_in_path ("gnome-mount");
02436       }
02437       
02438       if (gnome_mount != NULL) {
02439             command = g_strdup_printf ("%s --unmount --hal-udi=%%h", gnome_mount);
02440             retval = gvm_run_command (command, udi, NULL, NULL);
02441             g_free (command);
02442             
02443             return retval;
02444       } else {
02445             if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
02446                                               "org.freedesktop.Hal.Device.Volume",
02447                                               "Unmount"))) {
02448                   dbg ("unmount failed for %s: could not create dbus message\n", udi);
02449                   return FALSE;
02450             }
02451             
02452             if (!dbus_message_append_args (dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, 0,
02453                                      DBUS_TYPE_INVALID)) {
02454                   dbg ("unmount failed for %s: could not append args to dbus message\n", udi);
02455                   dbus_message_unref (dmesg);
02456                   return FALSE;
02457             }
02458             
02459             dbus_error_init (&error);
02460             if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
02461                   dbg ("unmount failed for %s: %s\n", udi, error.message);
02462                   dbus_message_unref (dmesg);
02463                   dbus_error_free (&error);
02464                   return FALSE;
02465             }
02466             
02467             dbg ("unmount queued for %s\n", udi);
02468             
02469             dbus_message_unref (dmesg);
02470             dbus_message_unref (reply);
02471             
02472             return TRUE;
02473       }
02474 }
02475 
02476 /*
02477  * gvm_run_cdplay - if so configured, execute the user-specified CD player on
02478  * the given device node
02479  */
02480 static void
02481 gvm_run_cdplayer (const char *udi, const char *device, const char *mount_point)
02482 {
02483       if (config.autoplay_cda_command != NULL)
02484             gvm_run_command (config.autoplay_cda_command, udi, device, mount_point);
02485 }
02486 
02487 static void
02488 cda_extra_cb (GvmPromptCtx *ctx, int action)
02489 {
02490       switch (action) {
02491       case GVM_RESPONSE_BROWSE:
02492             if (!gvm_udi_is_subfs_mount (ctx->udi))
02493                   gvm_device_mount (ctx->udi, TRUE);
02494             break;
02495       case GVM_RESPONSE_PLAY:
02496             gvm_run_cdplayer (ctx->udi, ctx->device, NULL);
02497             break;
02498       default:
02499             break;
02500       }
02501 }
02502 
02503 /*
02504  * gvm_ask_mixed - if a mixed mode CD (CD Plus) is inserted, we can either
02505  * mount the data tracks or play the audio tracks.  How we handle that depends
02506  * on the user's configuration.  If the configuration allows either option,
02507  * we ask.
02508  */
02509 static void
02510 gvm_ask_mixed (const char *udi)
02511 {
02512       char *device = NULL;
02513       GvmPromptCtx *ctx;
02514       DBusError error;
02515       
02516       dbus_error_init (&error);
02517       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "block.device", &error))) {
02518             warn ("cannot get block.device: %s", error.message);
02519             dbus_error_free (&error);
02520             return;
02521       }
02522       
02523       if (config.automount_media && config.autoplay_cda) {
02524             ctx = gvm_prompt_ctx_new (GVM_PROMPT_CDA_EXTRA, cda_extra_cb, udi, device, NULL, NULL);
02525             gvm_prompt (ctx, 0, NULL);
02526       } else if (config.automount_media) {
02527             if (!gvm_udi_is_subfs_mount (udi))
02528                   gvm_device_mount (udi, TRUE);
02529       } else if (config.autoplay_cda) {
02530             gvm_run_cdplayer (udi, device, NULL);
02531       }
02532       
02533       libhal_free_string (device);
02534 }
02535 
02536 
02537 typedef enum {
02538       WRITER_TYPE_NONE,
02539       WRITER_TYPE_CDR,
02540       WRITER_TYPE_DVD
02541 } writer_t;
02542 
02543 static struct {
02544       const char *disc;
02545       const char *drive;
02546       writer_t type;
02547 } burners[] = {
02548       { "cd_r",        "storage.cdrom.cdr",       WRITER_TYPE_CDR },
02549       { "cd_rw",       "storage.cdrom.cdrw",      WRITER_TYPE_CDR },
02550       { "dvd_r",       "storage.cdrom.dvdr",      WRITER_TYPE_DVD },
02551       { "dvd_rw",      "storage.cdrom.dvdrw",     WRITER_TYPE_DVD },
02552       { "dvd_ram",     "storage.cdrom.dvdram",    WRITER_TYPE_DVD },
02553       { "dvd_plus_r",  "storage.cdrom.dvdplusr",  WRITER_TYPE_DVD },
02554       { "dvd_plus_rw", "storage.cdrom.dvdplusrw", WRITER_TYPE_DVD },
02555 };
02556 
02557 /*
02558  * gvm_cdrom_media_is_writable - returns the type of media that can be written
02559  */
02560 static writer_t
02561 gvm_cdrom_media_is_writable (const char *udi)
02562 {
02563       writer_t retval = WRITER_TYPE_NONE;
02564       char *drive = NULL;
02565       char *disc = NULL;
02566       size_t i;
02567       
02568       if (!(disc = libhal_device_get_property_string (hal_ctx, udi, "volume.disc.type", NULL)))
02569             return FALSE;
02570       
02571       for (i = 0; i < G_N_ELEMENTS (burners); i++) {
02572             if (!strcmp (burners[i].disc, disc)) {
02573                   if (!(drive = libhal_device_get_property_string (hal_ctx, udi, "info.parent", NULL)))
02574                         break;
02575                   
02576                   if (libhal_device_get_property_bool (hal_ctx, drive, burners[i].drive, NULL))
02577                         retval = burners[i].type;
02578                   
02579                   break;
02580             }
02581       }
02582       
02583       libhal_free_string (drive);
02584       libhal_free_string (disc);
02585       
02586       return retval;
02587 }
02588 
02589 static void
02590 burn_cdr_cb (GvmPromptCtx *ctx, int action)
02591 {
02592       const char *command;
02593       
02594       switch (action) {
02595       case GVM_RESPONSE_WRITE_AUDIO_CD:
02596             command = config.autoburn_audio_cd_command;
02597             break;
02598       case GVM_RESPONSE_WRITE_DATA_CD:
02599             command = config.autoburn_data_cd_command;
02600             break;
02601       default:
02602             return;
02603       }
02604       
02605       gvm_run_command (command, ctx->udi, ctx->device, ctx->mount_point);
02606 }
02607 
02608 /*
02609  * gvm_run_cdburner - execute the user-specified CD burner command on the
02610  * given device node, if so configured
02611  */
02612 static void
02613 gvm_run_cdburner (const char *udi, int type, const char *device, const char *mount_point)
02614 {
02615       GvmPromptCtx *ctx;
02616       GvmPrompt prompt;
02617       
02618       if (!config.autoburn)
02619             return;
02620       
02621       if (type == WRITER_TYPE_DVD)
02622             prompt = GVM_PROMPT_WRITE_DVD;
02623       else
02624             prompt = GVM_PROMPT_WRITE_CDR;
02625       
02626       ctx = gvm_prompt_ctx_new (prompt, burn_cdr_cb, udi, device, mount_point, NULL);
02627       gvm_prompt (ctx, 0, NULL);
02628 }
02629 
02630 
02631 /*
02632  * gvm_cdrom_policy - There has been a media change event on the CD-ROM
02633  * associated with the given UDI.  Enforce policy.
02634  */
02635 static void
02636 gvm_cdrom_policy (const char *udi)
02637 {
02638       dbus_bool_t has_audio;
02639       dbus_bool_t has_data;
02640       char *device = NULL;
02641       DBusError error;
02642       writer_t type;
02643       
02644       dbus_error_init (&error);
02645       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "block.device", &error))) {
02646             warn ("cannot get block.device: %s", error.message);
02647             dbus_error_free (&error);
02648             return;
02649       }
02650       
02651       if (libhal_device_get_property_bool (hal_ctx, udi, "volume.disc.is_blank", NULL)) {
02652             if ((type = gvm_cdrom_media_is_writable (udi)))
02653                   gvm_run_cdburner (udi, type, device, NULL);
02654       } else {
02655             has_audio = libhal_device_get_property_bool (hal_ctx, udi, "volume.disc.has_audio", NULL);
02656             has_data = libhal_device_get_property_bool (hal_ctx, udi, "volume.disc.has_data", NULL);
02657             
02658             if (has_audio && has_data) {
02659                   gvm_ask_mixed (udi);
02660             } else if (has_audio) {
02661                   if (config.autoplay_cda)
02662                         gvm_run_cdplayer (udi, device, NULL);
02663             } else if (has_data) {
02664                   if (config.automount_media && !gvm_udi_is_subfs_mount (udi))
02665                         gvm_device_mount (udi, TRUE);
02666             }
02667       }
02668       
02669       /** @todo enforce policy for all the new disc types now supported */
02670       
02671       libhal_free_string (device);
02672 }
02673 
02674 /*
02675  * gvm_media_changed - generic media change handler.
02676  *
02677  * This is called on a UDI and the media's parent device in response to a media
02678  * change event.  We have to decipher the storage media type to run the
02679  * appropriate media-present check.  Then, if there is indeed media in the
02680  * drive, we enforce the appropriate policy.
02681  *
02682  * At the moment, we only handle CD-ROM and DVD drives.
02683  *
02684  * Returns TRUE if the device was handled or FALSE otherwise
02685  */
02686 static gboolean
02687 gvm_media_changed (const char *udi, const char *storage_device)
02688 {
02689       gboolean handled = FALSE;
02690       DBusError error;
02691       
02692       /* Refuse to enforce policy on removable media if drive is locked */
02693       dbus_error_init (&error);
02694       if (libhal_device_property_exists (hal_ctx, storage_device, "info.locked", NULL)
02695           && libhal_device_get_property_bool (hal_ctx, storage_device, "info.locked", NULL)) {
02696             dbg ("Drive with udi %s is locked through hal; skipping policy\n", storage_device);
02697             /* we return TRUE here because the device is locked - we can pretend we handled it */
02698             return TRUE;
02699       }
02700       
02701       if (gvm_storage_device_is_cdrom (storage_device)) {
02702             gvm_cdrom_policy (udi);
02703             handled = TRUE;
02704       }
02705       
02706       return handled;
02707 }
02708 #endif /* ENABLE_AUTOMOUNT */
02709 
02710 
02711 static void
02712 import_camera_cb (GvmPromptCtx *ctx, int action)
02713 {
02714       if (action == GVM_RESPONSE_IMPORT_PHOTOS)
02715             gvm_run_camera (ctx->udi, NULL, NULL);
02716 }
02717 
02718 
02719 static int
02720 strptrcmp (const void *strptr0, const void *strptr1)
02721 {
02722       return strcmp (*((const char **) strptr0), *((const char **) strptr1));
02723 }
02724 
02725 
02726 typedef gboolean (* DeviceAddedHandler) (const char *udi, const char *capability);
02727 
02728 #ifdef ENABLE_AUTOMOUNT
02729 static gboolean
02730 block_device_added (const char *udi, const char *capability GNUC_UNUSED)
02731 {
02732       char *fsusage = NULL, *device = NULL, *storage_device = NULL;
02733       DBusError error;
02734       int mountable;
02735       int crypto;
02736       
02737       dbus_error_init (&error);
02738       
02739       crypto = FALSE;
02740       
02741       /* is this a mountable volume? */
02742       if (!(mountable = libhal_device_get_property_bool (hal_ctx, udi, "block.is_volume", NULL))) {
02743             dbg ("not a mountable volume: %s\n", udi);
02744             goto out;
02745       }
02746       
02747       /* if it is a volume, it must have a device node */
02748       if (!(device = libhal_device_get_property_string (hal_ctx, udi, "block.device", &error))) {
02749             dbg ("cannot get block.device: %s\n", error.message);
02750             goto out;
02751       }
02752       
02753       if (mountable) {
02754             /* only mount if the block device has a sensible filesystem */
02755             if (!(fsusage = libhal_device_get_property_string (hal_ctx, udi, "volume.fsusage", &error))) {
02756                   dbg ("unable to get fsusage for %s: %s\n", udi, error.message);
02757                   mountable = FALSE;
02758             } else if (!strcmp (fsusage, "crypto")) {
02759                   dbg ("encrypted volume found: %s\n", udi);
02760                   /* TODO: handle encrypted volumes */
02761                   mountable = FALSE;
02762                   crypto = TRUE;
02763             } else if (strcmp (fsusage, "filesystem") != 0) {
02764                   dbg ("no sensible filesystem for %s\n", udi);
02765                   mountable = FALSE;
02766             }
02767       }
02768       
02769       /* get the backing storage device */
02770       if (!(storage_device = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", &error))) {
02771             dbg ("cannot get block.storage_device: %s\n", error.message);
02772             goto out;
02773       }
02774       
02775       /* if the partition_table_changed flag is set, we don't want
02776        * to mount as a partitioning tool might be modifying this
02777        * device */
02778       if (libhal_device_get_property_bool (hal_ctx, storage_device, "storage.partition_table_changed", NULL)) {
02779             dbg ("partition table changed for %s\n", storage_device);
02780             goto out;
02781       }
02782       
02783       /*
02784        * Does this device support removable media?  Note that we
02785        * check storage_device and not our own UDI
02786        */
02787       if (libhal_device_get_property_bool (hal_ctx, storage_device, "storage.removable", NULL)) {
02788             /* we handle media change events separately */
02789             dbg ("Changed: %s\n", device);
02790             if (gvm_media_changed (udi, storage_device))
02791                   goto out;
02792       }
02793       
02794       if (config.automount_drives && (mountable || crypto)) {
02795             if (!gvm_udi_is_subfs_mount (udi)) {
02796                   if (libhal_device_get_property_bool (hal_ctx, udi, "volume.ignore", NULL)) {
02797                         dbg ("volume.ignore set to true on %s, not mounting\n", udi);
02798                         } else if (!libhal_device_get_property_bool (hal_ctx, storage_device, "storage.automount_enabled_hint", NULL)) {
02799                         dbg ("automounting disabled for %s, not mounting\n", storage_device);
02800                   } else {
02801                         gvm_device_mount (udi, TRUE);
02802                   }
02803             }
02804       }
02805       
02806  out:
02807       
02808       if (dbus_error_is_set (&error))
02809             dbus_error_free (&error);
02810       
02811       libhal_free_string (device);
02812       libhal_free_string (fsusage);
02813       libhal_free_string (storage_device);
02814       
02815       return TRUE;
02816 }
02817 #endif /* ENABLE_AUTOMOUNT */
02818 
02819 static gboolean
02820 camera_device_added (const char *udi, const char *capability GNUC_UNUSED)
02821 {
02822       GvmPromptCtx *ctx;
02823       
02824       /* check that the camera is a non-storage camera */
02825       if (!gvm_udi_is_camera (udi))
02826             return TRUE;
02827       
02828       if (!(config.autophoto && config.autophoto_command))
02829             return FALSE;
02830       
02831       ctx = gvm_prompt_ctx_new (GVM_PROMPT_IMPORT_CAMERA, import_camera_cb, udi, NULL, NULL, NULL);
02832       gvm_prompt (ctx, 0, NULL);
02833       
02834       return TRUE;
02835 }
02836 
02837 static struct {
02838       const char *capability;
02839       gboolean *autoexec;
02840       char **command;
02841 } inputs[] = {
02842       { "input.keyboard", &config.autokeyboard, &config.autokeyboard_command },
02843       { "input.mouse",    &config.automouse,    &config.automouse_command    },
02844       { "input.tablet",   &config.autotablet,   &config.autotablet_command   },
02845 };
02846 
02847 static gboolean
02848 input_device_added (const char *udi, const char *capability)
02849 {
02850       /* input device (keyboard, mouse, wacom tablet, etc...) */
02851       const char *command = NULL;
02852       int autoexec = FALSE;
02853       DBusError error;
02854       char *device;
02855       size_t i;
02856       
02857       for (i = 0; i < G_N_ELEMENTS (inputs); i++) {
02858             if (!strcmp (inputs[i].capability, capability)) {
02859                   autoexec = *inputs[i].autoexec;
02860                   command = *inputs[i].command;
02861                   break;
02862             }
02863       }
02864       
02865       if (i == G_N_ELEMENTS (inputs)) {
02866             /* we don't handle this type of device */
02867             return FALSE;
02868       }
02869       
02870       if (autoexec && command) {
02871             dbus_error_init (&error);
02872             if ((device = libhal_device_get_property_string (hal_ctx, udi, "input.device", &error))) {
02873                   gvm_run_command (command, udi, device, NULL);
02874                   libhal_free_string (device);
02875             } else {
02876                   warn ("cannot get input.device property: %s", error.message);
02877                   dbus_error_free (&error);
02878             }
02879             
02880             return TRUE;
02881       }
02882       
02883       return FALSE;
02884 }
02885 
02886 static gboolean
02887 pda_device_added (const char *udi, const char *capability GNUC_UNUSED)
02888 {
02889       DBusError error;
02890       char *platform;
02891       
02892       dbus_error_init (&error);
02893       
02894       if (!(platform = libhal_device_get_property_string (hal_ctx, udi, "pda.platform", &error))) {
02895             warn ("cannot get pda.platform property: %s", error.message);
02896             dbus_error_free (&error);
02897             return TRUE;
02898       }
02899       
02900       if (!strcmp (platform, "palm")) {
02901             if (config.autopilot)
02902                   gvm_run_pilot (udi);
02903       } else if (!strcmp (platform, "pocketpc")) {
02904             if (config.autopocketpc)
02905                   gvm_run_pocketpc (udi);
02906       }
02907       
02908       libhal_free_string (platform);
02909       
02910       return TRUE;
02911 }
02912 
02913 static gboolean
02914 media_player_device_added (const char *udi, const char *capability GNUC_UNUSED)
02915 {
02916       char *access_method;
02917       DBusError error;
02918       
02919       dbus_error_init (&error);
02920       
02921       if (!(access_method = libhal_device_get_property_string (hal_ctx, udi, "portable_audio_player.access_method", &error))) {
02922             warn ("cannot get portable_audio_player.access_method property: %s", error.message);
02923             dbus_error_free (&error);
02924             return TRUE;
02925       }
02926       
02927       if (!strcmp (access_method, "storage")) {
02928             /* these get handled after being mounted */
02929             g_free (access_method);
02930             return TRUE;
02931       }
02932       
02933       g_free (access_method);
02934       
02935       if (config.autoipod)
02936             gvm_run_portable_media_player (udi, NULL, NULL);
02937       
02938       return TRUE;
02939 }
02940 
02941 static gboolean
02942 printer_device_added (const char *udi, const char *capability GNUC_UNUSED)
02943 {
02944       if (config.autoprinter) {
02945             gvm_run_printer (udi);
02946             return TRUE;
02947       }
02948       
02949       return FALSE;
02950 }
02951 
02952 static gboolean
02953 scanner_device_added (const char *udi, const char *capability GNUC_UNUSED)
02954 {
02955       if (config.autoscanner) {
02956             gvm_run_scanner (udi);
02957             return TRUE;
02958       }
02959       
02960       return FALSE;
02961 }
02962 
02963 static gboolean
02964 webcam_device_added (const char *udi, const char *capability GNUC_UNUSED)
02965 {
02966       if (config.autowebcam) {
02967             gvm_run_webcam (udi);
02968             return TRUE;
02969       }
02970       
02971       return FALSE;
02972 }
02973 
02974 /* Note: this list must be sorted alphabetically for the algorithm in hal_device_added to work */
02975 static struct {
02976       const char *capability;
02977       DeviceAddedHandler handler;
02978 } devices[] = {
02979 #ifdef ENABLE_AUTOMOUNT
02980       { "block",                 block_device_added        },
02981 #endif
02982       { "camera",                camera_device_added       },
02983       /*{ "input",                 input_device_added        },*/
02984       { "input.keyboard",        input_device_added        },
02985       { "input.mouse",           input_device_added        },
02986       { "input.tablet",          input_device_added        },
02987       { "pda",                   pda_device_added          },
02988       { "portable_audio_player", media_player_device_added },
02989       { "printer",               printer_device_added      },
02990       { "scanner",               scanner_device_added      },
02991       { "video4linux",           webcam_device_added       },
02992 };
02993 
02994 /** Invoked when a device is added to the Global Device List. 
02995  *
02996  *  @param  ctx                 LibHal context
02997  *  @param  udi                 Universal Device Id
02998  */
02999 static void
03000 hal_device_added (LibHalContext *ctx GNUC_UNUSED,
03001               const char *udi)
03002 {
03003       char **capabilities;
03004       size_t i, j, n;
03005       
03006       if (!gvm_user_is_active ())
03007             return;
03008       
03009       dbg ("Device added: %s\n", udi);
03010       
03011       if (!(capabilities = libhal_device_get_property_strlist (hal_ctx, udi, "info.capabilities", NULL)))
03012             return;
03013       
03014       for (n = 0; capabilities[n]; n++)
03015             ;
03016       
03017       qsort (capabilities, n, sizeof (char *), strptrcmp);
03018       
03019       for (i = 0, j = 0; i < G_N_ELEMENTS (devices) && j < n; i++) {
03020             int cmp = -1;
03021             
03022             while (j < n && (cmp = strcmp (capabilities[j], devices[i].capability)) < 0)
03023                   j++;
03024             
03025             if (cmp == 0) {
03026                   if (devices[i].handler (udi, capabilities[j]))
03027                         break;
03028                   j++;
03029             }
03030       }
03031       
03032       libhal_free_string_array (capabilities);
03033 }
03034 
03035 /** Invoked when a device is removed from the Global Device List. 
03036  *
03037  *  @param  ctx                 LibHal context
03038  *  @param  udi                 Universal Device Id
03039  */
03040 static void
03041 hal_device_removed (LibHalContext *ctx GNUC_UNUSED, const char *udi)
03042 {
03043       GtkDialog *dialog;
03044 #ifdef ENABLE_AUTOMOUNT
03045       char *device_udi;
03046 #endif
03047       
03048       dbg ("Device removed: %s\n", udi);
03049       
03050 #ifdef ENABLE_AUTOMOUNT
03051       /* unmounted leftover mount point */
03052       if ((device_udi = g_hash_table_lookup (device_table, udi))) {
03053             if (gvm_device_unmount (device_udi)) {
03054                   GSList *l, *n;
03055                               
03056                   for (l = mounted_volumes; l != NULL; l = n) {
03057                         n = l->next;
03058                         if (strcmp (udi, (const char *) l->data) == 0) {
03059                               g_free (l->data);
03060                               mounted_volumes = g_slist_delete_link (mounted_volumes, l);
03061                               break;
03062                         }
03063                   }
03064             }
03065       }
03066 #endif /* ENABLE_AUTOMOUNT */
03067       
03068       if ((dialog = g_hash_table_lookup (dialogs, udi)))
03069             gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
03070 }
03071 
03072 /** Invoked when device in the Global Device List acquires a new capability.
03073  *
03074  *  @param  ctx                 LibHal context
03075  *  @param  udi                 Universal Device Id
03076  *  @param  capability          Name of capability
03077  */
03078 static void
03079 hal_device_new_capability (LibHalContext *ctx GNUC_UNUSED,
03080                      const char *udi GNUC_UNUSED, 
03081                      const char *capability GNUC_UNUSED)
03082 {
03083 }
03084 
03085 /** Invoked when device in the Global Device List loses a capability.
03086  *
03087  *  @param  ctx                 LibHal context
03088  *  @param  udi                 Universal Device Id
03089  *  @param  capability          Name of capability
03090  */
03091 static void
03092 hal_device_lost_capability (LibHalContext *ctx GNUC_UNUSED,
03093                       const char *udi GNUC_UNUSED, 
03094                       const char *capability GNUC_UNUSED)
03095 {
03096 }
03097 
03098 /** Invoked when a property of a device in the Global Device List is
03099  *  changed, and we have subscribed to changes for that device.
03100  *
03101  *  @param  ctx                 LibHal context
03102  *  @param  udi                 Univerisal Device Id
03103  *  @param  key                 Key of property
03104  */
03105 static void
03106 hal_property_modified (LibHalContext *ctx GNUC_UNUSED,
03107                    const char *udi, 
03108                    const char *key,
03109                    dbus_bool_t is_removed GNUC_UNUSED, 
03110                    dbus_bool_t is_added GNUC_UNUSED)
03111 {
03112 #ifdef ENABLE_AUTOMOUNT
03113       struct _MountPolicy *policy;
03114 #endif
03115       gboolean mounted;
03116 #ifdef ENABLE_AUTOMOUNT
03117       GSList *l, *n;
03118 #endif
03119       
03120       if (strcmp (key, "volume.is_mounted") != 0)
03121             return;
03122       
03123       mounted = libhal_device_get_property_bool (hal_ctx, udi, key, NULL);
03124       
03125       if (mounted) {
03126             dbg ("Mounted: %s\n", udi);
03127             
03128 #ifdef ENABLE_AUTOMOUNT
03129             if ((policy = g_hash_table_lookup (mount_table, udi))) {
03130                   char *device;
03131                   
03132                   g_hash_table_remove (mount_table, udi);
03133                   
03134                   /* add to list of all volumes mounted during lifetime */
03135                   mounted_volumes = g_slist_append (mounted_volumes, g_strdup (udi));
03136                   
03137                   if ((device = libhal_device_get_property_string (hal_ctx, udi, "block.storage_device", NULL)))
03138                         g_hash_table_insert (device_table, g_strdup (udi), device);
03139                   
03140                   if (policy->apply)
03141                         gvm_device_mounted (udi);
03142                   
03143                   g_free (policy->udi);
03144                   g_free (policy);
03145             } else if (gvm_user_is_active ()) {
03146                   dbg ("not in mount queue: %s\n", udi);
03147                   /*gvm_device_mounted (udi);*/
03148             }
03149 #else
03150             gvm_device_mounted (udi);
03151 #endif /* ENABLE_AUTOMOUNT */
03152             
03153 #ifdef ENABLE_NOTIFY
03154             statfs_mount_info_add (udi);
03155 #endif
03156       } else {
03157             dbg ("Unmounted: %s\n", udi);
03158             
03159 #ifdef ENABLE_NOTIFY
03160             /* remove the udi from the statfs_mounts list */
03161             statfs_mount_info_remove (udi);
03162 #endif
03163             
03164 #ifdef ENABLE_AUTOMOUNT
03165             g_hash_table_remove (device_table, udi);
03166             
03167             /* unmount all volumes mounted during lifetime */
03168             for (l = mounted_volumes; l != NULL; l = n) {
03169                   n = l->next;
03170                   if (strcmp (udi, (const char *) l->data) == 0) {
03171                         g_free (l->data);
03172                         mounted_volumes = g_slist_delete_link (mounted_volumes, l);
03173                         break;
03174                   }
03175             }
03176 #endif /* ENABLE_AUTOMOUNT */
03177       }
03178 }
03179 
03180 #ifdef ENABLE_AUTOMOUNT
03181 static void
03182 gvm_device_eject (const char *udi)
03183 {
03184       char *storage, **volumes = NULL;
03185       DBusMessage *dmesg, *reply;
03186       const char *volume = udi;
03187       char **options = NULL;
03188       DBusError error;
03189       char *command;
03190       int i, n;
03191       
03192       if (!libhal_device_get_property_bool (hal_ctx, udi, "block.is_volume", NULL)) {
03193             dbus_error_init (&error);
03194             volumes = libhal_find_device_by_capability (hal_ctx, "volume", &n, &error);
03195             if (dbus_error_is_set (&error)) {
03196                   dbus_error_free (&error);
03197                   return;
03198             }
03199             
03200             volume = NULL;
03201             for (i = 0; i < n; i++) {
03202                   volume = volumes[i];
03203                   
03204                   if (!(storage = libhal_device_get_property_string (hal_ctx, volume, "info.parent", NULL)))
03205                         continue;
03206                   
03207                   if (!strcmp (udi, storage)) {
03208                         libhal_free_string (storage);
03209                         break;
03210                   }
03211                   
03212                   libhal_free_string (storage);
03213             }
03214       }
03215       
03216       dbg ("ejecting %s...\n", volume);
03217       
03218       if (!gnome_mount || access (gnome_mount, F_OK | R_OK | X_OK) != 0) {
03219             g_free (gnome_mount);
03220             gnome_mount = g_find_program_in_path ("gnome-mount");
03221       }
03222       
03223       if (gnome_mount != NULL) {
03224             command = g_strdup_printf ("%s --eject --hal-udi=%%h", gnome_mount);
03225             gvm_run_command (command, volume, NULL, NULL);
03226             g_free (command);
03227       } else {
03228             if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.Hal", volume,
03229                                               "org.freedesktop.Hal.Device.Volume",
03230                                               "Eject"))) {
03231                   goto done;
03232             }
03233             
03234             if (!dbus_message_append_args (dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, 0,
03235                                      DBUS_TYPE_INVALID)) {
03236                   dbg ("eject failed for %s: could not append args to dbus message\n", udi);
03237                   dbus_message_unref (dmesg);
03238                   goto done;
03239             }
03240             
03241             dbus_error_init (&error);
03242             if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
03243                   dbg ("eject failed for %s: %s\n", volume, error.message);
03244                   dbus_message_unref (dmesg);
03245                   dbus_error_free (&error);
03246                   goto done;
03247             }
03248             
03249             dbus_message_unref (dmesg);
03250             dbus_message_unref (reply);
03251       }
03252       
03253  done:
03254       
03255       if (volumes)
03256             libhal_free_string_array (volumes);
03257 }
03258 
03259 /** Invoked when a device in the GDL emits a condition that cannot be
03260  *  expressed in a property (like when the processor is overheating)
03261  *
03262  *  @param  ctx                 LibHal context
03263  *  @param  udi                 Univerisal Device Id
03264  *  @param  condition_name      Name of condition
03265  *  @param  message             D-BUS message with parameters
03266  */
03267 static void
03268 hal_device_condition (LibHalContext *ctx GNUC_UNUSED,
03269                   const char *udi, const char *condition_name,
03270                   const char *condition_details GNUC_UNUSED)
03271 {
03272       if (!gvm_user_is_active ())
03273             return;
03274       
03275       if (!strcmp (condition_name, "EjectPressed"))
03276             gvm_device_eject (udi);
03277 }
03278 #endif /* ENABLE_AUTOMOUNT */
03279 
03280 static gboolean
03281 reinit_dbus (gpointer user_data GNUC_UNUSED)
03282 {
03283       if (gvm_dbus_init ()) {
03284             /* dbus daemon is up, get a hal context */
03285             if ((hal_ctx = gvm_hal_init ()))
03286                   libhal_ctx_set_dbus_connection (hal_ctx, dbus_connection);
03287             else
03288                   exit (1);
03289             
03290             return FALSE;
03291       }
03292       
03293       /* dbus deamon not back up yet */
03294       
03295       return TRUE;
03296 }
03297 
03298 static DBusHandlerResult
03299 gvm_dbus_filter_function (DBusConnection *connection GNUC_UNUSED, DBusMessage *message, void *user_data GNUC_UNUSED)
03300 {
03301       if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
03302           strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
03303             libhal_ctx_free (hal_ctx);
03304             hal_ctx = NULL;
03305             
03306             dbus_connection_unref (dbus_connection);
03307             dbus_connection = NULL;
03308             
03309             g_timeout_add (3000, reinit_dbus, NULL);
03310             
03311             return DBUS_HANDLER_RESULT_HANDLED;
03312       }
03313       
03314       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
03315 }
03316 
03317 static gboolean
03318 gvm_dbus_init (void)
03319 {
03320       DBusError error;
03321       
03322       if (dbus_connection != NULL)
03323             return TRUE;
03324       
03325       dbus_error_init (&error);
03326       if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
03327             dbg ("could not get system bus: %s\n", error.message);
03328             dbus_error_free (&error);
03329             return FALSE;
03330       }
03331       
03332       dbus_connection_setup_with_g_main (dbus_connection, NULL);
03333       dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
03334       
03335       dbus_connection_add_filter (dbus_connection, gvm_dbus_filter_function, NULL, NULL);
03336       
03337       return TRUE;
03338 }
03339 
03340 
03341 static void
03342 gvm_hal_claim_branch (const char *udi)
03343 {
03344       const char *claimed_by = "gnome-volume-manager";
03345       DBusMessage *dmesg, *reply;
03346       DBusError error;
03347       
03348       if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.Hal",
03349                                         "/org/freedesktop/Hal/Manager",
03350                                         "org.freedesktop.Hal.Manager",
03351                                         "ClaimBranch"))) {
03352             return;
03353       }
03354       
03355       if (!dbus_message_append_args (dmesg, DBUS_TYPE_STRING, &udi, DBUS_TYPE_STRING, &claimed_by,
03356                                DBUS_TYPE_INVALID)) {
03357             dbus_message_unref (dmesg);
03358             return;
03359       }
03360       
03361       dbus_error_init (&error);
03362       if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
03363             dbus_message_unref (dmesg);
03364             dbus_error_free (&error);
03365             return;
03366       }
03367       
03368       dbus_message_unref (dmesg);
03369       dbus_message_unref (reply);
03370 }
03371 
03372 
03373 /** Internal HAL initialization function
03374  *
03375  * @return              The LibHalContext of the HAL connection or
03376  *                      NULL on error.
03377  */
03378 static LibHalContext *
03379 gvm_hal_init (void)
03380 {
03381       LibHalContext *ctx;
03382       DBusError error;
03383       char **devices;
03384       int nr;
03385       
03386       if (!gvm_dbus_init ())
03387             return NULL;
03388       
03389       if (!(ctx = libhal_ctx_new ())) {
03390             warn ("failed to create a HAL context!");
03391             return NULL;
03392       }
03393       
03394       libhal_ctx_set_dbus_connection (ctx, dbus_connection);
03395       
03396       libhal_ctx_set_device_added (ctx, hal_device_added);
03397       libhal_ctx_set_device_removed (ctx, hal_device_removed);
03398       libhal_ctx_set_device_new_capability (ctx, hal_device_new_capability);
03399       libhal_ctx_set_device_lost_capability (ctx, hal_device_lost_capability);
03400       libhal_ctx_set_device_property_modified (ctx, hal_property_modified);
03401 #ifdef ENABLE_AUTOMOUNT
03402       libhal_ctx_set_device_condition (ctx, hal_device_condition);
03403 #endif
03404       
03405       dbus_error_init (&error);
03406       if (!libhal_device_property_watch_all (ctx, &error)) {
03407             warn ("failed to watch all HAL properties: %s", error.message ? error.message : "unknown");
03408             dbus_error_free (&error);
03409             libhal_ctx_free (ctx);
03410             return NULL;
03411       }
03412       
03413       if (!libhal_ctx_init (ctx, &error)) {
03414             warn ("libhal_ctx_init failed: %s", error.message ? error.message : "unknown");
03415             dbus_error_free (&error);
03416             libhal_ctx_free (ctx);
03417             return NULL;
03418       }
03419       
03420       /*
03421        * Do something to ping the HAL daemon - the above functions will
03422        * succeed even if hald is not running, so long as DBUS is.  But we
03423        * want to exit silently if hald is not running, to behave on
03424        * pre-2.6 systems.
03425        */
03426       if (!(devices = libhal_get_all_devices (ctx, &nr, &error))) {
03427             warn ("seems that HAL is not running: %s", error.message ? error.message : "unknown");
03428             dbus_error_free (&error);
03429             
03430             libhal_ctx_shutdown (ctx, NULL);
03431             libhal_ctx_free (ctx);
03432             return NULL;
03433       }
03434       
03435       libhal_free_string_array (devices);
03436       
03437       gvm_hal_claim_branch ("/org/freedesktop/Hal/devices/local");
03438 
03439       return ctx;
03440 }
03441 
03442 
03443 #ifdef ENABLE_AUTOMOUNT
03444 /** Attempt to mount all volumes; should be called on startup.
03445  *
03446  *  @param  ctx                 LibHal context
03447  */
03448 static void
03449 mount_all (LibHalContext *ctx)
03450 {
03451       char *prop, *dev, *udi, *drive;
03452       int num_volumes, mount;
03453       char **volumes;
03454       DBusError error;
03455       int i;
03456       
03457       if (!config.automount_media)
03458             return;
03459       
03460       dbus_error_init (&error);
03461       volumes = libhal_find_device_by_capability (ctx, "volume", &num_volumes, &error);
03462       if (dbus_error_is_set (&error)) {
03463             warn ("mount_all: could not find volume devices: %s", error.message);
03464             dbus_error_free (&error);
03465             return;
03466       }
03467       
03468       for (i = 0; i < num_volumes; i++) {
03469             udi = volumes[i];
03470 
03471             if (gvm_udi_is_subfs_mount (udi)) {
03472 #ifdef ENABLE_NOTIFY
03473                   statfs_mount_info_add (udi);
03474 #endif
03475                   
03476                   /* monitor them for unmounting, just in case */
03477                   if ((dev = libhal_device_get_property_string (ctx, udi, "block.storage_device", NULL)))
03478                         g_hash_table_insert (device_table, g_strdup (udi), dev);
03479                   
03480                   continue;
03481             }
03482             
03483             /* don't attempt to mount already mounted volumes */
03484             if (!libhal_device_property_exists (ctx, udi, "volume.is_mounted", NULL)
03485                 || libhal_device_get_property_bool (ctx, udi, "volume.is_mounted", NULL)) {
03486 #ifdef ENABLE_NOTIFY
03487                   statfs_mount_info_add (udi);
03488 #endif
03489                   continue;
03490             }
03491             
03492             /* only mount if the block device has a sensible filesystem */
03493             if (!libhal_device_property_exists (ctx, udi, "volume.fsusage", NULL))
03494                   continue;
03495             prop = libhal_device_get_property_string (ctx, udi, "volume.fsusage", NULL);
03496             if (!prop || ((strcmp (prop, "filesystem") != 0) && (strcmp (prop, "crypto") != 0))) {
03497                   libhal_free_string (prop);
03498                   continue;
03499             }
03500             libhal_free_string (prop);
03501             
03502             /* check our mounting policy */
03503             if (!(drive = libhal_device_get_property_string (ctx, udi, "info.parent", NULL)))
03504                   continue;
03505             
03506             if (libhal_device_property_exists (ctx, drive, "storage.hotpluggable", NULL)
03507                 && libhal_device_get_property_bool (ctx, drive, "storage.hotpluggable", NULL))
03508                   mount = config.automount_drives;
03509             else if (libhal_device_property_exists (ctx, drive, "storage.removable", NULL)
03510                    && libhal_device_get_property_bool (ctx, drive, "storage.removable", NULL))
03511                   mount = config.automount_media;
03512             else
03513                   mount = !libhal_device_get_property_bool (ctx, udi, "volume.ignore", NULL) &&
03514                             libhal_device_get_property_bool (ctx, drive, "storage.automount_enabled_hint", NULL);
03515 
03516             libhal_free_string (drive);
03517             
03518             if (!mount)
03519                   continue;
03520             
03521             /* mount the device */
03522             if ((dev = libhal_device_get_property_string (ctx, udi, "block.device", &error))) {
03523                   dbg ("mount_all: mounting %s\n", dev);
03524                   /* don't make the mount program put up error dialogs */
03525                   gvm_device_mount (udi, FALSE);
03526                   libhal_free_string (dev);
03527             } else {
03528                   warn ("mount_all: no device for udi=%s: %s", udi, error.message);
03529                   if (dbus_error_is_set (&error))
03530                         dbus_error_free (&error);
03531             }
03532       }
03533       
03534       libhal_free_string_array (volumes);
03535 }
03536 
03537 /** Unmount all volumes that were mounted during the lifetime of this
03538  *  g-v-m instance
03539  *
03540  *  @param  ctx                 LibHal context
03541  */
03542 static void
03543 unmount_all (void)
03544 {
03545       GSList *l;
03546       
03547       dbg ("unmounting all volumes that we mounted in our lifetime\n");
03548       
03549       for (l = mounted_volumes; l != NULL; l = l->next) {
03550             const char *udi = l->data;
03551             
03552             dbg ("unmount_all: unmounting %s...\n", udi);
03553             gvm_device_unmount (udi);
03554       }
03555 }
03556 #endif /* ENABLE_AUTOMOUNT */
03557 
03558 
03559 static int sigterm_unix_signal_pipe_fds[2];
03560 static GIOChannel *sigterm_iochn;
03561 
03562 static void 
03563 handle_sigterm (int value GNUC_UNUSED)
03564 {
03565       static char marker[1] = {'S'};
03566 
03567       /* write a 'S' character to the other end to tell about
03568        * the signal. Note that 'the other end' is a GIOChannel thingy
03569        * that is only called from the mainloop - thus this is how we
03570        * defer this since UNIX signal handlers are evil
03571        *
03572        * Oh, and write(2) is indeed reentrant */
03573       write (sigterm_unix_signal_pipe_fds[1], marker, 1);
03574 }
03575 
03576 static gboolean
03577 sigterm_iochn_data (GIOChannel *source, 
03578                 GIOCondition condition GNUC_UNUSED, 
03579                 gpointer user_data GNUC_UNUSED)
03580 {
03581       GError *err = NULL;
03582       gchar data[1];
03583       gsize bytes_read;
03584       
03585       /* Empty the pipe */
03586       if (G_IO_STATUS_NORMAL != g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) {
03587             warn ("Error emptying callout notify pipe: %s", err->message);
03588             g_error_free (err);
03589             goto out;
03590       }
03591       
03592       dbg ("Received SIGTERM, initiating shutdown\n");
03593       
03594 #ifdef ENABLE_AUTOMOUNT
03595       unmount_all ();
03596 #endif
03597       
03598       gtk_main_quit ();
03599       
03600  out:
03601       return TRUE;
03602 }
03603 
03604 
03605 #ifdef ENABLE_NOTIFY
03606 static void
03607 statfs_mount_info_add (const char *udi)
03608 {
03609       statfs_mount_info *info;
03610       
03611       if (g_hash_table_lookup (statfs_mounts, udi))
03612             return;
03613       
03614       if (libhal_device_get_property_bool (hal_ctx, udi, "volume.is_mounted_read_only", NULL))
03615             return;
03616       
03617       /* FIXME: this check can probably be dropped */
03618       if (gvm_udi_is_cdrom (udi))
03619             return;
03620       
03621       info = g_new0 (statfs_mount_info, 1);
03622       info->udi = g_strdup (udi);
03623       info->notified = FALSE;
03624       info->last_notified = 0.0;
03625       
03626       g_hash_table_insert (statfs_mounts, info->udi, info);
03627       
03628       gvm_statfs_check_space (info->udi, info, NULL);
03629 }
03630 
03631 static void
03632 statfs_mount_info_remove (const char *udi)
03633 {
03634       statfs_mount_info *info;
03635       
03636       if ((info = g_hash_table_lookup (statfs_mounts, udi))) {
03637             g_hash_table_remove (statfs_mounts, udi);
03638             statfs_mount_info_free (info);
03639       }
03640 }
03641 
03642 static void
03643 statfs_mount_info_free (statfs_mount_info *info)
03644 {
03645       g_free (info->udi);
03646       g_free (info);
03647 }
03648 
03649 static gboolean
03650 gvm_statfs_check_space (const char *udi, statfs_mount_info *info, gpointer user_data GNUC_UNUSED)
03651 {
03652       char *mount_point = NULL;
03653       NotifyNotification *n;
03654       struct statvfs buf;
03655       
03656       if (!(mount_point = libhal_device_get_property_string (hal_ctx, udi, "volume.mount_point", NULL)))
03657             return TRUE;
03658       
03659       if (statvfs (mount_point, &buf) != -1) {
03660             unsigned long twogb_blocks;
03661             double free_space;
03662             
03663             free_space = (double) buf.f_bavail / (double) buf.f_blocks;
03664             twogb_blocks = (2 * 1024 * 1024) / buf.f_bsize;
03665             
03666             /*
03667                If we continue using up more and more space or if we pass
03668                the threshold value while running, we want to notify again
03669             */
03670             if ((info->last_notified - free_space) > config.percent_used
03671                 || (free_space < config.percent_threshold && info->last_notified > config.percent_threshold))
03672                   info->notified = FALSE;
03673 
03674             /*
03675                If we didn't notify before (or need to again as above),
03676                and the %free is less than the threshold, and the amount of
03677                free space is less than 2GB, then notify the user
03678             */
03679             if (!info->notified && (free_space < config.percent_threshold && buf.f_bavail < twogb_blocks)) {
03680                   char *disk, *label, *msg, *icon;
03681                   int in_use;
03682                   
03683                   label = libhal_device_get_property_string (hal_ctx, udi, "volume.label", NULL);
03684                   if (!label || label[0] == '\0' || !strcmp (label, mount_point))
03685                         disk = g_strdup (mount_point);
03686                   else
03687                         disk = g_strdup_printf ("%s (%s)", label, mount_point);
03688                   libhal_free_string (label);
03689                   
03690                   in_use = 100 - (free_space * 100);
03691                   
03692                   if (!strcmp (disk, "/"))
03693                         msg = g_strdup_printf (_("%d%% of the disk space on the root partition is in use"), in_use);
03694                   else
03695                         msg = g_strdup_printf (_("%d%% of the disk space on `%s' is in use"), in_use, disk);
03696                   g_free (disk);
03697                   
03698                   icon = libhal_device_get_property_string (hal_ctx, udi, "info.icon_name", NULL);
03699                   if (icon != NULL)
03700                         n = notify_notification_new (_("Low Disk Space"), msg, icon, NULL);
03701                   else
03702                         n = notify_notification_new (_("Low Disk Space"), msg, "drive-harddisk", NULL);
03703                   
03704                   notify_notification_set_urgency (n, NOTIFY_URGENCY_CRITICAL);
03705                   notify_notification_show (n, NULL);
03706                   g_object_unref (n);
03707                   g_free (msg);
03708                   
03709                   info->notified = TRUE;
03710                   info->last_notified = free_space;
03711             }
03712       }
03713       
03714       libhal_free_string (mount_point);
03715       
03716       return TRUE;
03717 }
03718 
03719 static gboolean
03720 gvm_statfs_timeout (gpointer user_data GNUC_UNUSED)
03721 {
03722       g_hash_table_foreach (statfs_mounts, (GHFunc) gvm_statfs_check_space, NULL);
03723       
03724       return TRUE;
03725 }
03726 #endif /* ENABLE_NOTIFY */
03727 
03728 static void
03729 gvm_die (GnomeClient *client GNUC_UNUSED,
03730        gpointer user_data GNUC_UNUSED)
03731 {
03732       dbg ("Received 'die', initiating shutdown\n");
03733       
03734 #ifdef ENABLE_AUTOMOUNT
03735       unmount_all ();
03736 #endif
03737       
03738       gtk_main_quit ();
03739 }
03740 
03741 
03742 #ifdef __linux__
03743 enum {
03744       LOCAL_USER_CHECKED = (1 << 0),
03745       LOCAL_USER_FOUND   = (1 << 1)
03746 };
03747 
03748 /* checks that the user is logged-in at a local X session (which does not necessarily infer an *active* session) */
03749 static gboolean
03750 gvm_user_is_local_fallback (void)
03751 {
03752       static guint local = 0;
03753       struct dirent *dent;
03754       struct utmp *utmp;
03755       const char *user;
03756       char *vtend;
03757       size_t n;
03758       DIR *dir;
03759       int vt;
03760       
03761       if (local & LOCAL_USER_CHECKED)
03762             return (local & LOCAL_USER_FOUND);
03763       
03764       user = g_get_user_name ();
03765       n = strlen (user);
03766       
03767       if (!(dir = opendir (GVM_CONSOLE_AUTH_DIR)))
03768             goto fallback;
03769       
03770       /* this works for pam_console ($path/user) and pam_foreground ($path/user:vt) - see bug #336932 */
03771       while ((dent = readdir (dir))) {
03772                 if (!strncmp (user, dent->d_name, n)
03773                 && (dent->d_name[n] == '\0'
03774                   || (dent->d_name[n] == ':'
03775                       && ((vt = strtol (dent->d_name + n + 1, &vtend, 10)) >= 0)
03776                       && *vtend == '\0'))) {
03777                   local = LOCAL_USER_FOUND;
03778                   break;
03779             }
03780       }
03781       
03782       closedir (dir);
03783       
03784  fallback:
03785       
03786       if (!(local & LOCAL_USER_FOUND)) {
03787             setutent ();
03788             
03789             while (!(local & LOCAL_USER_FOUND) && (utmp = getutent ())) {
03790                   if (utmp->ut_type != USER_PROCESS || strncmp (utmp->ut_user, user, n) != 0)
03791                         continue;
03792                   
03793                   /* only accept local X sessions or local tty's (user started X via `startx`) */
03794                   local = (utmp->ut_line[0] == ':' && utmp->ut_line[1] >= '0' && utmp->ut_line[1] <= '9')
03795                         || !strncmp (utmp->ut_line, "tty", 3) ? LOCAL_USER_FOUND : 0;
03796             }
03797             
03798             endutent ();
03799       }
03800       
03801       local |= LOCAL_USER_CHECKED;
03802       
03803       return (local & LOCAL_USER_FOUND);
03804 }
03805 #endif /* __linux__ */
03806 
03807 
03808 static GPtrArray *
03809 gvm_console_kit_get_seats (void)
03810 {
03811       DBusMessage *dmesg, *reply;
03812       DBusMessageIter iter, elem;
03813       GPtrArray *seats = NULL;
03814       DBusError error;
03815       char *path;
03816       
03817       if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
03818                                         "/org/freedesktop/ConsoleKit/Manager",
03819                                         "org.freedesktop.ConsoleKit.Manager",
03820                                         "GetSeats"))) {
03821             dbg ("failed to create ConsoleKit.Manager request\n");
03822             return NULL;
03823       }
03824       
03825       dbus_error_init (&error);
03826       if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
03827             dbg ("ConsoleKit.GetSeats request failed to reply\n");
03828             dbus_message_unref (dmesg);
03829             dbus_error_free (&error);
03830             return NULL;
03831       }
03832       
03833       dbus_message_unref (dmesg);
03834       
03835       dbus_message_iter_init (reply, &iter);
03836       if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY ||
03837           dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_OBJECT_PATH) {
03838             dbg ("Unrecognized response to ConsoleKit.GetSeats request\n");
03839             dbus_message_unref (reply);
03840             return NULL;
03841       }
03842       
03843       dbus_message_iter_recurse (&iter, &elem);
03844       
03845       seats = g_ptr_array_new ();
03846       
03847       do {
03848             if (dbus_message_iter_get_arg_type (&elem) == DBUS_TYPE_OBJECT_PATH) {
03849                   dbus_message_iter_get_basic (&elem, &path);
03850                   g_ptr_array_add (seats, g_strdup (path));
03851             }
03852       } while (dbus_message_iter_next (&elem));
03853       
03854       dbus_message_unref (reply);
03855       
03856       return seats;
03857 }
03858 
03859 static GPtrArray *
03860 gvm_console_kit_seat_get_sessions (const char *seat_id)
03861 {
03862       DBusMessage *dmesg, *reply;
03863       DBusMessageIter iter, elem;
03864       GPtrArray *sessions = NULL;
03865       DBusError error;
03866       char *path;
03867       
03868       if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", seat_id,
03869                                         "org.freedesktop.ConsoleKit.Seat",
03870                                         "GetSessions"))) {
03871             dbg ("failed to create ConsoleKit GetSessions request\n");
03872             return NULL;
03873       }
03874       
03875       dbus_error_init (&error);
03876       if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
03877             dbg ("ConsoleKit GetSessions request failed to reply\n");
03878             dbus_message_unref (dmesg);
03879             dbus_error_free (&error);
03880             return NULL;
03881       }
03882       
03883       dbus_message_unref (dmesg);
03884       
03885       dbus_message_iter_init (reply, &iter);
03886       if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY ||
03887           dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_OBJECT_PATH) {
03888             dbg ("Unrecognized response to ConsoleKit GetSessions request\n");
03889             dbus_message_unref (reply);
03890             return NULL;
03891       }
03892       
03893       dbus_message_iter_recurse (&iter, &elem);
03894       
03895       sessions = g_ptr_array_new ();
03896       
03897       do {
03898             if (dbus_message_iter_get_arg_type (&elem) == DBUS_TYPE_OBJECT_PATH) {
03899                   dbus_message_iter_get_basic (&elem, &path);
03900                   g_ptr_array_add (sessions, g_strdup (path));
03901             }
03902       } while (dbus_message_iter_next (&elem));
03903       
03904       dbus_message_unref (reply);
03905       
03906       return sessions;
03907 }
03908 
03909 static gboolean
03910 gvm_console_kit_session_get_uid (const char *session_id, uid_t *uid)
03911 {
03912       DBusMessage *dmesg, *reply;
03913       DBusMessageIter iter;
03914       DBusError error;
03915       
03916       if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", session_id,
03917                                         "org.freedesktop.ConsoleKit.Session",
03918                                         "GetUnixUser"))) {
03919             dbg ("failed to create ConsoleKit GetUnixUser request\n");
03920             return FALSE;
03921       }
03922       
03923       dbus_error_init (&error);
03924       if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
03925             dbg ("ConsoleKit GetUnixUser request failed to reply\n");
03926             dbus_message_unref (dmesg);
03927             dbus_error_free (&error);
03928             return FALSE;
03929       }
03930       
03931       dbus_message_unref (dmesg);
03932       
03933       dbus_message_iter_init (reply, &iter);
03934       if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INT32) {
03935             dbg ("Unrecognized response to ConsoleKit GetUnixUser request\n");
03936             dbus_message_unref (reply);
03937             return FALSE;
03938       }
03939       
03940       dbus_message_iter_get_basic (&iter, uid);
03941       
03942       dbus_message_unref (reply);
03943       
03944       return TRUE;
03945 }
03946 
03947 static gboolean
03948 gvm_console_kit_session_get_bool (const char *session_id, const char *method, gboolean *value)
03949 {
03950       DBusMessage *dmesg, *reply;
03951       DBusMessageIter iter;
03952       DBusError error;
03953       
03954       if (!(dmesg = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", session_id,
03955                                         "org.freedesktop.ConsoleKit.Session",
03956                                         method))) {
03957             dbg ("failed to create ConsoleKit %s request\n", method);
03958             return FALSE;
03959       }
03960       
03961       dbus_error_init (&error);
03962       if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, dmesg, -1, &error))) {
03963             dbg ("ConsoleKit %s request failed to reply\n", method);
03964             dbus_message_unref (dmesg);
03965             dbus_error_free (&error);
03966             return FALSE;
03967       }
03968       
03969       dbus_message_unref (dmesg);
03970       
03971       dbus_message_iter_init (reply, &iter);
03972       if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_BOOLEAN) {
03973             dbg ("Unrecognized response to ConsoleKit %s request\n", method);
03974             dbus_message_unref (reply);
03975             return FALSE;
03976       }
03977       
03978       dbus_message_iter_get_basic (&iter, value);
03979       
03980       dbus_message_unref (reply);
03981       
03982       return TRUE;
03983 }
03984 
03985 #define QUERY_BOOL(session, method, rv) gvm_console_kit_session_get_bool (session, method, rv)
03986 
03987 enum {
03988       USER_IS_LOCAL  = 1 << 0,
03989       USER_IS_ACTIVE = 1 << 1,
03990 };
03991 
03992 static int
03993 gvm_query_console_kit (guint32 query, guint32 *result)
03994 {
03995       GPtrArray *sessions, *seats = NULL;
03996       char *session_id, *seat_id;
03997       gboolean found = FALSE;
03998       gboolean rv;
03999       guint i, j;
04000       uid_t uid;
04001       
04002       *result = 0;
04003       
04004       if (!(seats = gvm_console_kit_get_seats ()))
04005             return -1;
04006       
04007       for (i = 0; i < seats->len && !found; i++) {
04008             seat_id = seats->pdata[i];
04009             
04010             sessions = gvm_console_kit_seat_get_sessions (seat_id);
04011             if (sessions != NULL) {
04012                   for (j = 0; j < sessions->len && !found; j++) {
04013                         session_id = sessions->pdata[j];
04014                         
04015                         if (gvm_console_kit_session_get_uid (session_id, &uid) && uid == getuid ()) {
04016                               if ((query & USER_IS_ACTIVE) && QUERY_BOOL (session_id, "IsActive", &rv) && rv)
04017                                     *result |= USER_IS_ACTIVE;
04018                               if ((query & USER_IS_LOCAL) && QUERY_BOOL (session_id, "IsLocal", &rv) && rv)
04019                                     *result |= USER_IS_LOCAL;
04020                               found = TRUE;
04021                         }
04022                         
04023                         g_free (session_id);
04024                   }
04025                   
04026                   for ( ; j < sessions->len; j++)
04027                         g_free (sessions->pdata[j]);
04028                   
04029                   g_ptr_array_free (sessions, TRUE);
04030             }
04031             
04032             g_free (seat_id);
04033       }
04034       
04035       for ( ; i < seats->len; i++)
04036             g_free (seats->pdata[i]);
04037       
04038       g_ptr_array_free (seats, TRUE);
04039       
04040       return 0;
04041 }
04042 
04043 static gboolean
04044 gvm_user_is_local (void)
04045 {
04046       guint32 result = 0;
04047       
04048       if (gvm_query_console_kit (USER_IS_LOCAL, &result) != -1)
04049             return (result & USER_IS_LOCAL);
04050       
04051       /* querying ConsoleKit failed, fall back to old behavior */
04052 #ifdef __linux__
04053       return gvm_user_is_local_fallback ();
04054 #else
04055       return FALSE;
04056 #endif
04057 }
04058 
04059 /* checks that the user is at the local active X session */
04060 static gboolean
04061 gvm_user_is_active (void)
04062 {
04063 #ifdef ENABLE_MULTIUSER
04064       guint32 result = 0;
04065       
04066       if (gvm_query_console_kit (USER_IS_ACTIVE, &result) != -1)
04067             return (result & USER_IS_ACTIVE);
04068       
04069       /* querying ConsoleKit failed, fall back to old behavior */
04070       
04071       return TRUE;
04072 #else
04073       /* for the non-multiuser configuration, we assume we're always on the active console */
04074       return TRUE;
04075 #endif
04076 }
04077 
04078 
04079 static gboolean print_version = FALSE;
04080 static const char *daemon_arg = NULL;
04081 static gboolean no_daemon = FALSE;
04082 static gboolean secret_mode = FALSE;
04083 
04084 static GOptionEntry options[] = {
04085       { "version", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, &print_version,
04086         N_("Print version and exit"), NULL },
04087       { "daemon", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_STRING, (char **) &daemon_arg,
04088         N_("Run as a daemon"), "<yes|no>" },
04089       { "no-daemon", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, &no_daemon,
04090         N_("Don't run as a daemon"), NULL },
04091       { "secret-mode", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, &secret_mode,
04092         N_("Run in secret mode"), NULL },
04093       { NULL, '\0', 0, 0, NULL, NULL, NULL }
04094 };
04095 
04096 
04097 int
04098 main (int argc, char **argv)
04099 {
04100       gboolean daemonize = TRUE;
04101       GnomeProgram *program;
04102       GnomeClient *client;
04103       GOptionContext *ctx;
04104       
04105       bindtextdomain (PACKAGE, GNOMELOCALEDIR);
04106       bind_textdomain_codeset (PACKAGE, "UTF-8");
04107       textdomain (PACKAGE);
04108       
04109       ctx = g_option_context_new (PACKAGE);
04110       g_option_context_add_main_entries (ctx, options, NULL);
04111       
04112       program = gnome_program_init (PACKAGE, VERSION, LIBGNOMEUI_MODULE, argc, argv,
04113                               GNOME_PARAM_GOPTION_CONTEXT, ctx, GNOME_PARAM_NONE);
04114       
04115       if (print_version) {
04116             fprintf (stdout, "%s version %s\n", PACKAGE, VERSION);
04117             exit (0);
04118       }
04119       
04120       if (daemon_arg != NULL) {
04121             if (!strcmp (daemon_arg, "yes") || !strcmp (daemon_arg, "true"))
04122                   daemonize = TRUE;
04123             else if (!strcmp (daemon_arg, "no") || !strcmp (daemon_arg, "false"))
04124                   daemonize = FALSE;
04125             else
04126                   fprintf (stdout, _("Unrecognized --daemon argument: %s\n"), daemon_arg);
04127       }
04128       
04129       if (no_daemon)
04130             daemonize = FALSE;
04131       
04132       if (secret_mode)
04133             fprintf (stdout, "Run silent, run deep.\n");
04134       
04135       if (daemonize && daemon (0, 0) < 0) {
04136             warn ("daemonizing failed: %s", g_strerror (errno));
04137             return 1;
04138       }
04139       
04140       client = gnome_master_client ();
04141       g_signal_connect (client, "die", G_CALLBACK (gvm_die), NULL);
04142       
04143       if (!(hal_ctx = gvm_hal_init ()))
04144             return 1;
04145       
04146       if (gvm_get_clipboard () && gvm_user_is_local ()) {
04147             gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
04148       } else {
04149             gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
04150             if (gvm_user_is_local ())
04151                   warn ("already running");
04152             
04153             return 1;
04154       }
04155       
04156       gvm_init_config ();
04157       
04158       /* SIGTERM handling via pipes  */
04159       if (pipe (sigterm_unix_signal_pipe_fds) != 0) {
04160             warn ("Could not setup pipe, errno=%d", errno);
04161             return 1;
04162       }
04163       
04164       sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]);
04165       if (sigterm_iochn == NULL) {
04166             warn ("Could not create GIOChannel");
04167             return 1;
04168       }
04169       
04170       g_io_add_watch (sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL);
04171       signal (SIGTERM, handle_sigterm);
04172       
04173 #ifdef ENABLE_AUTOMOUNT
04174       device_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
04175                                     (GDestroyNotify) libhal_free_string);
04176       mount_table = g_hash_table_new (g_str_hash, g_str_equal);
04177 #endif
04178       
04179       dialogs = g_hash_table_new (g_str_hash, g_str_equal);
04180       
04181 #ifdef ENABLE_NOTIFY
04182       /* Initialize the notification connection */
04183       notify_init ("GNOME Volume Manager");
04184       
04185       /* Initialize the statfs_mounts hash table */
04186       statfs_mounts = g_hash_table_new (g_str_hash, g_str_equal);
04187 #endif
04188       
04189 #ifdef ENABLE_AUTOMOUNT
04190       mount_all (hal_ctx);
04191 #endif
04192       
04193 #ifdef ENABLE_NOTIFY
04194       /* Add a timeout for our statfs checking */
04195       statfs_id = g_timeout_add (15000, (GSourceFunc) gvm_statfs_timeout, NULL);
04196 #endif
04197       
04198       gtk_main ();
04199       
04200 #ifdef ENABLE_NOTIFY
04201       g_hash_table_destroy (statfs_mounts);
04202       g_source_remove (statfs_id);
04203       statfs_id = 0;
04204 #endif
04205       
04206 #ifdef ENABLE_AUTOMOUNT
04207       g_hash_table_destroy (device_table);
04208       g_hash_table_destroy (mount_table);
04209 #endif
04210       
04211       g_object_unref (program);
04212       
04213       return 0;
04214 }

Generated by  Doxygen 1.5.1