<commit_before>#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <Ecore.h>
#include <E_Hal.h>

#if EWL_GUI
#include <Ewl.h>
#include <Efreet.h>
#endif

/* test app */

static E_DBus_Connection *conn;
#if EWL_GUI
static int mount_id = 0;
#endif

#define DEVICE_TYPE_STORAGE 1
#define DEVICE_TYPE_VOLUME  2
typedef struct Device Device;
struct Device {
  int type;
  char *udi;
};

typedef struct Storage Storage;
struct Storage {
  int type;
  char *udi;
  char *bus;
  char *drive_type;

  char *model;
  char *vendor;
  char *serial;

  char removable;
  char media_available;
  unsigned long media_size;

  char requires_eject;
  char hotpluggable;
  char media_check_enabled;

  struct {
    char *drive;
    char *volume;
  } icon;

  Eina_List *volumes;
};


typedef struct Volume Volume;
struct Volume {
  int type;
  char *udi;
  char *uuid;
  char *label;
  char *fstype;

  char partition;
  char *partition_label;
  char mounted;
  char *mount_point;

  Storage *storage;
};

void volume_free(Volume *volume);

static Eina_List *storage_devices;
static Eina_List *volumes;

void
devices_dirty(void)
{
#if EWL_GUI
  Ewl_Widget *mvc;

  mvc = ewl_widget_name_find("device_mvc");
  ewl_mvc_dirty_set(EWL_MVC(mvc), 1);
#endif
}


Storage *
storage_new(void)
{
  Storage *s;
  s = calloc(1, sizeof(Storage));
  s->type = DEVICE_TYPE_STORAGE;
  s->volumes = NULL;
  return s;
}

void
storage_free(Storage *storage)
{
  Volume *v;
  Eina_List *l;
  EINA_LOG_INFO("storage_free: %s", storage->udi);

  /* disconnect storage from volume */
  EINA_LIST_FOREACH(storage->volumes, l, v)
    v->storage = NULL;
  while (storage->volumes)
    {
       v = eina_list_data_get(storage->volumes);
       volume_free(v);
       storage->volumes = eina_list_remove_list(storage->volumes, storage->volumes);
    }
    
  if (storage->udi) free(storage->udi);
  if (storage->bus) free(storage->bus);
  if (storage->drive_type) free(storage->drive_type);

  if (storage->model) free(storage->model);
  if (storage->vendor) free(storage->vendor);
  if (storage->serial) free(storage->serial);
  if (storage->icon.drive) free(storage->icon.drive);
  if (storage->icon.volume) free(storage->icon.volume);

  free(storage);
}

static int
storage_find_helper(Storage *s, const char *udi)
{
  if (!s->udi) return -1;
  return strcmp(s->udi, udi);
}

Storage *
storage_find(const char *udi)
{
  Storage *s = NULL;
  if (!udi) return NULL;
  s = eina_list_search_unsorted(storage_devices, (Eina_Compare_Cb)storage_find_helper, udi);
  return s;
}

void
storage_remove(const char *udi)
{
  Storage *sto;

  if ((sto = storage_find(udi)))
  {
    storage_devices = eina_list_remove(storage_devices, sto);
    storage_free(sto);
    devices_dirty();
  }
}

static void
cb_storage_properties(void *data, void *reply_data, DBusError *error)
{
  Storage *s = data;
  E_Hal_Properties *ret = reply_data;
  int err = 0;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  s->bus = e_hal_property_string_get(ret, "storage.bus", &err);
  if (err) goto error;
  s->drive_type = e_hal_property_string_get(ret, "storage.drive_type", &err);
  if (err) goto error;
  s->model = e_hal_property_string_get(ret, "storage.model", &err);
  if (err) goto error;
  s->vendor = e_hal_property_string_get(ret, "storage.vendor", &err);
  if (err) goto error;
  s->serial = e_hal_property_string_get(ret, "storage.serial", &err);

  s->removable = e_hal_property_bool_get(ret, "storage.removable", &err);
  if (err) goto error;

  if (s->removable)
  {
    s->media_available = e_hal_property_bool_get(ret, "storage.removable.media_available", &err);
    s->media_size = e_hal_property_uint64_get(ret, "storage.removable.media_size", &err);
  }

  s->requires_eject = e_hal_property_bool_get(ret, "storage.requires_eject", &err);
  s->hotpluggable = e_hal_property_bool_get(ret, "storage.hotpluggable", &err);
  s->media_check_enabled = e_hal_property_bool_get(ret, "storage.media_check_enabled", &err);

  s->icon.drive = e_hal_property_string_get(ret, "storage.icon.drive", &err);
  s->icon.volume = e_hal_property_string_get(ret, "storage.icon.volume", &err);

  devices_dirty();
  /* now fetch the children (volumes?) */

  //printf("Got storage:\n  udi: %s\n  bus: %s\n  drive_type: %s\n  model: %s\n  vendor: %s\n  serial: %s\n  icon.drive: %s\n  icon.volume: %s\n\n", s->udi, s->bus, s->drive_type, s->model, s->vendor, s->serial, s->icon.drive, s->icon.volume);
  return;

error:
  storage_remove(s->udi);
}

Storage *
storage_append(const char *udi)
{
  Storage *s;
  if (!udi) return NULL;
  s = storage_new();
  s->udi = strdup(udi);
  storage_devices = eina_list_append(storage_devices, s);
  e_hal_device_get_all_properties(conn, s->udi, cb_storage_properties, s);
  devices_dirty();
  return s;
}

Volume *
volume_new(void)
{
  Volume *v;
  v = calloc(1, sizeof(Volume));
  v->type = DEVICE_TYPE_VOLUME;
  return v;
}

void
volume_free(Volume *volume)
{
  if (!volume) return;

  /* disconnect volume from storage */
  if (volume->storage)
  {
    volume->storage->volumes = eina_list_remove(volume->storage->volumes, volume);
    volume_free(volume);
  }

  if (volume->udi) free(volume->udi);
  if (volume->uuid) free(volume->uuid);
  if (volume->label) free(volume->label);
  if (volume->fstype) free(volume->fstype);
  if (volume->partition_label) free(volume->partition_label);
  if (volume->mount_point) free(volume->mount_point);

  free(volume);
}

static int
volume_find_helper(Volume *v, const char *udi)
{
  if (!v->udi) return -1;
  return strcmp(v->udi, udi);
}

Volume *
volume_find(const char *udi)
{
  if (!udi) return NULL;
  return eina_list_search_unsorted(volumes, (Eina_Compare_Cb)volume_find_helper, udi);
}

void
volume_remove(const char *udi)
{
  Volume *vol;

  if ((vol = volume_find(udi)))
  {
    volumes = eina_list_remove(volumes, vol);
    volume_free(vol);
    devices_dirty();
  }
}

static void
cb_volume_properties(void *data, void *reply_data, DBusError *error)
{
  Volume *v = data;
  Storage *s = NULL;
  E_Hal_Device_Get_All_Properties_Return *ret = reply_data;
  int err = 0;
  char *str = NULL;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  /* skip volumes with volume.ignore set */
  if (e_hal_property_bool_get(ret, "volume.ignore", &err) || err)
    goto error;

  /* skip volumes that aren't filesystems */
  str = e_hal_property_string_get(ret, "volume.fsusage", &err);
  if (err || !str) goto error;
  if (strcmp(str, "filesystem")) goto error;
  free(str);
  str = NULL;

  v->uuid = e_hal_property_string_get(ret, "volume.uuid", &err);
  if (err) goto error;

  v->label = e_hal_property_string_get(ret, "volume.label", &err);
  if (err) goto error;

  v->fstype = e_hal_property_string_get(ret, "volume.fstype", &err);
  if (err) goto error;

  v->mounted = e_hal_property_bool_get(ret, "volume.is_mounted", &err);
  if (err) goto error;

  v->partition = e_hal_property_bool_get(ret, "volume.is_partition", &err);
  if (err) goto error;

  v->mount_point = e_hal_property_string_get(ret, "volume.mount_point", &err);
  if (err) goto error;

  if (v->partition)
  {
    v->partition_label = e_hal_property_string_get(ret, "volume.partition.label", &err);
    if (err) goto error;
  }

  str = e_hal_property_string_get(ret, "info.parent", &err);
  if (!err && str)
  {
    s = storage_find(str);
    if (s)
    {
      v->storage = s;
      s->volumes = eina_list_append(s->volumes, v);
    }
    free(str);
    str = NULL;
  }
  
  //printf("Got volume\n  udi: %s\n  uuid: %s\n  fstype: %s\n  label: %s\n  partition: %d\n  partition_label: %s\n  mounted: %d\n  mount_point: %s\n\n", v->udi, v->uuid, v->fstype,  v->label, v->partition, v->partition ? v->partition_label : "(not a partition)", v->mounted, v->mount_point);
  //if (s) printf("  for storage: %s\n", s->udi);
  //else printf("  storage unknown\n");

  devices_dirty();
  return;

error:
  if (str) free(str);
  volume_remove(v->udi);
  return;
  
}

void
volume_setup(Volume *v)
{
  e_hal_device_get_all_properties(conn, v->udi, cb_volume_properties, v);
}

Volume *
volume_append(const char *udi)
{
  Volume *v;
  if (!udi) return NULL;
  EINA_LOG_INFO("ADDING %s", udi);
  v = volume_new();
  v->udi = strdup(udi);
  volumes = eina_list_append(volumes, v);
  volume_setup(v);

  //this will get called when volume_setup() returns, which is more important
  //devices_dirty();

  return v;
}

static void
cb_test_get_all_devices(void *user_data __UNUSED__, void *reply_data, DBusError *error)
{
  E_Hal_Manager_Get_All_Devices_Return *ret = reply_data;
  Eina_List *l;
  char *device;
  
  if (!ret || !ret->strings) return;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    return;
  }

  EINA_LIST_FOREACH(ret->strings, l, device)
  {
     EINA_LOG_INFO("device: %s", device);
  }
}

static void
cb_test_find_device_by_capability_storage(void *user_data __UNUSED__, void *reply_data, DBusError *error)
{
  E_Hal_Manager_Find_Device_By_Capability_Return *ret = reply_data;
  Eina_List *l;
  char *device;
  
  if (!ret || !ret->strings) return;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    return;
  }

  EINA_LIST_FOREACH(ret->strings, l, device)
    storage_append(device);
}

static void
cb_test_find_device_by_capability_volume(void *user_data __UNUSED__, void *reply_data, DBusError *error)
{
  E_Hal_Manager_Find_Device_By_Capability_Return *ret = reply_data;
  Eina_List *l;
  char *device;
  
  if (!ret || !ret->strings) return;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    return;
  }

  EINA_LIST_FOREACH(ret->strings, l, device)
    volume_append(device);
}

static void
cb_is_storage(void *user_data, void *reply_data, DBusError *error)
{
  char *udi = user_data;
  E_Hal_Device_Query_Capability_Return *ret = reply_data;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  if (ret && ret->boolean)
    storage_append(udi);

error:
  free(udi);
}

static void
cb_is_volume(void *user_data, void *reply_data, DBusError *error)
{
  char *udi = user_data;
  E_Hal_Device_Query_Capability_Return *ret = reply_data;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  if (ret && ret->boolean)
    volume_append(udi);

error:
  free(udi);
}

static void
cb_signal_device_added(void *data __UNUSED__, DBusMessage *msg)
{
  DBusError err;
  char *udi;
  int ret;

  dbus_error_init(&err);
  dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID);
  udi = strdup(udi);
  EINA_LOG_INFO("Ehal: Device added: %s", udi);
  ret = e_hal_device_query_capability(conn, udi, "storage", cb_is_storage, strdup(udi));
  e_hal_device_query_capability(conn, udi, "volume", cb_is_volume, strdup(udi));
}

void
cb_signal_device_removed(void *data __UNUSED__, DBusMessage *msg)
{
  DBusError err;
  char *udi;

  dbus_error_init(&err);

  dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID);
  EINA_LOG_INFO("Removed: %s", udi);
  storage_remove(udi);
  volume_remove(udi);
}

void
cb_signal_new_capability(void *data __UNUSED__, DBusMessage *msg)
{
  DBusError err;<commit_msg>Remove storage of return value<commit_after>#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <Ecore.h>
#include <E_Hal.h>

#if EWL_GUI
#include <Ewl.h>
#include <Efreet.h>
#endif

/* test app */

static E_DBus_Connection *conn;
#if EWL_GUI
static int mount_id = 0;
#endif

#define DEVICE_TYPE_STORAGE 1
#define DEVICE_TYPE_VOLUME  2
typedef struct Device Device;
struct Device {
  int type;
  char *udi;
};

typedef struct Storage Storage;
struct Storage {
  int type;
  char *udi;
  char *bus;
  char *drive_type;

  char *model;
  char *vendor;
  char *serial;

  char removable;
  char media_available;
  unsigned long media_size;

  char requires_eject;
  char hotpluggable;
  char media_check_enabled;

  struct {
    char *drive;
    char *volume;
  } icon;

  Eina_List *volumes;
};


typedef struct Volume Volume;
struct Volume {
  int type;
  char *udi;
  char *uuid;
  char *label;
  char *fstype;

  char partition;
  char *partition_label;
  char mounted;
  char *mount_point;

  Storage *storage;
};

void volume_free(Volume *volume);

static Eina_List *storage_devices;
static Eina_List *volumes;

void
devices_dirty(void)
{
#if EWL_GUI
  Ewl_Widget *mvc;

  mvc = ewl_widget_name_find("device_mvc");
  ewl_mvc_dirty_set(EWL_MVC(mvc), 1);
#endif
}


Storage *
storage_new(void)
{
  Storage *s;
  s = calloc(1, sizeof(Storage));
  s->type = DEVICE_TYPE_STORAGE;
  s->volumes = NULL;
  return s;
}

void
storage_free(Storage *storage)
{
  Volume *v;
  Eina_List *l;
  EINA_LOG_INFO("storage_free: %s", storage->udi);

  /* disconnect storage from volume */
  EINA_LIST_FOREACH(storage->volumes, l, v)
    v->storage = NULL;
  while (storage->volumes)
    {
       v = eina_list_data_get(storage->volumes);
       volume_free(v);
       storage->volumes = eina_list_remove_list(storage->volumes, storage->volumes);
    }
    
  if (storage->udi) free(storage->udi);
  if (storage->bus) free(storage->bus);
  if (storage->drive_type) free(storage->drive_type);

  if (storage->model) free(storage->model);
  if (storage->vendor) free(storage->vendor);
  if (storage->serial) free(storage->serial);
  if (storage->icon.drive) free(storage->icon.drive);
  if (storage->icon.volume) free(storage->icon.volume);

  free(storage);
}

static int
storage_find_helper(Storage *s, const char *udi)
{
  if (!s->udi) return -1;
  return strcmp(s->udi, udi);
}

Storage *
storage_find(const char *udi)
{
  Storage *s = NULL;
  if (!udi) return NULL;
  s = eina_list_search_unsorted(storage_devices, (Eina_Compare_Cb)storage_find_helper, udi);
  return s;
}

void
storage_remove(const char *udi)
{
  Storage *sto;

  if ((sto = storage_find(udi)))
  {
    storage_devices = eina_list_remove(storage_devices, sto);
    storage_free(sto);
    devices_dirty();
  }
}

static void
cb_storage_properties(void *data, void *reply_data, DBusError *error)
{
  Storage *s = data;
  E_Hal_Properties *ret = reply_data;
  int err = 0;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  s->bus = e_hal_property_string_get(ret, "storage.bus", &err);
  if (err) goto error;
  s->drive_type = e_hal_property_string_get(ret, "storage.drive_type", &err);
  if (err) goto error;
  s->model = e_hal_property_string_get(ret, "storage.model", &err);
  if (err) goto error;
  s->vendor = e_hal_property_string_get(ret, "storage.vendor", &err);
  if (err) goto error;
  s->serial = e_hal_property_string_get(ret, "storage.serial", &err);

  s->removable = e_hal_property_bool_get(ret, "storage.removable", &err);
  if (err) goto error;

  if (s->removable)
  {
    s->media_available = e_hal_property_bool_get(ret, "storage.removable.media_available", &err);
    s->media_size = e_hal_property_uint64_get(ret, "storage.removable.media_size", &err);
  }

  s->requires_eject = e_hal_property_bool_get(ret, "storage.requires_eject", &err);
  s->hotpluggable = e_hal_property_bool_get(ret, "storage.hotpluggable", &err);
  s->media_check_enabled = e_hal_property_bool_get(ret, "storage.media_check_enabled", &err);

  s->icon.drive = e_hal_property_string_get(ret, "storage.icon.drive", &err);
  s->icon.volume = e_hal_property_string_get(ret, "storage.icon.volume", &err);

  devices_dirty();
  /* now fetch the children (volumes?) */

  //printf("Got storage:\n  udi: %s\n  bus: %s\n  drive_type: %s\n  model: %s\n  vendor: %s\n  serial: %s\n  icon.drive: %s\n  icon.volume: %s\n\n", s->udi, s->bus, s->drive_type, s->model, s->vendor, s->serial, s->icon.drive, s->icon.volume);
  return;

error:
  storage_remove(s->udi);
}

Storage *
storage_append(const char *udi)
{
  Storage *s;
  if (!udi) return NULL;
  s = storage_new();
  s->udi = strdup(udi);
  storage_devices = eina_list_append(storage_devices, s);
  e_hal_device_get_all_properties(conn, s->udi, cb_storage_properties, s);
  devices_dirty();
  return s;
}

Volume *
volume_new(void)
{
  Volume *v;
  v = calloc(1, sizeof(Volume));
  v->type = DEVICE_TYPE_VOLUME;
  return v;
}

void
volume_free(Volume *volume)
{
  if (!volume) return;

  /* disconnect volume from storage */
  if (volume->storage)
  {
    volume->storage->volumes = eina_list_remove(volume->storage->volumes, volume);
    volume_free(volume);
  }

  if (volume->udi) free(volume->udi);
  if (volume->uuid) free(volume->uuid);
  if (volume->label) free(volume->label);
  if (volume->fstype) free(volume->fstype);
  if (volume->partition_label) free(volume->partition_label);
  if (volume->mount_point) free(volume->mount_point);

  free(volume);
}

static int
volume_find_helper(Volume *v, const char *udi)
{
  if (!v->udi) return -1;
  return strcmp(v->udi, udi);
}

Volume *
volume_find(const char *udi)
{
  if (!udi) return NULL;
  return eina_list_search_unsorted(volumes, (Eina_Compare_Cb)volume_find_helper, udi);
}

void
volume_remove(const char *udi)
{
  Volume *vol;

  if ((vol = volume_find(udi)))
  {
    volumes = eina_list_remove(volumes, vol);
    volume_free(vol);
    devices_dirty();
  }
}

static void
cb_volume_properties(void *data, void *reply_data, DBusError *error)
{
  Volume *v = data;
  Storage *s = NULL;
  E_Hal_Device_Get_All_Properties_Return *ret = reply_data;
  int err = 0;
  char *str = NULL;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  /* skip volumes with volume.ignore set */
  if (e_hal_property_bool_get(ret, "volume.ignore", &err) || err)
    goto error;

  /* skip volumes that aren't filesystems */
  str = e_hal_property_string_get(ret, "volume.fsusage", &err);
  if (err || !str) goto error;
  if (strcmp(str, "filesystem")) goto error;
  free(str);
  str = NULL;

  v->uuid = e_hal_property_string_get(ret, "volume.uuid", &err);
  if (err) goto error;

  v->label = e_hal_property_string_get(ret, "volume.label", &err);
  if (err) goto error;

  v->fstype = e_hal_property_string_get(ret, "volume.fstype", &err);
  if (err) goto error;

  v->mounted = e_hal_property_bool_get(ret, "volume.is_mounted", &err);
  if (err) goto error;

  v->partition = e_hal_property_bool_get(ret, "volume.is_partition", &err);
  if (err) goto error;

  v->mount_point = e_hal_property_string_get(ret, "volume.mount_point", &err);
  if (err) goto error;

  if (v->partition)
  {
    v->partition_label = e_hal_property_string_get(ret, "volume.partition.label", &err);
    if (err) goto error;
  }

  str = e_hal_property_string_get(ret, "info.parent", &err);
  if (!err && str)
  {
    s = storage_find(str);
    if (s)
    {
      v->storage = s;
      s->volumes = eina_list_append(s->volumes, v);
    }
    free(str);
    str = NULL;
  }
  
  //printf("Got volume\n  udi: %s\n  uuid: %s\n  fstype: %s\n  label: %s\n  partition: %d\n  partition_label: %s\n  mounted: %d\n  mount_point: %s\n\n", v->udi, v->uuid, v->fstype,  v->label, v->partition, v->partition ? v->partition_label : "(not a partition)", v->mounted, v->mount_point);
  //if (s) printf("  for storage: %s\n", s->udi);
  //else printf("  storage unknown\n");

  devices_dirty();
  return;

error:
  if (str) free(str);
  volume_remove(v->udi);
  return;
  
}

void
volume_setup(Volume *v)
{
  e_hal_device_get_all_properties(conn, v->udi, cb_volume_properties, v);
}

Volume *
volume_append(const char *udi)
{
  Volume *v;
  if (!udi) return NULL;
  EINA_LOG_INFO("ADDING %s", udi);
  v = volume_new();
  v->udi = strdup(udi);
  volumes = eina_list_append(volumes, v);
  volume_setup(v);

  //this will get called when volume_setup() returns, which is more important
  //devices_dirty();

  return v;
}

static void
cb_test_get_all_devices(void *user_data __UNUSED__, void *reply_data, DBusError *error)
{
  E_Hal_Manager_Get_All_Devices_Return *ret = reply_data;
  Eina_List *l;
  char *device;
  
  if (!ret || !ret->strings) return;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    return;
  }

  EINA_LIST_FOREACH(ret->strings, l, device)
  {
     EINA_LOG_INFO("device: %s", device);
  }
}

static void
cb_test_find_device_by_capability_storage(void *user_data __UNUSED__, void *reply_data, DBusError *error)
{
  E_Hal_Manager_Find_Device_By_Capability_Return *ret = reply_data;
  Eina_List *l;
  char *device;
  
  if (!ret || !ret->strings) return;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    return;
  }

  EINA_LIST_FOREACH(ret->strings, l, device)
    storage_append(device);
}

static void
cb_test_find_device_by_capability_volume(void *user_data __UNUSED__, void *reply_data, DBusError *error)
{
  E_Hal_Manager_Find_Device_By_Capability_Return *ret = reply_data;
  Eina_List *l;
  char *device;
  
  if (!ret || !ret->strings) return;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    return;
  }

  EINA_LIST_FOREACH(ret->strings, l, device)
    volume_append(device);
}

static void
cb_is_storage(void *user_data, void *reply_data, DBusError *error)
{
  char *udi = user_data;
  E_Hal_Device_Query_Capability_Return *ret = reply_data;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  if (ret && ret->boolean)
    storage_append(udi);

error:
  free(udi);
}

static void
cb_is_volume(void *user_data, void *reply_data, DBusError *error)
{
  char *udi = user_data;
  E_Hal_Device_Query_Capability_Return *ret = reply_data;

  if (dbus_error_is_set(error)) 
  {
    // XXX handle...
    dbus_error_free(error);
    goto error;
  }

  if (ret && ret->boolean)
    volume_append(udi);

error:
  free(udi);
}

static void
cb_signal_device_added(void *data __UNUSED__, DBusMessage *msg)
{
  DBusError err;
  char *udi;

  dbus_error_init(&err);
  dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID);
  udi = strdup(udi);
  EINA_LOG_INFO("Ehal: Device added: %s", udi);
  e_hal_device_query_capability(conn, udi, "storage", cb_is_storage, strdup(udi));
  e_hal_device_query_capability(conn, udi, "volume", cb_is_volume, strdup(udi));
}

void
cb_signal_device_removed(void *data __UNUSED__, DBusMessage *msg)
{
  DBusError err;
  char *udi;

  dbus_error_init(&err);

  dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID);
  EINA_LOG_INFO("Removed: %s", udi);
  storage_remove(udi);
  volume_remove(udi);
}

void
cb_signal_new_capability(void *data __UNUSED__, DBusMessage *msg)
{
  DBusError err;
{'fraction_non_alphanumeric': 0.14235562576794805, 'fraction_numerical': 0.00105318588730911, 'mean_word_length': 4.201780415430267, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 223, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4758344', 'n_tokens_mistral': 8932, 'n_tokens_neox': 8752, 'n_words': 1860}