/**
 * inf_text_gtk_viewport_set_active_user:
 * @viewport: A #InfTextGtkViewport.
 * @user: A user from @viewport's user table, or %NULL.
 *
 * Sets the user for which perspective to draw the viewport. The cursor
 * position for teh active user is not draws since it is assumed that the
 * viewport's "real" scrollbars match the active user's position.
 */
void
inf_text_gtk_viewport_set_active_user(InfTextGtkViewport* viewport,
                                      InfTextUser* user)
{
  InfTextGtkViewportPrivate* priv;
  InfTextUser* active_user;

  g_return_if_fail(INF_TEXT_GTK_IS_VIEWPORT(viewport));
  g_return_if_fail(user == NULL || INF_TEXT_IS_USER(user));

  priv = INF_TEXT_GTK_VIEWPORT_PRIVATE(viewport);
  g_return_if_fail(
    user == NULL ||
    inf_user_table_lookup_user_by_id(
      priv->user_table,
      inf_user_get_id(INF_USER(user))
    ) == INF_USER(user)
  );

  if(priv->active_user != NULL)
  {
    active_user = priv->active_user;
    priv->active_user = NULL;

    inf_text_gtk_viewport_user_added(viewport, active_user);
  }

  if(user != NULL)
  {
    inf_text_gtk_viewport_user_removed(viewport, user);
  }

  priv->active_user = user;
  g_object_notify(G_OBJECT(viewport), "active-user");
}
Exemplo n.º 2
0
static InfAdoptedUser*
inf_adopted_session_user_from_request_xml(InfAdoptedSession* session,
                                          xmlNodePtr xml,
                                          GError** error)
{
  InfUserTable* user_table;
  InfUser* user;
  guint user_id;

  user_table = inf_session_get_user_table(INF_SESSION(session));

  if(!inf_xml_util_get_attribute_uint_required(xml, "user", &user_id, error))
    return FALSE;

  /* User ID 0 means no user */
  if(user_id == 0) return NULL;

  user = inf_user_table_lookup_user_by_id(user_table, user_id);

  if(user == NULL)
  {
    g_set_error(
      error,
      inf_adopted_session_error_quark,
      INF_ADOPTED_SESSION_ERROR_NO_SUCH_USER,
      _("No such user with user ID '%u'"),
      user_id
    );

    return NULL;
  }

  g_assert(INF_ADOPTED_IS_USER(user));
  return INF_ADOPTED_USER(user);
}
Exemplo n.º 3
0
static gboolean
inf_adopted_session_process_xml_sync(InfSession* session,
                                     InfXmlConnection* connection,
                                     const xmlNodePtr xml,
                                     GError** error)
{
  InfAdoptedSessionClass* session_class;
  InfAdoptedRequest* request;
  InfAdoptedUser* user;
  InfAdoptedRequestLog* log;

  if(strcmp((const char*)xml->name, "sync-request") == 0)
  {
    session_class = INF_ADOPTED_SESSION_GET_CLASS(session);
    g_assert(session_class->xml_to_request != NULL);

    request = session_class->xml_to_request(
      INF_ADOPTED_SESSION(session),
      xml,
      NULL, /* TODO: Diff to previous request, if any. */
      TRUE,
      error
    );

    if(request == NULL) return FALSE;

    user = INF_ADOPTED_USER(
      inf_user_table_lookup_user_by_id(
        inf_session_get_user_table(session),
        inf_adopted_request_get_user_id(request)
      )
    );

    log = inf_adopted_user_get_request_log(user);
    if(inf_adopted_session_validate_request(log, request, error) == FALSE)
    {
      g_object_unref(request);
      return FALSE;
    }

    inf_adopted_request_log_add_request(log, request);
    g_object_unref(request);

    return TRUE;
  }

  return INF_SESSION_CLASS(parent_class)->process_xml_sync(
    session,
    connection, 
    xml,
    error
  );
}
Exemplo n.º 4
0
/* Breadcasts a request N times - makes only sense for undo and redo requests,
 * so that's the only thing we offer API for. */
static void
inf_adopted_session_broadcast_n_requests(InfAdoptedSession* session,
                                         InfAdoptedRequest* request,
                                         guint n)
{
  InfAdoptedSessionPrivate* priv;
  InfAdoptedSessionClass* session_class;
  InfUserTable* user_table;
  guint user_id;
  InfUser* user;
  InfAdoptedSessionLocalUser* local;
  xmlNodePtr xml;

  priv = INF_ADOPTED_SESSION_PRIVATE(session);
  session_class = INF_ADOPTED_SESSION_GET_CLASS(session);
  g_assert(session_class->request_to_xml != NULL);

  user_table = inf_session_get_user_table(INF_SESSION(session));
  user_id = inf_adopted_request_get_user_id(request);
  user = inf_user_table_lookup_user_by_id(user_table, user_id);
  g_assert(user != NULL);

  local = inf_adopted_session_lookup_local_user(
    session,
    INF_ADOPTED_USER(user)
  );
  g_assert(local != NULL);

  xml = xmlNewNode(NULL, (const xmlChar*)"request");

  session_class->request_to_xml(
    session,
    xml,
    request,
    local->last_send_vector,
    FALSE
  );

  if(n > 1) inf_xml_util_set_attribute_uint(xml, "num", n);
  inf_session_send_to_subscriptions(INF_SESSION(session), xml);

  inf_adopted_state_vector_free(local->last_send_vector);
  local->last_send_vector = inf_adopted_state_vector_copy(
    inf_adopted_request_get_vector(request)
  );

  /* Add this request to last send vector if it increases vector time
   * (-> affects buffer). */
  if(inf_adopted_request_affects_buffer(request) == TRUE)
    inf_adopted_state_vector_add(local->last_send_vector, user_id, n);

  inf_adopted_session_stop_noop_timer(session, local);
}
Exemplo n.º 5
0
void Gobby::ChatSessionView::set_active_user(InfUser* user)
{
	g_assert(
		user == NULL ||
		inf_user_table_lookup_user_by_id(
			inf_session_get_user_table(INF_SESSION(m_session)),
			inf_user_get_id(INF_USER(user)))
		== INF_USER(user));

	inf_gtk_chat_set_active_user(m_chat, user);
	active_user_changed(user);
}
Exemplo n.º 6
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;
}
static gboolean
perform_test(guint max_total_log_size,
             InfTextChunk* initial,
             GSList* users,
             GSList* requests,
             GError** error)
{
  InfTextBuffer* buffer;
  InfCommunicationManager* manager;
  InfIo* io;
  InfTextSession* session;
  InfAdoptedAlgorithm* algorithm;

  InfUserTable* user_table;
  InfTextUser* user;
  gchar* user_name;

  GSList* item;
  xmlNodePtr request;
  gboolean result;
  GError* local_error;
  
  guint verify_user_id;
  InfAdoptedUser* verify_user;
  guint verify_log_size;
  gint verify_can_undo;
  gint verify_can_redo;

  InfAdoptedRequestLog* log;
  guint log_size;

  buffer = INF_TEXT_BUFFER(inf_text_default_buffer_new("UTF-8"));
  inf_text_buffer_insert_chunk(buffer, 0, initial, NULL);

  manager = inf_communication_manager_new();
  io = INF_IO(inf_standalone_io_new());
  user_table = inf_user_table_new();
  local_error = NULL;

  for(item = users; item != NULL; item = g_slist_next(item))
  {
    user_name = g_strdup_printf("User_%u", GPOINTER_TO_UINT(item->data));

    user = INF_TEXT_USER(
      g_object_new(
        INF_TEXT_TYPE_USER,
        "id", GPOINTER_TO_UINT(item->data),
        "name", user_name,
        "status", INF_USER_ACTIVE,
        "flags", 0,
        NULL
      )
    );

    g_free(user_name);
    inf_user_table_add_user(user_table, INF_USER(user));
    g_object_unref(user);
  }

  session = INF_TEXT_SESSION(
    g_object_new(
      INF_TEXT_TYPE_SESSION,
      "communication-manager", manager,
      "buffer", buffer,
      "io", io,
      "user_table", user_table,
      "max-total-log-size", max_total_log_size,
      NULL
    )
  );
  
  algorithm = inf_adopted_session_get_algorithm(INF_ADOPTED_SESSION(session));

  g_object_unref(io);
  g_object_unref(manager);
  g_object_unref(user_table);
  g_object_unref(buffer);

  for(item = requests; item != NULL; item = item->next)
  {
    request = (xmlNodePtr)item->data;
    
    if(strcmp((const char*)request->name, "request") == 0)
    {
      /* Request */
      result = inf_communication_object_received(
        INF_COMMUNICATION_OBJECT(session),
        NULL,
        request,
        &local_error
      );
      
      if(local_error != NULL)
        goto fail;
    }
    else
    {
      /* TODO: Make an extra function out of this: */
      /* Verify */
      result = inf_xml_util_get_attribute_uint_required(
        request,
        "user",
        &verify_user_id,
        &local_error
      );
      
      if(result == FALSE)
        goto fail;

      verify_user = INF_ADOPTED_USER(
        inf_user_table_lookup_user_by_id(user_table, verify_user_id)
      );

      if(verify_user == NULL)
      {
        g_set_error(
          error,
          inf_test_text_cleanup_error_quark(),
          INF_TEST_TEXT_CLEANUP_USER_UNAVAILABLE,
          "User ID '%u' not available",
          verify_user_id
        );
        
        goto fail;
      }

      result = inf_xml_util_get_attribute_uint(
        request,
        "log-size",
        &verify_log_size,
        &local_error
      );

      if(local_error) goto fail;

      if(result)
      {
        log = inf_adopted_user_get_request_log(INF_ADOPTED_USER(verify_user));
        log_size = inf_adopted_request_log_get_end(log) -
          inf_adopted_request_log_get_begin(log);
        if(verify_log_size != log_size)
        {
          g_set_error(
            error,
            inf_test_text_cleanup_error_quark(),
            INF_TEST_TEXT_CLEANUP_VERIFY_FAILED,
            "Log size does not match; got %u, but expected %u",
            log_size,
            verify_log_size
          );

          goto fail;
        }
      }
      
      result = inf_xml_util_get_attribute_int(
        request,
        "can-undo",
        &verify_can_undo,
        &local_error
      );

      if(local_error) goto fail;

      if(result)
      {
        result = inf_adopted_algorithm_can_undo(algorithm, verify_user);
        if(result != verify_can_undo)
        {
          g_set_error(
            error,
            inf_test_text_cleanup_error_quark(),
            INF_TEST_TEXT_CLEANUP_VERIFY_FAILED,
            "can-undo does not match; got %d, but expected %d",
            (guint)result,
            verify_can_undo
          );

          goto fail;
        }
      }

      result = inf_xml_util_get_attribute_int(
        request,
        "can-redo",
        &verify_can_redo,
        &local_error
      );

      if(local_error) goto fail;

      if(result)
      {
        result = inf_adopted_algorithm_can_redo(algorithm, verify_user);
        if(result != verify_can_redo)
        {
          g_set_error(
            error,
            inf_test_text_cleanup_error_quark(),
            INF_TEST_TEXT_CLEANUP_VERIFY_FAILED,
            "can-redo does not match; got %d, but expected %d",
            (guint)result,
            verify_can_redo
          );

          goto fail;
        }
      }
    }
  }

  g_object_unref(session);
  return TRUE;

fail:
  g_object_unref(session);
  if(local_error) g_propagate_error(error, local_error);
  return FALSE;
}
static gboolean
infd_note_plugin_text_read_user(InfUserTable* user_table,
                                xmlNodePtr node,
                                GError** error)
{
  guint id;
  gdouble hue;
  xmlChar* name;
  gboolean result;
  InfUser* user;

  if(!inf_xml_util_get_attribute_uint_required(node, "id", &id, error))
    return FALSE;

  if(!inf_xml_util_get_attribute_double_required(node, "hue", &hue, error))
    return FALSE;

  name = inf_xml_util_get_attribute_required(node, "name", error);
  if(name == NULL)
    return FALSE;

  if(inf_user_table_lookup_user_by_id(user_table, id) != NULL)
  {
    g_set_error(
      error,
      g_quark_from_static_string("INF_NOTE_PLUGIN_TEXT_ERROR"),
      INFD_NOTE_PLUGIN_TEXT_ERROR_USER_EXISTS,
      "User with ID %u exists already",
      id
    );

    result = FALSE;
  }
  else
  {
    if(inf_user_table_lookup_user_by_name(user_table, (const gchar*)name))
    {
      g_set_error(
        error,
        g_quark_from_static_string("INF_NOTE_PLUGIN_TEXT_ERROR"),
        INFD_NOTE_PLUGIN_TEXT_ERROR_USER_EXISTS,
        "User with name `%s' exists already",
        (const gchar*)name
      );

      result = FALSE;
    }
    else
    {
      user = INF_USER(
        g_object_new(
          INF_TEXT_TYPE_USER,
          "id", id,
          "name", name,
          "hue", hue,
          NULL
        )
      );

      inf_user_table_add_user(user_table, user);
      g_object_unref(user);
      result = TRUE;
    }
  }

  xmlFree(name);
  return result;
}
static gboolean
infd_note_plugin_text_read_buffer(InfTextBuffer* buffer,
                                  InfUserTable* user_table,
                                  xmlNodePtr node,
                                  GError** error)
{
  xmlNodePtr child;
  guint author;
  gchar* content;
  gboolean result;
  gboolean res;
  InfUser* user;
  gsize bytes;
  guint chars;


  g_assert(inf_text_buffer_get_length(buffer) == 0);

  for(child = node->children; child != NULL; child = child->next)
  {
    if(child->type != XML_ELEMENT_NODE)
      continue;

    if(strcmp((const gchar*)child->name, "segment") == 0)
    {
      res = inf_xml_util_get_attribute_uint_required(
        child,
        "author",
        &author,
        error
      );

      if(res == FALSE)
      {
        result = FALSE;
        break;
      }

      if(author != 0)
      {
        user = inf_user_table_lookup_user_by_id(user_table, author);

        if(user == NULL)
        {
          g_set_error(
            error,
            g_quark_from_static_string("INF_NOTE_PLUGIN_TEXT_ERROR"),
            INFD_NOTE_PLUGIN_TEXT_ERROR_NO_SUCH_USER,
            "User with ID %u does not exist",
            author
          );

          result = FALSE;
          break;
        }
      }
      else
      {
        user = NULL;
      }

      content = inf_xml_util_get_child_text(child, &bytes, &chars, error);
      if(!content)
      {
        result = FALSE;
        break;
      }

      if(*content != '\0')
      {
        /* TODO: Use inf_text_buffer_append when we have it */
        inf_text_buffer_insert_text(
          buffer,
          inf_text_buffer_get_length(buffer),
          content,
          bytes,
          chars,
          user
        );
      }

      g_free(content);
    }
    else
    {
      infd_note_plugin_text_session_unexpected_node(child, error);
      result = FALSE;
      break;
    }
  }

  if(child == NULL)
    result = TRUE;

  return result;
}