Beispiel #1
0
static gboolean
inf_xml_util_string_to_int(const gchar* attribute,
                           const xmlChar* value,
                           gint* output,
                           GError** error)
{
  long converted;
  char* endptr;

  errno = 0;
  converted = strtol((const char*)value, &endptr, 0);

  if(*value == '\0' || *endptr != '\0')
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Attribute '%s' does not contain a valid number"),
      attribute
    );

    return FALSE;
  }
  else if( (errno == ERANGE && converted == LONG_MAX) ||
          converted > (long)G_MAXINT)
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Attribute '%s' causes overflow (%s)"),
      attribute,
      (const gchar*)value
    );

    return FALSE;
  }
  else if( (errno == ERANGE && converted == LONG_MIN) ||
            converted < (long)G_MININT)
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Attribute '%s' causes underflow (%s)"),
      attribute,
      (const gchar*)value
    );

    return FALSE;
  }
  else
  {
    *output = (gint)converted;
    return TRUE;
  }
}
Beispiel #2
0
static gboolean
inf_xml_util_string_to_double(const gchar* attribute,
                              const xmlChar* value,
                              gdouble* output,
                              GError** error)
{
  double converted;
  char* endptr;

  errno = 0;
  converted = g_ascii_strtod((const char*)value, &endptr);

  if(*value == '\0' || *endptr != '\0')
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Attribute '%s' does not contain a valid number"),
      attribute
    );

    return FALSE;
  }
  else if(errno == ERANGE &&
          (converted == HUGE_VAL || converted == -HUGE_VAL))
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Attribute '%s' causes overflow (%s)"),
      attribute,
      (const gchar*)value
    );

    return FALSE;
  }
  else if(errno == ERANGE && converted == 0.0)
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Attribute '%s' causes underflow (%s)"),
      attribute,
      (const gchar*)value
    );

    return FALSE;
  }
  else
  {
    *output = converted;
    return TRUE;
  }
}
Beispiel #3
0
/**
 * infc_request_manager_get_request_by_xml:
 * @manager: A #InfcRequestManager.
 * @name: Name of the expected request. May be %NULL to allow all requests.
 * @xml: XML node that is supposed to contain a "seq" attribute.
 * @error: Location to store error information.
 *
 * Looks whether there is a "seq" attribute in @xml. If not, the function
 * returns %NULL (without setting @error). Otherwise, it returns the request
 * with the given seq and name (if any). If the "seq" attribute is set but
 * the actual request is not present (or has another name), the function
 * returns %NULL and @error is set.
 *
 * Return Value: The resulting request, or %NULL if the "seq" attribute was
 * not present or an error occured.
 **/
InfcRequest*
infc_request_manager_get_request_by_xml(InfcRequestManager* manager,
                                        const gchar* name,
                                        xmlNodePtr xml,
                                        GError** error)
{
  InfcRequestManagerPrivate* priv;
  InfcRequest* request;
  gboolean has_seq;
  guint seq;

  g_return_val_if_fail(INFC_IS_REQUEST_MANAGER(manager), NULL);
  g_return_val_if_fail(xml != NULL, NULL);

  priv = INFC_REQUEST_MANAGER_PRIVATE(manager);
  request = NULL;

  has_seq = inf_xml_util_get_attribute_uint(xml, "seq", &seq, error);
  if(has_seq == FALSE) return NULL;

  request = infc_request_manager_get_request_by_seq(manager, seq);
  if(request == NULL)
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_SEQ,
      _("The request contains an unknown sequence number")
    );
  }
  else
  {
    if(name != NULL &&
       strcmp(name, infc_request_get_name(request)) != 0)
    {
      g_set_error(
        error,
        inf_request_error_quark(),
        INF_REQUEST_ERROR_INVALID_SEQ,
        _("The request contains a sequence number refering to a request of "
          "type '%s', but a request of type '%s' was expected"),
        infc_request_get_name(request),
        name
      );

      request = NULL;
    }
  }

  return request;
}
void Gobby::UserJoinCommands::UserJoinInfo::
	on_user_join_finished(InfUser* user, const GError* error)
{
	if(m_request != NULL)
	{
		g_object_unref(m_request);
		m_request = NULL;
	}

	if(error == NULL)
	{
		user_join_complete(user);
	}
	else if(error->domain == inf_user_error_quark() &&
	        error->code == INF_USER_ERROR_NAME_IN_USE)
	{
		// If name is in use retry with alternative user name
		++m_retry_index;
		attempt_user_join();
	}
	else if(error->domain == inf_request_error_quark() &&
	        error->code == INF_REQUEST_ERROR_NOT_AUTHORIZED)
	{
		set_permission_denied_text(m_view);
		finish();
	}
	else
	{
		set_error_text(m_view, error->message);
		finish();
	}
}
Beispiel #5
0
static GError*
infc_session_proxy_translate_error_impl(InfcSessionProxy* proxy,
                                        GQuark domain,
                                        guint code)
{
  GError* error;
  const gchar* error_msg;

  if(domain == inf_request_error_quark())
    error_msg = inf_request_strerror(code);
  else if(domain == inf_user_error_quark())
    error_msg = inf_user_strerror(code);
  else
    error_msg = NULL;

  error = NULL;
  if(error_msg != NULL)
  {
    g_set_error(&error, domain, code, "%s", error_msg);
  }
  else
  {
    /* TODO: Check whether a human-readable error string was sent (that
     * we cannot translate then, of course). */
    g_set_error(
      &error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_UNKNOWN_DOMAIN,
      _("Error comes from unknown error domain '%s' (code %u)"),
      g_quark_to_string(domain),
      (guint)code
    );
  }

  return error;
}
Beispiel #6
0
xmlChar*
inf_xml_util_get_attribute_required(xmlNodePtr xml,
                                    const gchar* attribute,
                                    GError** error)
{
  xmlChar* value;
  value = xmlGetProp(xml, (const xmlChar*)attribute);

  if(value == NULL)
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_NO_SUCH_ATTRIBUTE,
      _("Request '%s' does not contain required attribute '%s'"),
      (const gchar*)xml->name,
      attribute
    );
  }

  return value;
}
Beispiel #7
0
/**
 * infc_request_manager_get_request_by_xml_required:
 * @manager: A #InfcRequestManager.
 * @name: Name of the expected request. May be %NULL to allow all requests.
 * @xml: XML node that is supposed to contain a "seq" attribute.
 * @error: Location to store error information.
 *
 * Looks whether there is a "seq" attribute in @xml. If so, it returns the
 * request with the given seq and name (if any). If the "seq" attribute is
 * not set or the actual request is not present (or has another name), the
 * function returns %NULL and @error is set.
 *
 * Return Value: The resulting request, or %NULL if an error occured.
 **/
InfcRequest*
infc_request_manager_get_request_by_xml_required(InfcRequestManager* manager,
                                                 const gchar* name,
                                                 xmlNodePtr xml,
                                                 GError** error)
{
  InfcRequest* request;
  GError* own_error;

  own_error = NULL;
  request = infc_request_manager_get_request_by_xml(
    manager,
    name,
    xml,
    &own_error
  );

  if(request == NULL)
  {
    if(own_error == NULL)
    {
      g_set_error(
        error,
        inf_request_error_quark(),
        INF_REQUEST_ERROR_INVALID_SEQ,
        _("The request does not contain a sequence number, but one "
          "is required")
      );
    }
    else
    {
      g_propagate_error(error, own_error);
    }
  }

  return request;
}
Beispiel #8
0
void Gobby::UserJoin::attempt_user_join()
{
	// Check if there is already a local user, for example for a
	// synced-in document.
	InfSession* session;
	g_object_get(G_OBJECT(m_proxy), "session", &session, NULL);

	InfUserTable* user_table = inf_session_get_user_table(session);
	InfUser* user = NULL;
	inf_user_table_foreach_local_user(user_table,
	                                  retr_local_user_func, &user);
	g_object_unref(session);

	if(user != NULL)
	{
		user_join_complete(user, NULL);
		return;
	}

	// Next, check whether we are allowed to join a user
	if(m_node.get_browser() && m_node.get_browser_iter())
	{
		InfBrowser* browser = m_node.get_browser();
		const InfBrowserIter* iter = m_node.get_browser_iter();
		const InfAclAccount* account =
			inf_browser_get_acl_local_account(browser);
		const InfAclAccountId acc_id =
			(account != NULL) ? account->id : 0;
		InfAclMask msk;
		inf_acl_mask_set1(&msk, INF_ACL_CAN_JOIN_USER);
		if(!inf_browser_check_acl(browser, iter, acc_id, &msk, NULL))
		{
			GError* error = NULL;
			g_set_error(
				&error, inf_request_error_quark(),
				INF_REQUEST_ERROR_NOT_AUTHORIZED,
				"%s", inf_request_strerror(
					INF_REQUEST_ERROR_NOT_AUTHORIZED));
			user_join_complete(NULL, error);
			g_error_free(error);
			return;
		}
	}

	// We are allowed, so attempt to join the user now.
	std::vector<GParameter> params =
		m_param_provider->get_user_join_parameters();
	std::vector<GParameter>::iterator name_index =
		find_name_param(params);
	const gchar* name = g_value_get_string(&name_index->value);

	if(m_retry_index > 1)
	{
		gchar* new_name = g_strdup_printf(
			"%s %u", name, m_retry_index);
		g_value_take_string(&name_index->value, new_name);
	}

	GError* error = NULL;
	InfRequest* request = inf_session_proxy_join_user(
		m_proxy, params.size(), &params[0],
		on_user_join_finished_static, this);

	for(unsigned int i = 0; i < params.size(); ++i)
		g_value_unset(&params[i].value);

	if(request != NULL)
	{
		m_request = request;
		g_object_ref(m_request);
	}
}
Beispiel #9
0
static gboolean
infc_session_proxy_net_object_received(InfNetObject* net_object,
                                       InfXmlConnection* connection,
                                       xmlNodePtr node,
                                       GError** error)
{
  InfcSessionProxy* proxy;
  InfcSessionProxyPrivate* priv;
  InfcSessionProxyClass* proxy_class;
  InfSessionSyncStatus status;
  GError* local_error;

  InfcRequest* request;
  GError* seq_error;

  proxy = INFC_SESSION_PROXY(net_object);
  priv = INFC_SESSION_PROXY_PRIVATE(proxy);
  proxy_class = INFC_SESSION_PROXY_GET_CLASS(proxy);
  status = inf_session_get_synchronization_status(priv->session, connection);
  local_error = NULL;

  g_assert(status != INF_SESSION_SYNC_NONE || priv->connection == connection);
  g_assert(inf_session_get_status(priv->session) != INF_SESSION_CLOSED);

  if(status != INF_SESSION_SYNC_NONE)
  {
    /* Direct delegate during synchronization */
    return inf_net_object_received(
      INF_NET_OBJECT(priv->session),
      connection,
      node,
      error
    );
  }
  else
  {
    if(strcmp((const char*)node->name, "user-join") == 0)
    {
      infc_session_proxy_handle_user_join(
        proxy,
        connection,
        node,
        &local_error
      );
    }
    else if(strcmp((const char*)node->name, "user-rejoin") == 0)
    {
      infc_session_proxy_handle_user_rejoin(
        proxy,
        connection,
        node,
        &local_error
      );
    }
    else if(strcmp((const char*)node->name, "request-failed") == 0)
    {
      infc_session_proxy_handle_request_failed(
        proxy,
        connection,
        node,
        &local_error
      );
    }
    else if(strcmp((const char*)node->name, "session-close") == 0)
    {
      infc_session_proxy_handle_session_close(
        proxy,
        connection,
        node,
        &local_error
      );
    }
    else
    {
      return inf_net_object_received(
        INF_NET_OBJECT(priv->session),
        connection,
        node,
        error
      );
    }
  }

  if(local_error != NULL)
  {
    /* If the request had a (valid) seq set, we cancel the corresponding
     * request because the reply could not be processed. */
    request = infc_request_manager_get_request_by_xml(
      priv->request_manager,
      NULL,
      node,
      NULL
    );

    if(request != NULL)
    {
      /* If the request had a seq set, we cancel the corresponding request
       * because the reply could not be processed. */
      seq_error = NULL;
      g_set_error(
        &seq_error,
        inf_request_error_quark(),
        INF_REQUEST_ERROR_REPLY_UNPROCESSED,
        _("Server reply could not be processed: %s"),
        local_error->message
      );

      infc_request_manager_fail_request(
        priv->request_manager,
        request,
        seq_error
      );

      g_error_free(seq_error);
    }

    g_propagate_error(error, local_error);
  }

  /* Don't forward any of the handled messages */
  return FALSE;
}
Beispiel #10
0
static gboolean
infc_session_proxy_handle_user_rejoin(InfcSessionProxy* proxy,
                                      InfXmlConnection* connection,
                                      xmlNodePtr xml,
                                      GError** error)
{
  InfcSessionProxyPrivate* priv;
  InfSessionClass* session_class;
  GArray* array;
  InfUser* user;
  const GParameter* idparam;
  GParameter* param;
  guint id;
  gboolean result;
  guint i;

  priv = INFC_SESSION_PROXY_PRIVATE(proxy);
  session_class = INF_SESSION_GET_CLASS(priv->session);

  array = session_class->get_xml_user_props(priv->session, connection, xml);

  /* Find rejoining user first */
  idparam = inf_session_lookup_user_property(
    (const GParameter*)array->data,
    array->len,
    "id"
  );

  if(idparam == NULL)
  {
    g_set_error(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_NO_SUCH_ATTRIBUTE,
      _("Request does not contain required attribute 'id'")
    );

    goto error;
  }

  id = g_value_get_uint(&idparam->value);
  user = inf_user_table_lookup_user_by_id(
    inf_session_get_user_table(priv->session),
    id
  );

  if(user == NULL)
  {
    g_set_error(
      error,
      inf_user_error_quark(),
      INF_USER_ERROR_NO_SUCH_USER,
      _("No such user with ID %u"),
      id
    );

    goto error;
  }

  /* Set local flag if the join was requested by us (seq is present in
   * server response). */
  param = inf_session_get_user_property(array, "flags");
  g_assert(!G_IS_VALUE(&param->value)); /* must not have been set already */

  g_value_init(&param->value, INF_TYPE_USER_FLAGS);
  if(xmlHasProp(xml, (const xmlChar*)"seq") != NULL)
    g_value_set_flags(&param->value, INF_USER_LOCAL);
  else
    g_value_set_flags(&param->value, 0);

  /* Set connection. If none was given, use publisher connection */
  param = inf_session_get_user_property(array, "connection");
  if(!G_IS_VALUE(&param->value))
  {
    g_value_init(&param->value, INF_TYPE_XML_CONNECTION);
    g_value_set_object(&param->value, G_OBJECT(connection));
  }

  result = session_class->validate_user_props(
    priv->session,
    (const GParameter*)array->data,
    array->len,
    user,
    error
  );

  if(result == FALSE)
    goto error;

  /* Set properties on the found user object, performing the rejoin */
  g_object_freeze_notify(G_OBJECT(user));

  for(i = 0; i < array->len; ++ i)
  {
    param = &g_array_index(array, GParameter, i);

    /* Don't set ID because the ID is the same anyway (we did the user lookup
     * by it). The "id" property is construct only anyway. */
    if(strcmp(param->name, "id") != 0)
      g_object_set_property(G_OBJECT(user), param->name, &param->value);
  }

  /* TODO: Set user status to available, if the server did not send the
   * status property? Require the status property being set on a rejoin?
   * Make sure it is not unavailable? */

  g_object_thaw_notify(G_OBJECT(user));
  for(i = 0; i < array->len; ++ i)
    g_value_unset(&g_array_index(array, GParameter, i).value);
  g_array_free(array, TRUE);

  infc_session_proxy_succeed_user_request(proxy, xml, user);

  return TRUE;

error:
  for(i = 0; i < array->len; ++ i)
    g_value_unset(&g_array_index(array, GParameter, i).value);
  g_array_free(array, TRUE);
  return FALSE;
}
/**
 * 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;
}
Beispiel #12
0
/**
 * inf_protocol_parse_version:
 * @version: A version string, such as "1.0"
 * @major: A location to store the major version number to.
 * @minor: A location to store the minor version number to
 * @error: Location to store error information, if any.
 *
 * Splits the given version string into it's major and minor version number.
 * If the string is badly formatted then the function returns %FALSE, @error
 * is set and @major and @minor are left untouched.
 *
 * It is guaranteed that, if @version is inf_protocol_get_version(), the
 * function does not fail.
 *
 * Returns: %TRUE on success, or %FALSE on error.
 */
gboolean
inf_protocol_parse_version(const gchar* version,
                           guint* major,
                           guint* minor,
                           GError** error)
{
  gchar* endptr;
  unsigned long maj;
  unsigned long min;

  errno = 0;
  maj = strtoul(version, &endptr, 10);
  if(errno == ERANGE || maj > (unsigned long)G_MAXUINT)
  {
    g_set_error_literal(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Major part of version number causes overflow")
    );

    return FALSE;
  }

  if(*endptr != '.')
  {
    g_set_error_literal(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Version number parts are not separated by '.'")
    );

    return FALSE;
  }

  errno = 0;
  min = strtoul(endptr+1, &endptr, 10);
  if(errno == ERANGE || min > (unsigned long)G_MAXUINT)
  {
    g_set_error_literal(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Minor part of version number causes overflow")
    );

    return FALSE;
  }

  if(*endptr != '\0')
  {
    g_set_error_literal(
      error,
      inf_request_error_quark(),
      INF_REQUEST_ERROR_INVALID_NUMBER,
      _("Trailing characters after version number")
    );

    return FALSE;
  }

  if(major) *major = maj;
  if(minor) *minor = min;
  return TRUE;
}