Commit message: SoupServer: fix to not allow smuggling ".." into path

When SoupServer:raw-paths was set (the default), it was possible to
sneak ".." segments into the path passed to the SoupServerHandler,
which could then end up tricking some handlers into retrieving
arbitrary files from the filesystem. Fix that.

https://bugzilla.gnome.org/show_bug.cgi?id=653258
CVE: CVE-2011-2524
Vulnerability description: Directory traversal vulnerability in soup-uri.c in SoupServer in libsoup before 2.35.4 allows remote attackers to read arbitrary files via a %2e%2e (encoded dot dot) in a URI.

Project: libsoup
File: soup-server.c

get_property (GObject *object, guint prop_id,
	      GValue *value, GParamSpec *pspec)
{
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (object);

	switch (prop_id) {
	case PROP_PORT:
		g_value_set_uint (value, priv->port);
		break;
	case PROP_INTERFACE:
		g_value_set_object (value, priv->interface);
		break;
	case PROP_SSL_CERT_FILE:
		g_value_set_string (value, priv->ssl_cert_file);
		break;
	case PROP_SSL_KEY_FILE:
		g_value_set_string (value, priv->ssl_key_file);
		break;
	case PROP_ASYNC_CONTEXT:
		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
		break;
	case PROP_RAW_PATHS:
		g_value_set_boolean (value, priv->raw_paths);
		break;
	case PROP_SERVER_HEADER:
		g_value_set_string (value, priv->server_header);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

Project: libsoup
File: soup-server.c

soup_server_get_port (SoupServer *server)
{
	g_return_val_if_fail (SOUP_IS_SERVER (server), 0);

	return SOUP_SERVER_GET_PRIVATE (server)->port;
}

Project: libsoup
File: soup-server.c

soup_client_context_get_address (SoupClientContext *client)
{
	g_return_val_if_fail (client != NULL, NULL);

	return soup_socket_get_remote_address (client->sock);
}

Project: libsoup
File: soup-server.c

soup_server_unpause_message (SoupServer *server,
			     SoupMessage *msg)
{
	g_return_if_fail (SOUP_IS_SERVER (server));
	g_return_if_fail (SOUP_IS_MESSAGE (msg));

	soup_message_io_unpause (msg);
}

Project: libsoup
File: soup-server.c

got_headers (SoupMessage *req, SoupClientContext *client)
{
	SoupServer *server = client->server;
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
	SoupURI *uri;
	SoupDate *date;
	char *date_string;
	SoupAuthDomain *domain;
	GSList *iter;
	gboolean rejected = FALSE;
	char *auth_user;

	if (!priv->raw_paths) {
		char *decoded_path;

		uri = soup_message_get_uri (req);
		decoded_path = soup_uri_decode (uri->path);

		if (strstr (decoded_path, "/../") ||
		    g_str_has_suffix (decoded_path, "/..")) {
			/* Introducing new ".." segments is not allowed */
			g_free (decoded_path);
			soup_message_set_status (req, SOUP_STATUS_BAD_REQUEST);
			return;
		}

		soup_uri_set_path (uri, decoded_path);
		g_free (decoded_path);
	}

	/* Add required response headers */
	date = soup_date_new_from_now (0);
	date_string = soup_date_to_string (date, SOUP_DATE_HTTP);
	soup_message_headers_replace (req->response_headers, "Date",
				      date_string);
	g_free (date_string);
	soup_date_free (date);
	
	/* Now handle authentication. (We do this here so that if
	 * the request uses "Expect: 100-continue", we can reject it
	 * immediately rather than waiting for the request body to
	 * be sent.
	 */
	for (iter = priv->auth_domains; iter; iter = iter->next) {
		domain = iter->data;

		if (soup_auth_domain_covers (domain, req)) {
			auth_user = soup_auth_domain_accepts (domain, req);
			if (auth_user) {
				client->auth_domain = g_object_ref (domain);
				client->auth_user = auth_user;
				return;
			}

			rejected = TRUE;
		}
	}

	/* If no auth domain rejected it, then it's ok. */
	if (!rejected)
		return;

	for (iter = priv->auth_domains; iter; iter = iter->next) {
		domain = iter->data;

		if (soup_auth_domain_covers (domain, req))
			soup_auth_domain_challenge (domain, req);
	}
}

Project: libsoup
File: soup-server.c

finalize (GObject *object)
{
	SoupServer *server = SOUP_SERVER (object);
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
	GSList *iter;

	if (priv->interface)
		g_object_unref (priv->interface);

	g_free (priv->ssl_cert_file);
	g_free (priv->ssl_key_file);
	if (priv->ssl_creds)
		soup_ssl_free_server_credentials (priv->ssl_creds);

	g_free (priv->server_header);

	if (priv->listen_sock)
		g_object_unref (priv->listen_sock);

	while (priv->clients) {
		SoupClientContext *client = priv->clients->data;
		SoupSocket *sock = g_object_ref (client->sock);

		priv->clients = g_slist_remove (priv->clients, client);

		if (client->msg) {
			soup_message_set_status (client->msg, SOUP_STATUS_IO_ERROR);
			soup_message_io_finished (client->msg);
		}

		soup_socket_disconnect (sock);
		g_object_unref (sock);
	}

	if (priv->default_handler)
		free_handler (priv->default_handler);
	soup_path_map_free (priv->handlers);

	for (iter = priv->auth_domains; iter; iter = iter->next)
		g_object_unref (iter->data);
	g_slist_free (priv->auth_domains);

	if (priv->loop)
		g_main_loop_unref (priv->loop);
	if (priv->async_context)
		g_main_context_unref (priv->async_context);

	G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
}

Project: libsoup
File: soup-server.c

soup_client_context_get_socket (SoupClientContext *client)
{
	g_return_val_if_fail (client != NULL, NULL);

	return client->sock;
}

Project: libsoup
File: soup-server.c

soup_server_get_async_context (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
	priv = SOUP_SERVER_GET_PRIVATE (server);

	return priv->async_context;
}

Project: libsoup
File: soup-server.c

soup_server_is_https (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
	priv = SOUP_SERVER_GET_PRIVATE (server);

	return (priv->ssl_cert_file && priv->ssl_key_file);
}

Project: libsoup
File: soup-server.c

unregister_handler (SoupServerHandler *handler)
{
	if (handler->destroy)
		handler->destroy (handler->user_data);
}

Project: libsoup
File: soup-server.c

soup_server_disconnect (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	soup_server_quit (server);

	if (priv->listen_sock) {
		soup_socket_disconnect (priv->listen_sock);
		g_object_unref (priv->listen_sock);
		priv->listen_sock = NULL;
	}
}

Project: libsoup
File: soup-server.c

soup_client_context_ref (SoupClientContext *client)
{
	client->ref_count++;
	return client;
}

Project: libsoup
File: soup-server.c

soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
{
	SoupServerPrivate *priv;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	priv->auth_domains = g_slist_remove (priv->auth_domains, auth_domain);
	g_object_unref (auth_domain);
}

Project: libsoup
File: soup-server.c

start_request (SoupServer *server, SoupClientContext *client)
{
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
	SoupMessage *msg;

	soup_client_context_cleanup (client);

	/* Listen for another request on this connection */
	msg = g_object_new (SOUP_TYPE_MESSAGE,
			    SOUP_MESSAGE_SERVER_SIDE, TRUE,
			    NULL);
	client->msg = msg;

	if (priv->server_header) {
		soup_message_headers_append (msg->response_headers, "Server",
					     priv->server_header);
	}

	g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), client);
	g_signal_connect (msg, "got_body", G_CALLBACK (call_handler), client);

	g_signal_emit (server, signals[REQUEST_STARTED], 0,
		       msg, client);

	g_object_ref (client->sock);
	soup_message_read_request (msg, client->sock,
				   request_finished, client);
}

Project: libsoup
File: soup-server.c

soup_client_context_get_auth_user (SoupClientContext *client)
{
	g_return_val_if_fail (client != NULL, NULL);

	return client->auth_user;
}

Project: libsoup
File: soup-server.c

soup_server_quit (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	g_signal_handlers_disconnect_by_func (priv->listen_sock,
					      G_CALLBACK (new_connection),
					      server);
	if (priv->loop)
		g_main_loop_quit (priv->loop);
}

Project: libsoup
File: soup-server.c

soup_server_get_handler (SoupServer *server, const char *path)
{
	SoupServerPrivate *priv;
	SoupServerHandler *hand;

	g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
	priv = SOUP_SERVER_GET_PRIVATE (server);

	if (path) {
		hand = soup_path_map_lookup (priv->handlers, path);
		if (hand)
			return hand;
		if (!strcmp (path, "*"))
			return NULL;
	}
	return priv->default_handler;
}

Project: libsoup
File: soup-server.c

soup_server_add_handler (SoupServer            *server,
			 const char            *path,
			 SoupServerCallback     callback,
			 gpointer               user_data,
			 GDestroyNotify         destroy)
{
	SoupServerPrivate *priv;
	SoupServerHandler *hand;

	g_return_if_fail (SOUP_IS_SERVER (server));
	g_return_if_fail (callback != NULL);
	priv = SOUP_SERVER_GET_PRIVATE (server);

	/* "" was never documented as meaning the same this as "/",
	 * but it effectively was. We have to special case it now or
	 * otherwise it would match "*" too.
	 */
	if (path && (!*path || !strcmp (path, "/")))
		path = NULL;

	hand = g_slice_new0 (SoupServerHandler);
	hand->path       = g_strdup (path);
	hand->callback   = callback;
	hand->destroy    = destroy;
	hand->user_data  = user_data;

	soup_server_remove_handler (server, path);
	if (path)
		soup_path_map_add (priv->handlers, path, hand);
	else
		priv->default_handler = hand;
}

Project: libsoup
File: soup-server.c

constructor (GType                  type,
	     guint                  n_construct_properties,
	     GObjectConstructParam *construct_properties)
{
	GObject *server;
	SoupServerPrivate *priv;

	server = G_OBJECT_CLASS (soup_server_parent_class)->constructor (
		type, n_construct_properties, construct_properties);
	if (!server)
		return NULL;
	priv = SOUP_SERVER_GET_PRIVATE (server);

	if (!priv->interface) {
		priv->interface =
			soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4,
					      priv->port);
	}

	if (priv->ssl_cert_file && priv->ssl_key_file) {
		priv->ssl_creds = soup_ssl_get_server_credentials (
			priv->ssl_cert_file,
			priv->ssl_key_file);
		if (!priv->ssl_creds) {
			g_object_unref (server);
			return NULL;
		}
	}

	priv->listen_sock =
		soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->interface,
				 SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
				 SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
				 NULL);
	if (!soup_socket_listen (priv->listen_sock)) {
		g_object_unref (server);
		return NULL;
	}

	/* Re-resolve the interface address, in particular in case
	 * the passed-in address had SOUP_ADDRESS_ANY_PORT.
	 */
	g_object_unref (priv->interface);
	priv->interface = soup_socket_get_local_address (priv->listen_sock);
	g_object_ref (priv->interface);
	priv->port = soup_address_get_port (priv->interface);

	return server;
}

Project: libsoup
File: soup-server.c

soup_client_context_cleanup (SoupClientContext *client)
{
	if (client->auth_domain) {
		g_object_unref (client->auth_domain);
		client->auth_domain = NULL;
	}
	if (client->auth_user) {
		g_free (client->auth_user);
		client->auth_user = NULL;
	}
	client->msg = NULL;
}

Project: libsoup
File: soup-server.c

socket_disconnected (SoupSocket *sock, SoupClientContext *client)
{
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (client->server);

	priv->clients = g_slist_remove (priv->clients, client);
	g_signal_handlers_disconnect_by_func (sock, socket_disconnected, client);
	g_object_unref (sock);
}

Project: libsoup
File: soup-server.c

soup_server_init (SoupServer *server)
{
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);

	priv->handlers = soup_path_map_new ((GDestroyNotify)free_handler);
}

Project: libsoup
File: soup-server.c

soup_client_context_get_auth_domain (SoupClientContext *client)
{
	g_return_val_if_fail (client != NULL, NULL);

	return client->auth_domain;
}

Project: libsoup
File: soup-server.c

soup_client_context_get_host (SoupClientContext *client)
{
	SoupAddress *address;

	address = soup_client_context_get_address (client);
	return soup_address_get_physical (address);
}

Project: libsoup
File: soup-server.c

G_DEFINE_TYPE (SoupServer, soup_server, G_TYPE_OBJECT)

enum {
	REQUEST_STARTED,
	REQUEST_READ,
	REQUEST_FINISHED,
	REQUEST_ABORTED,
	LAST_SIGNAL
};

Project: libsoup
File: soup-server.c

soup_server_run_async (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	if (!priv->listen_sock) {
		if (priv->loop) {
			g_main_loop_unref (priv->loop);
			priv->loop = NULL;
		}
		return;
	}

	g_signal_connect (priv->listen_sock, "new_connection",
			  G_CALLBACK (new_connection), server);

	return;

}

Project: libsoup
File: soup-server.c

request_finished (SoupMessage *msg, gpointer user_data)
{
	SoupClientContext *client = user_data;
	SoupServer *server = client->server;
	SoupSocket *sock = client->sock;

	soup_message_finished (msg);
	g_signal_emit (server,
		       msg->status_code == SOUP_STATUS_IO_ERROR ?
		       signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED],
		       0, msg, client);

	soup_client_context_cleanup (client);
	if (soup_socket_is_connected (sock) && soup_message_is_keepalive (msg)) {
		/* Start a new request */
		start_request (server, client);
	} else {
		soup_socket_disconnect (sock);
		soup_client_context_unref (client);
	}
	g_object_unref (msg);
	g_object_unref (sock);
}

Project: libsoup
File: soup-server.c

call_handler (SoupMessage *req, SoupClientContext *client)
{
	SoupServer *server = client->server;
	SoupServerHandler *hand;
	SoupURI *uri;

	g_signal_emit (server, signals[REQUEST_READ], 0, req, client);

	if (req->status_code != 0)
		return;

	uri = soup_message_get_uri (req);
	hand = soup_server_get_handler (server, uri->path);
	if (!hand) {
		soup_message_set_status (req, SOUP_STATUS_NOT_FOUND);
		return;
	}

	if (hand->callback) {
		GHashTable *form_data_set;

		if (uri->query)
			form_data_set = soup_form_decode (uri->query);
		else
			form_data_set = NULL;

		/* Call method handler */
		(*hand->callback) (server, req,
				   uri->path, form_data_set,
				   client, hand->user_data);

		if (form_data_set)
			g_hash_table_destroy (form_data_set);
	}
}

Project: libsoup
File: soup-server.c

soup_server_get_listener (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
	priv = SOUP_SERVER_GET_PRIVATE (server);

	return priv->listen_sock;
}

Project: libsoup
File: soup-server.c

soup_client_context_unref (SoupClientContext *client)
{
	if (--client->ref_count == 0) {
		soup_client_context_cleanup (client);
		g_slice_free (SoupClientContext, client);
	}
}

Project: libsoup
File: soup-server.c

soup_server_remove_handler (SoupServer *server, const char *path)
{
	SoupServerPrivate *priv;
	SoupServerHandler *hand;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	if (!path || !*path || !strcmp (path, "/")) {
		if (priv->default_handler) {
			unregister_handler (priv->default_handler);
			free_handler (priv->default_handler);
			priv->default_handler = NULL;
		}
		return;
	}

	hand = soup_path_map_lookup (priv->handlers, path);
	if (hand && !strcmp (path, hand->path)) {
		unregister_handler (hand);
		soup_path_map_remove (priv->handlers, path);
	}
}

Project: libsoup
File: soup-server.c

new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
{
	SoupServer *server = user_data;
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
	SoupClientContext *client;

	client = soup_client_context_new (server, g_object_ref (sock));
	priv->clients = g_slist_prepend (priv->clients, client);
	g_signal_connect (sock, "disconnected",
			  G_CALLBACK (socket_disconnected), client);
	start_request (server, client);
}

Project: libsoup
File: soup-server.c

soup_client_context_get_type (void)
{
	static volatile gsize type_volatile = 0;

	if (g_once_init_enter (&type_volatile)) {
		GType type = g_boxed_type_register_static (
			g_intern_static_string ("SoupClientContext"),
			(GBoxedCopyFunc) soup_client_context_ref,
			(GBoxedFreeFunc) soup_client_context_unref);
		g_once_init_leave (&type_volatile, type);
	}
	return type_volatile;
}

Project: libsoup
File: soup-server.c

free_handler (SoupServerHandler *hand)
{
	g_free (hand->path);
	g_slice_free (SoupServerHandler, hand);
}

Project: libsoup
File: soup-server.c

soup_client_context_new (SoupServer *server, SoupSocket *sock)
{
	SoupClientContext *client = g_slice_new0 (SoupClientContext);

	client->server = server;
	client->sock = sock;
	client->ref_count = 1;

	return client;
}

Project: libsoup
File: soup-server.c

set_property (GObject *object, guint prop_id,
	      const GValue *value, GParamSpec *pspec)
{
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (object);
	const char *header;

	switch (prop_id) {
	case PROP_PORT:
		priv->port = g_value_get_uint (value);
		break;
	case PROP_INTERFACE:
		if (priv->interface)
			g_object_unref (priv->interface);
		priv->interface = g_value_get_object (value);
		if (priv->interface)
			g_object_ref (priv->interface);
		break;
	case PROP_SSL_CERT_FILE:
		priv->ssl_cert_file =
			g_strdup (g_value_get_string (value));
		break;
	case PROP_SSL_KEY_FILE:
		priv->ssl_key_file =
			g_strdup (g_value_get_string (value));
		break;
	case PROP_ASYNC_CONTEXT:
		priv->async_context = g_value_get_pointer (value);
		if (priv->async_context)
			g_main_context_ref (priv->async_context);
		break;
	case PROP_RAW_PATHS:
		priv->raw_paths = g_value_get_boolean (value);
		break;
	case PROP_SERVER_HEADER:
		g_free (priv->server_header);
		header = g_value_get_string (value);
		if (!header)
			priv->server_header = NULL;
		else if (!*header) {
			priv->server_header =
				g_strdup (SOUP_SERVER_SERVER_HEADER_BASE);
		} else if (g_str_has_suffix (header, " ")) {
			priv->server_header =
				g_strdup_printf ("%s%s", header,
						 SOUP_SERVER_SERVER_HEADER_BASE);
		} else
			priv->server_header = g_strdup (header);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

Project: libsoup
File: soup-server.c

soup_server_pause_message (SoupServer *server,
			   SoupMessage *msg)
{
	g_return_if_fail (SOUP_IS_SERVER (server));
	g_return_if_fail (SOUP_IS_MESSAGE (msg));

	soup_message_io_pause (msg);
}

Project: libsoup
File: soup-server.c

soup_server_add_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
{
	SoupServerPrivate *priv;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	priv->auth_domains = g_slist_append (priv->auth_domains, auth_domain);
	g_object_ref (auth_domain);
}

Project: libsoup
File: soup-server.c

soup_server_new (const char *optname1, ...)
{
	SoupServer *server;
	va_list ap;

	va_start (ap, optname1);
	server = (SoupServer *)g_object_new_valist (SOUP_TYPE_SERVER,
						    optname1, ap);
	va_end (ap);

	return server;
}

Project: libsoup
File: soup-server.c

soup_server_class_init (SoupServerClass *server_class)
{
	GObjectClass *object_class = G_OBJECT_CLASS (server_class);

	g_type_class_add_private (server_class, sizeof (SoupServerPrivate));

	/* virtual method override */
	object_class->constructor = constructor;
	object_class->finalize = finalize;
	object_class->set_property = set_property;
	object_class->get_property = get_property;

	/* signals */

	/**
	 * SoupServer::request-started
	 * @server: the server
	 * @message: the new message
	 * @client: the client context
	 *
	 * Emitted when the server has started reading a new request.
	 * @message will be completely blank; not even the
	 * Request-Line will have been read yet. About the only thing
	 * you can usefully do with it is connect to its signals.
	 *
	 * If the request is read successfully, this will eventually
	 * be followed by a #SoupServer::request_read signal. If a
	 * response is then sent, the request processing will end with
	 * a #SoupServer::request_finished signal. If a network error
	 * occurs, the processing will instead end with
	 * #SoupServer::request_aborted.
	 **/
	signals[REQUEST_STARTED] =
		g_signal_new ("request-started",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (SoupServerClass, request_started),
			      NULL, NULL,
			      soup_marshal_NONE__OBJECT_POINTER,
			      G_TYPE_NONE, 2, 
			      SOUP_TYPE_MESSAGE,
			      SOUP_TYPE_CLIENT_CONTEXT);

	/**
	 * SoupServer::request-read
	 * @server: the server
	 * @message: the message
	 * @client: the client context
	 *
	 * Emitted when the server has successfully read a request.
	 * @message will have all of its request-side information
	 * filled in, and if the message was authenticated, @client
	 * will have information about that. This signal is emitted
	 * before any handlers are called for the message, and if it
	 * sets the message's #status_code, then normal handler
	 * processing will be skipped.
	 **/
	signals[REQUEST_READ] =
		g_signal_new ("request-read",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (SoupServerClass, request_read),
			      NULL, NULL,
			      soup_marshal_NONE__OBJECT_POINTER,
			      G_TYPE_NONE, 2,
			      SOUP_TYPE_MESSAGE,
			      SOUP_TYPE_CLIENT_CONTEXT);

	/**
	 * SoupServer::request-finished
	 * @server: the server
	 * @message: the message
	 * @client: the client context
	 *
	 * Emitted when the server has finished writing a response to
	 * a request.
	 **/
	signals[REQUEST_FINISHED] =
		g_signal_new ("request-finished",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (SoupServerClass, request_finished),
			      NULL, NULL,
			      soup_marshal_NONE__OBJECT_POINTER,
			      G_TYPE_NONE, 2,
			      SOUP_TYPE_MESSAGE,
			      SOUP_TYPE_CLIENT_CONTEXT);

	/**
	 * SoupServer::request-aborted
	 * @server: the server
	 * @message: the message
	 * @client: the client context
	 *
	 * Emitted when processing has failed for a message; this
	 * could mean either that it could not be read (if
	 * #SoupServer::request_read has not been emitted for it yet),
	 * or that the response could not be written back (if
	 * #SoupServer::request_read has been emitted but
	 * #SoupServer::request_finished has not been).
	 *
	 * @message is in an undefined state when this signal is
	 * emitted; the signal exists primarily to allow the server to
	 * free any state that it may have allocated in
	 * #SoupServer::request_started.
	 **/
	signals[REQUEST_ABORTED] =
		g_signal_new ("request-aborted",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (SoupServerClass, request_aborted),
			      NULL, NULL,
			      soup_marshal_NONE__OBJECT_POINTER,
			      G_TYPE_NONE, 2,
			      SOUP_TYPE_MESSAGE,
			      SOUP_TYPE_CLIENT_CONTEXT);

	/* properties */
	/**
	 * SOUP_SERVER_PORT:
	 *
	 * Alias for the #SoupServer:port property. (The port the
	 * server listens on.)
	 **/
	g_object_class_install_property (
		object_class, PROP_PORT,
		g_param_spec_uint (SOUP_SERVER_PORT,
				   "Port",
				   "Port to listen on",
				   0, 65536, 0,
				   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
	/**
	 * SOUP_SERVER_INTERFACE:
	 *
	 * Alias for the #SoupServer:interface property. (The address
	 * of the network interface the server listens on.)
	 **/
	g_object_class_install_property (
		object_class, PROP_INTERFACE,
		g_param_spec_object (SOUP_SERVER_INTERFACE,
				     "Interface",
				     "Address of interface to listen on",
				     SOUP_TYPE_ADDRESS,
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
	/**
	 * SOUP_SERVER_SSL_CERT_FILE:
	 *
	 * Alias for the #SoupServer:ssl-cert-file property. (The file
	 * containing the SSL certificate for the server.)
	 **/
	g_object_class_install_property (
		object_class, PROP_SSL_CERT_FILE,
		g_param_spec_string (SOUP_SERVER_SSL_CERT_FILE,
				     "SSL certificate file",
				     "File containing server SSL certificate",
				     NULL,
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
	/**
	 * SOUP_SERVER_SSL_KEY_FILE:
	 *
	 * Alias for the #SoupServer:ssl-key-file property. (The file
	 * containing the SSL certificate key for the server.)
	 **/
	g_object_class_install_property (
		object_class, PROP_SSL_KEY_FILE,
		g_param_spec_string (SOUP_SERVER_SSL_KEY_FILE,
				     "SSL key file",
				     "File containing server SSL key",
				     NULL,
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
	/**
	 * SOUP_SERVER_ASYNC_CONTEXT:
	 *
	 * Alias for the #SoupServer:async-context property. (The
	 * server's #GMainContext.)
	 **/
	g_object_class_install_property (
		object_class, PROP_ASYNC_CONTEXT,
		g_param_spec_pointer (SOUP_SERVER_ASYNC_CONTEXT,
				      "Async GMainContext",
				      "The GMainContext to dispatch async I/O in",
				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
	/**
	 * SOUP_SERVER_RAW_PATHS:
	 *
	 * Alias for the #SoupServer:raw-paths property. (If %TRUE,
	 * percent-encoding in the Request-URI path will not be
	 * automatically decoded.)
	 **/
	g_object_class_install_property (
		object_class, PROP_RAW_PATHS,
		g_param_spec_boolean (SOUP_SERVER_RAW_PATHS,
				      "Raw paths",
				      "If %TRUE, percent-encoding in the Request-URI path will not be automatically decoded.",
				      FALSE,
				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	/**
	 * SoupServer:server-header:
	 *
	 * If non-%NULL, the value to use for the "Server" header on
	 * #SoupMessage<!-- -->s processed by this server.
	 *
	 * The Server header is the server equivalent of the
	 * User-Agent header, and provides information about the
	 * server and its components. It contains a list of one or
	 * more product tokens, separated by whitespace, with the most
	 * significant product token coming first. The tokens must be
	 * brief, ASCII, and mostly alphanumeric (although "-", "_",
	 * and "." are also allowed), and may optionally include a "/"
	 * followed by a version string. You may also put comments,
	 * enclosed in parentheses, between or after the tokens.
	 *
	 * Some HTTP server implementations intentionally do not use
	 * version numbers in their Server header, so that
	 * installations running older versions of the server don't
	 * end up advertising their vulnerability to specific security
	 * holes.
	 *
	 * As with #SoupSession:user_agent, if you set a
	 * %server_header property that has trailing whitespace,
	 * #SoupServer will append its own product token (eg,
	 * "<literal>libsoup/2.3.2</literal>") to the end of the
	 * header for you.
	 **/
	/**
	 * SOUP_SERVER_SERVER_HEADER:
	 *
	 * Alias for the #SoupServer:server-header property, qv.
	 **/
	g_object_class_install_property (
		object_class, PROP_SERVER_HEADER,
		g_param_spec_string (SOUP_SERVER_SERVER_HEADER,
				     "Server header",
				     "Server header",
				     NULL,
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
}

Project: libsoup
File: soup-server.c

soup_server_run (SoupServer *server)
{
	SoupServerPrivate *priv;

	g_return_if_fail (SOUP_IS_SERVER (server));
	priv = SOUP_SERVER_GET_PRIVATE (server);

	if (!priv->loop) {
		priv->loop = g_main_loop_new (priv->async_context, TRUE);
		soup_server_run_async (server);
	}

	if (priv->loop)
		g_main_loop_run (priv->loop);
}