static GArray*
inf_adopted_session_get_xml_user_props(InfSession* session,
                                       InfXmlConnection* conn,
                                       const xmlNodePtr xml)
{
  GArray* array;
  GParameter* parameter;
  InfAdoptedStateVector* vector;
  xmlChar* time;

  array = INF_SESSION_CLASS(parent_class)->get_xml_user_props(
    session,
    conn,
    xml
  );

  /* Vector time */
  time = inf_xml_util_get_attribute(xml, "time");
  if(time != NULL)
  {
    vector = inf_adopted_state_vector_from_string((const gchar*)time, NULL);
    xmlFree(time);

    /* TODO: Error reporting for get_xml_user_props */
    if(vector != NULL)
    {
      parameter = inf_session_get_user_property(array, "vector");
      g_value_init(&parameter->value, INF_ADOPTED_TYPE_STATE_VECTOR);
      g_value_take_boxed(&parameter->value, vector);
    }
  }

  /* log-begin is not in the  spec */
#if 0
  /* Initial request log, only if ID is also given */
  id_param = inf_session_lookup_user_property(
    (const GParameter*)array->data,
    array->len,
    "id"
  );

  if(id_param != NULL &&
     inf_xml_util_get_attribute_uint(xml, "log-begin", &log_begin, NULL))
  {
    log = inf_adopted_request_log_new(
      g_value_get_uint(&id_param->value),
      log_begin
    );

    parameter = inf_session_get_user_property(array, "request-log");
    g_value_init(&parameter->value, INF_ADOPTED_TYPE_REQUEST_LOG);
    g_value_take_object(&parameter->value, log);
  }
#endif

  return array;
}
/**
 * infd_acl_account_info_from_xml:
 * @xml: The XML node from which to read the account information.
 * @error: Location to store error information, if any.
 *
 * Reads information for one account from a serialized XML node. The account
 * info can be written to XML with the infd_acl_account_info_to_xml()
 * function. If the function fails it returns %NULL and @error is set.
 *
 * Returns: A #InfdAclAccountInfo, or %NULL. Free with
 * infd_acl_account_info_free() when no longer needed.
 */
InfdAclAccountInfo*
infd_acl_account_info_from_xml(xmlNodePtr xml,
                               GError** error)
{
  InfdAclAccountInfo* info;
  InfAclAccount* account;

  GError* local_error;
  gboolean has_first_seen;
  gdouble first_seen;
  gboolean has_last_seen;
  gdouble last_seen;

  xmlChar* password_salt;
  xmlChar* password_hash;
  gnutls_datum_t datum;
  size_t hash_len;
  int res;
  gchar* binary_salt;
  gchar* binary_hash;

  xmlNodePtr child;
  GPtrArray* certificate_array;
  guint i;

  g_return_val_if_fail(xml != NULL, NULL);
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);

  local_error = NULL;

  has_first_seen = inf_xml_util_get_attribute_double(
    xml,
    "first-seen",
    &first_seen,
    &local_error
  );

  if(local_error != NULL)
    return NULL;

  has_last_seen = inf_xml_util_get_attribute_double(
    xml,
    "last-seen",
    &last_seen,
    &local_error
  );

  if(local_error != NULL)
    return NULL;

  account = inf_acl_account_from_xml(xml, error);
  if(account == NULL) return NULL;

  password_salt = inf_xml_util_get_attribute(xml, "password-salt");
  password_hash = inf_xml_util_get_attribute(xml, "password-hash");

  if( (password_salt != NULL && password_hash == NULL) ||
      (password_salt == NULL && password_hash != NULL))
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_ATTRIBUTE,
      "%s",
      _("If one of \"password-hash\" or \"password-salt\" is provided, the "
        "other must be provided as well.")
    );

    if(password_salt != NULL) xmlFree(password_salt);
    if(password_hash != NULL) xmlFree(password_hash);

    inf_acl_account_free(account);
    return NULL;
  }

  if(password_salt != NULL && password_hash != NULL)
  {
    datum.data = password_salt;
    datum.size = strlen(password_salt);

    hash_len = 32;
    binary_salt = g_malloc(hash_len);
    res = gnutls_hex_decode(&datum, binary_salt, &hash_len);
    xmlFree(password_salt);

    if(hash_len != 32)
    {
      g_set_error(
        error,
        inf_request_error_quark(),
        INF_REQUEST_ERROR_INVALID_ATTRIBUTE,
        "%s",
        _("The length of the password salt is incorrect, it should "
          "be 32 bytes")
      );

      xmlFree(password_hash);
      g_free(binary_salt);
      return NULL;
    }
    else if(res != GNUTLS_E_SUCCESS)
    {
      inf_gnutls_set_error(error, res);
      xmlFree(password_hash);
      g_free(binary_salt);
      return NULL;
    }

    datum.data = password_hash;
    datum.size = strlen(password_hash);

    hash_len = gnutls_hash_get_len(GNUTLS_DIG_SHA256);
    binary_hash = g_malloc(hash_len);
    res = gnutls_hex_decode(&datum, binary_hash, &hash_len);
    xmlFree(password_hash);
  
    if(hash_len != gnutls_hash_get_len(GNUTLS_DIG_SHA256))
    {
      g_set_error(
        error,
        inf_request_error_quark(),
        INF_REQUEST_ERROR_INVALID_ATTRIBUTE,
        _("The length of the password hash is incorrect, it should be "
          "%u bytes"),
        (unsigned int)gnutls_hash_get_len(GNUTLS_DIG_SHA256)
      );

      g_free(binary_salt);
      g_free(binary_hash);
      return NULL;
    }
    else if(res != GNUTLS_E_SUCCESS)
    {
      inf_gnutls_set_error(error, res);
      g_free(binary_salt);
      g_free(binary_hash);
      return NULL;
    }
  }
  else
  {
    binary_salt = NULL;
    binary_hash = NULL;\

    if(password_salt != NULL) xmlFree(password_salt);
    if(password_hash != NULL) xmlFree(password_hash);
  }

  certificate_array = g_ptr_array_new();
  for(child = xml->children; child != NULL; child = child->next)
  {
    if(child->type != XML_ELEMENT_NODE) continue;
    if(strcmp((const char*)child->name, "certificate") == 0)
      g_ptr_array_add(certificate_array, xmlNodeGetContent(child));
  }

  info = infd_acl_account_info_new(account->id, account->name, FALSE);
  inf_acl_account_free(account);

  info->certificates = g_malloc(sizeof(gchar*) * certificate_array->len);
  for(i = 0; i < certificate_array->len; ++i)
  {
    info->certificates[i] = g_strdup(certificate_array->pdata[i]);
    xmlFree(certificate_array->pdata[i]);
  }
  
  info->n_certificates = certificate_array->len;
  g_ptr_array_free(certificate_array, TRUE);

  info->password_salt = binary_salt;
  info->password_hash = binary_hash;
  if(has_first_seen == TRUE)
    info->first_seen = first_seen * 1e6;
  else
    info->first_seen = 0;

  if(has_last_seen == TRUE)
    info->last_seen = last_seen * 1e6;
  else
    info->last_seen = 0;

  return info;
}