/* Creates a new TCP connection from an accepted socket. This is only used
 * by InfdTcpServer and should not be considered regular API. Do not call
 * this function. Language bindings should not wrap it. */
InfTcpConnection*
_inf_tcp_connection_accepted(InfIo* io,
                             InfNativeSocket socket,
                             InfIpAddress* address,
                             guint port,
                             const InfKeepalive* keepalive,
                             GError** error)
{
  InfTcpConnection* connection;
  InfTcpConnectionPrivate* priv;
  int errcode;

  g_return_val_if_fail(INF_IS_IO(io), NULL);
  g_return_val_if_fail(socket != INVALID_SOCKET, NULL);
  g_return_val_if_fail(address != NULL, NULL);
  g_return_val_if_fail(keepalive != NULL, NULL);

  if(inf_tcp_connection_configure_socket(socket, keepalive, error) != TRUE)
    return NULL;

  g_return_val_if_fail(address != NULL, NULL);
  g_return_val_if_fail(port != 0, NULL);

  connection = inf_tcp_connection_new(io, address, port);

  inf_ip_address_free(address);

  priv = INF_TCP_CONNECTION_PRIVATE(connection);
  priv->socket = socket;
  priv->keepalive = *keepalive;

  inf_tcp_connection_connected(connection);
  return connection;
}
/**
 * inf_tcp_connection_new_and_open: (constructor)
 * @io: A #InfIo object used to watch for activity.
 * @remote_addr: The address to connect to.
 * @remote_port: The port to connect to.
 * @error: Location to store error information.
 *
 * Creates a new #InfTcpConnection and connects it to the given TCP endpoint.
 * Like inf_tcp_connection_new(), but calls inf_tcp_connection_open().
 *
 * Returns: (transfer full): A new #InfTcpConnection, or %NULL on error.
 * Free with g_object_unref().
 **/
InfTcpConnection*
inf_tcp_connection_new_and_open(InfIo* io,
                                const InfIpAddress* remote_addr,
                                guint remote_port,
                                GError** error)
{
  InfTcpConnection* tcp;

  g_return_val_if_fail(INF_IS_IO(io), NULL);
  g_return_val_if_fail(remote_addr != NULL, NULL);
  g_return_val_if_fail(remote_port <= 65535, NULL);
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);

  tcp = inf_tcp_connection_new(io, remote_addr, remote_port);

  if(inf_tcp_connection_open(tcp, error) == FALSE)
  {
    g_object_unref(tcp);
    return NULL;
  }

  return tcp;
}
static InfXmppConnection*
inf_test_certificate_validate_setup_client(InfIo* io,
                                           const gchar* ca_file,
                                           const gchar* remote_hostname,
                                           GError** error)
{
  InfIpAddress* addr;
  InfTcpConnection* conn;
  InfXmppConnection* xmpp;

  InfCertificateCredentials* creds;
  GPtrArray* cas;
  int res;
  guint i;

  creds = inf_certificate_credentials_new();
  if(ca_file != NULL)
  {
    cas = inf_cert_util_read_certificate(ca_file, NULL, error);
    if(cas == NULL)
    {
      inf_certificate_credentials_unref(creds);
      return NULL;
    }

    res = gnutls_certificate_set_x509_trust(
      inf_certificate_credentials_get(creds),
      (gnutls_x509_crt_t*)cas->pdata,
      cas->len
    );

    for(i = 0; i < cas->len; ++i)
      gnutls_x509_crt_deinit(cas->pdata[i]);
    g_ptr_array_free(cas, TRUE);
  }

  addr = inf_ip_address_new_loopback4();
  conn = inf_tcp_connection_new(io, addr, 6524);
  inf_ip_address_free(addr);

  xmpp = inf_xmpp_connection_new(
    conn,
    INF_XMPP_CONNECTION_CLIENT,
    g_get_host_name(),
    remote_hostname,
    INF_XMPP_CONNECTION_SECURITY_ONLY_TLS,
    creds,
    NULL,
    NULL
  );

  inf_certificate_credentials_unref(creds);
  if(inf_tcp_connection_open(conn, error) == FALSE)
  {
    g_object_unref(conn);
    g_object_unref(xmpp);
    return NULL;
  }

  g_object_unref(conn);
  return xmpp;
}
static void
bookmark_added (GeditCollaborationWindowHelper *helper,
                GeditCollaborationBookmark     *bookmark)
{
	GResolver *resolver = g_resolver_get_default ();
	GList *addresses;
	InfTcpConnection *tcp;
	InfIpAddress *ipaddress;
	InfXmppConnection *connection;
	gchar *ipaddr;
	NameInfo *info;
	GeditCollaborationUser *user;

	/* TODO: make this asynchronous and be smarter about it */
	addresses = g_resolver_lookup_by_name (resolver,
	                                       gedit_collaboration_bookmark_get_host (bookmark),
	                                       NULL,
	                                       NULL);

	if (!addresses)
	{
		return;
	}

	ipaddr = g_inet_address_to_string ((GInetAddress *)addresses->data);
	g_resolver_free_addresses (addresses);

	ipaddress = inf_ip_address_new_from_string (ipaddr);
	g_free (ipaddr);

	tcp = inf_tcp_connection_new (helper->priv->io,
	                              ipaddress,
	                              (guint)gedit_collaboration_bookmark_get_port (bookmark));

	user = gedit_collaboration_bookmark_get_user (bookmark);
	connection = inf_xmpp_connection_new (tcp,
	                                      INF_XMPP_CONNECTION_CLIENT,
	                                      NULL,
	                                      gedit_collaboration_bookmark_get_host (bookmark),
	                                      INF_XMPP_CONNECTION_SECURITY_BOTH_PREFER_TLS,
	                                      helper->priv->certificate_credentials,
	                                      gedit_collaboration_user_get_sasl_context (user),
	                                      "ANONYMOUS PLAIN");

	g_signal_connect (user,
	                  "request-password",
	                  G_CALLBACK (user_request_password),
	                  helper);

	inf_gtk_browser_store_add_connection (helper->priv->browser_store,
	                                      INF_XML_CONNECTION (connection),
	                                      gedit_collaboration_bookmark_get_name (bookmark));

	g_object_set_data (G_OBJECT (connection), BOOKMARK_DATA_KEY, bookmark);

	inf_ip_address_free (ipaddress);
	g_object_unref (tcp);

	info = g_slice_new (NameInfo);
	info->helper = helper;
	info->connection = INF_XML_CONNECTION (connection);

	g_signal_connect_data (bookmark,
	                       "notify::name",
	                       G_CALLBACK (on_bookmark_name_changed),
	                       info,
	                       (GClosureNotify)name_info_free,
	                       0);
}
static void
inf_test_mass_join_connect(InfTestMassJoin* massjoin,
                           const char* hostname,
                           guint port,
                           const char* document,
                           const char* username)
{
    InfIpAddress* addr;
    InfTcpConnection* tcp;
    InfXmppConnection* xmpp;
    InfTestMassJoiner* joiner;
    InfXmlConnection* xml;
    GError* error;

    addr = inf_ip_address_new_from_string(hostname);
    tcp = inf_tcp_connection_new(massjoin->io, addr, port);
    xmpp = inf_xmpp_connection_new(
               tcp,
               INF_XMPP_CONNECTION_CLIENT,
               g_get_host_name(),
               hostname,
               INF_XMPP_CONNECTION_SECURITY_BOTH_PREFER_TLS,
               NULL,
               NULL,
               NULL
           );

    joiner = g_slice_new(InfTestMassJoiner);
    joiner->communication_manager = inf_communication_manager_new();
    joiner->browser = infc_browser_new(
                          massjoin->io,
                          joiner->communication_manager,
                          INF_XML_CONNECTION(xmpp)
                      );
    joiner->session = NULL;
    joiner->document = g_strdup(document);
    joiner->username = g_strdup(username);

    g_object_unref(xmpp);
    g_object_unref(tcp);
    inf_ip_address_free(addr);

    massjoin->joiners = g_slist_prepend(massjoin->joiners, joiner);
    infc_browser_add_plugin(joiner->browser, &INF_TEST_MASS_JOIN_TEXT_PLUGIN);

    g_signal_connect(
        G_OBJECT(joiner->browser),
        "notify::status",
        G_CALLBACK(inf_test_mass_join_browser_notify_status_cb),
        massjoin
    );

    error = NULL;
    xml = infc_browser_get_connection(joiner->browser);
    if(inf_xml_connection_open(xml, &error) == FALSE)
    {
        fprintf(
            stderr,
            "Joiner %s: Failed to connect to %s: %s\n",
            joiner->username,
            hostname,
            error->message
        );

        g_error_free(error);
        massjoin->joiners = g_slist_remove(massjoin->joiners, joiner);

        if(massjoin->joiners == NULL)
            inf_standalone_io_loop_quit(INF_STANDALONE_IO(massjoin->io));
    }
}