static void
inf_chat_test_session_synchronization_complete_cb(InfSession* session,
                                                  InfXmlConnection* connection,
                                                  gpointer user_data)
{
  InfTestChat* test;
  InfcSessionProxy* proxy;
  InfcUserRequest* request;
  GParameter params[1] = { { "name", { 0 } } };
  GError* error;

  printf("Synchronization complete, joining user...\n");

  test = (InfTestChat*)user_data;
  proxy = infc_browser_get_chat_session(test->browser);

  g_value_init(&params[0].value, G_TYPE_STRING);
  g_value_set_string(&params[0].value, g_get_user_name());

  error = NULL;
  request = infc_session_proxy_join_user(
    proxy,
    params,
    G_N_ELEMENTS(params),
    &error
  );

  g_value_unset(&params[0].value);

  if(!request)
  {
    fprintf(stderr, "User join failed: %s\n", error->message);
    g_error_free(error);

    inf_standalone_io_loop_quit(test->io);
  }
  else
  {
    g_signal_connect_after(
      G_OBJECT(request),
      "failed",
      G_CALLBACK(inf_test_chat_userjoin_failed_cb),
      test
    );

    g_signal_connect_after(
      G_OBJECT(request),
      "finished",
      G_CALLBACK(inf_test_chat_userjoin_finished_cb),
      test
    );
  }
}
static void
request_join (ChatData    *data,
              const gchar *name)
{
	InfcUserRequest *request;
	GError *error = NULL;

	GParameter parameters[] = {
		{"name", {0,}}
	};

	g_value_init (&parameters[0].value, G_TYPE_STRING);
	g_value_set_string (&parameters[0].value,
	                    name);

	request = infc_session_proxy_join_user (data->proxy,
	                                        parameters,
	                                        1,
	                                        &error);

	g_value_unset (&parameters[0].value);

	if (error != NULL)
	{
		g_warning ("%s", error->message);
		g_error_free (error);
	}
	else
	{
		g_signal_connect_after (request,
		                        "failed",
		                        G_CALLBACK (on_join_user_request_failed),
		                        data);

		g_signal_connect_after (request,
		                        "finished",
		                        G_CALLBACK (on_join_user_request_finished),
		                        data);
	}
}
static void
request_join(InfTestGtkBrowserWindow* test,
             const gchar* user_name)
{
  InfcUserRequest* request;
  InfAdoptedStateVector* v;
  GError* error;
  GtkTextBuffer* buffer;
  GtkTextMark* mark;
  GtkTextIter iter;
  GParameter params[3] = {
    { "name", { 0 } },
    { "vector", { 0 } },
    { "caret-position", { 0 } }
  };

  g_value_init(&params[0].value, G_TYPE_STRING);
  g_value_init(&params[1].value, INF_ADOPTED_TYPE_STATE_VECTOR);
  g_value_init(&params[2].value, G_TYPE_UINT);

  g_value_set_static_string(&params[0].value, user_name);

  /* Use current state vector. Infinote should already do this. */
  v = inf_adopted_state_vector_copy(
    inf_adopted_algorithm_get_current(
      inf_adopted_session_get_algorithm(
        INF_ADOPTED_SESSION(infc_session_proxy_get_session(test->proxy))
      )
    )
  );

  g_value_take_boxed(&params[1].value, v);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(test->textview));
  mark = gtk_text_buffer_get_insert(buffer);
  gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
  g_value_set_uint(&params[2].value, gtk_text_iter_get_offset(&iter));

  error = NULL;
  request = infc_session_proxy_join_user(test->proxy, params, 3, &error);

  /* TODO: Free GValues? */

  if(request == NULL)
  {
    set_error(test, "Failed to request user join", error->message);
  }
  else
  {
    g_signal_connect_after(
      G_OBJECT(request),
      "failed",
      G_CALLBACK(on_join_failed),
      test
    );

    g_signal_connect_after(
      G_OBJECT(request),
      "finished",
      G_CALLBACK(on_join_finished),
      test
    );
  }
}