# HG changeset patch
# Node ID cff4990fcf3c
# Parent  ce9e6617890a
diff --git a/traversity/meson.build b/traversity/meson.build
--- a/traversity/meson.build
+++ b/traversity/meson.build
@@ -40,10 +40,15 @@
 
 TRAVERSITY_ALL_SOURCES = TRAVERSITY_SOURCES + TRAVERSITY_HEADERS + TRAVERSITY_BUILT_SOURCES + TRAVERSITY_BUILT_HEADERS
 
+TRAVERSITY_CFLAGS = [
+	'-DTRAVERSITY_COMPILATION',
+	'-DTRAVERSITY_RESOURCE_PATH="/org/imfreedom/keep/traversity/traversity"',
+]
+
 libtraversity = library('traversity',
 	TRAVERSITY_ALL_SOURCES,
 	dependencies : [GLIB, GOBJECT, TEMPLATE_GLIB],
-	c_args : ['-DTRAVERSITY_COMPILATION'],
+	c_args : TRAVERSITY_CFLAGS,
 	version : LIBTRAVERSITY_VERSION,
 	include_directories : [toplevel_inc, traversity_inc],
 )
diff --git a/traversity/traversityupnpdiscoverer.h b/traversity/traversityupnpdiscoverer.h
--- a/traversity/traversityupnpdiscoverer.h
+++ b/traversity/traversityupnpdiscoverer.h
@@ -12,11 +12,67 @@
 
 G_BEGIN_DECLS
 
+/**
+ * TraversityUpnpDiscoverer:
+ *
+ * A discoverer that support Universal Plug and Play.
+ *
+ * Since: 1.0.0
+ */
+
 #define TRAVERSITY_TYPE_UPNP_DISCOVERER (traversity_upnp_discoverer_get_type())
-G_DECLARE_FINAL_TYPE(TraversityUpnpDiscoverer, traversity_upnp_discoverer, TRAVERSITY,
-                     UPNP_DISCOVERER, TraversityDiscoverer)
+G_DECLARE_FINAL_TYPE(TraversityUpnpDiscoverer, traversity_upnp_discoverer,
+                     TRAVERSITY, UPNP_DISCOVERER, TraversityDiscoverer)
+
+/**
+ * traversity_upnp_discoverer_new:
+ *
+ * Creates a new [class@Traversity.Discoverer] that supports Universal Plug and
+ * Play.
+ *
+ * Returns: (transfer full): The new discoverer.
+ *
+ * Since: 1.0.0
+ */
+TraversityDiscoverer *traversity_upnp_discoverer_new(void);
 
-TraversityDiscoverer *traversity_upnp_discoverer_new(void);
+/**
+ * traversity_upnp_discoverer_new_custom:
+ * @host_address: The custom hostname or IP address to use.
+ * @host_port: The custom UDP port number to use.
+ *
+ * Creates a new [class@Traversity.Discoverer] that supports Universal Plug and
+ * Play with the given @host_address and @host_port.
+ *
+ * Returns: (transfer full): The new discoverer.
+ *
+ * Since: 1.0.0
+ */
+TraversityDiscoverer *traversity_upnp_discoverer_new_custom(const char *host_address, int host_port);
+
+/**
+ * traversity_upnp_discoverer_get_host_address:
+ * @upnp: The instance.
+ *
+ * Gets the host address that this discoverer is using.
+ *
+ * Returns: The host address.
+ *
+ * Since: 1.0.0
+ */
+const char *traversity_upnp_discoverer_get_host_address(TraversityUpnpDiscoverer *upnp);
+
+/**
+ * traversity_upnp_discoverer_get_host_port:
+ * @upnp: The instance.
+ *
+ * Gets the host port that this discoverer is using.
+ *
+ * Returns: The host port.
+ *
+ * Since: 1.0.0
+ */
+int traversity_upnp_discoverer_get_host_port(TraversityUpnpDiscoverer *upnp);
 
 G_END_DECLS
 
diff --git a/traversity/traversityupnpdiscoverer.c b/traversity/traversityupnpdiscoverer.c
--- a/traversity/traversityupnpdiscoverer.c
+++ b/traversity/traversityupnpdiscoverer.c
@@ -10,31 +10,62 @@
 #define TRAVERSITY_UPNP_DISCOVER_WAN_IP_SERVICE "WANIPConnection:1"
 #define TRAVERSITY_UPNP_DISCOVER_WAN_PPP_SERVICE "WANPPPConnection:1"
 
+enum {
+	PROP_0,
+	PROP_HOST_ADDRESS,
+	PROP_HOST_PORT,
+	N_PROPERTIES
+};
+static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+
 struct _TraversityUpnpDiscoverer {
 	TraversityDiscoverer parent;
 
+	char *host_address;
+	int host_port;
+
 	GSocket *socket;
 	GSocketAddress *address;
 
 	guint source;
 };
 
-G_DEFINE_TYPE(TraversityUpnpDiscoverer, traversity_upnp_discoverer, TRAVERSITY_TYPE_DISCOVERER)
+G_DEFINE_TYPE(TraversityUpnpDiscoverer, traversity_upnp_discoverer,
+              TRAVERSITY_TYPE_DISCOVERER)
 
 /******************************************************************************
  * Helpers
  *****************************************************************************/
-static gchar *
+static void
+traversity_upnp_discoverer_set_host_address(TraversityUpnpDiscoverer *upnp,
+                                            const char *host_address)
+{
+	g_free(upnp->host_address);
+	upnp->host_address = g_strdup(host_address);
+
+	g_object_notify_by_pspec(G_OBJECT(upnp), properties[PROP_HOST_ADDRESS]);
+}
+
+static void
+traversity_upnp_discoverer_set_host_port(TraversityUpnpDiscoverer *upnp,
+                                         int host_port)
+{
+	upnp->host_port = host_port;
+
+	g_object_notify_by_pspec(G_OBJECT(upnp), properties[PROP_HOST_PORT]);
+}
+
+static char *
 traversity_upnp_build_search_request(TraversityUpnpDiscoverer *upnp,
-                               const gchar *service_type,
-                               GError **error)
+                                     const char *service_type,
+                                     GError **error)
 {
 	TmplScope *scope = NULL;
 	TmplSymbol *symbol = NULL;
 	TmplTemplate *template = NULL;
 	gboolean success = FALSE;
-	gchar *ret = NULL;
-	const gchar *path = "/org/imfreedom/keep/traversity/traversity/upnp/search-request";
+	char *ret = NULL;
+	const char *path = TRAVERSITY_RESOURCE_PATH "/upnp/search-request";
 
 	template = tmpl_template_new(NULL);
 	success = tmpl_template_parse_resource(template, path, NULL, error);
@@ -45,10 +76,10 @@
 	scope = tmpl_scope_new();
 
 	symbol = tmpl_scope_get(scope, "httpmu_host_address");
-	tmpl_symbol_assign_string(symbol, TRAVERSITY_UPNP_DISCOVERER_HTTPMU_HOST_ADDRESS);
+	tmpl_symbol_assign_string(symbol, upnp->host_address);
 
 	symbol = tmpl_scope_get(scope, "httpmu_host_port");
-	tmpl_symbol_assign_string(symbol, G_STRINGIFY(TRAVERSITY_UPNP_DISCOVERER_HTTPMU_HOST_PORT));
+	tmpl_symbol_assign_double(symbol, upnp->host_port);
 
 	symbol = tmpl_scope_get(scope, "service_type");
 	tmpl_symbol_assign_string(symbol, service_type);
@@ -61,12 +92,13 @@
 }
 
 static void
-traversity_upnp_discoverer_udp_read(GSocket *socket, GIOCondition condition,
-                              gpointer data)
+traversity_upnp_discoverer_udp_read(GSocket *socket,
+                                    G_GNUC_UNUSED GIOCondition condition,
+                                    gpointer data)
 {
 	TraversityUpnpDiscoverer *upnp = data;
 	GError *error = NULL;
-	gchar buffer[65536];
+	char buffer[65536];
 	gssize length;
 
 	length = g_socket_receive(upnp->socket, buffer, sizeof(buffer) - 1, NULL,
@@ -83,19 +115,23 @@
 }
 
 static void
-traversity_upnp_discoverer_broadcast(TraversityUpnpDiscoverer *upnp, GTask *task) {
+traversity_upnp_discoverer_broadcast(TraversityUpnpDiscoverer *upnp,
+                                     GTask *task)
+{
 	GError *error = NULL;
 	GSource *source = NULL;
-	gchar *body = NULL;
+	char *body = NULL;
+	const char *service = TRAVERSITY_UPNP_DISCOVER_WAN_IP_SERVICE;
 	gssize sent;
 	gsize length;
 
-	body = traversity_upnp_build_search_request(upnp, TRAVERSITY_UPNP_DISCOVER_WAN_IP_SERVICE, &error);
+	body = traversity_upnp_build_search_request(upnp, service, &error);
 	length = strlen(body);
 
-	g_message("sending body (%d)\n%s", length, body);
+	g_message("sending body (%ld)\n%s", length, body);
 
-	sent = g_socket_send_to(upnp->socket, upnp->address, body, length, NULL, &error);
+	sent = g_socket_send_to(upnp->socket, upnp->address, body, length, NULL,
+	                        &error);
 	if(error != NULL) {
 		g_task_return_error(task, error);
 	}
@@ -115,9 +151,9 @@
  *****************************************************************************/
 static void
 traversity_upnp_discoverer_discover_async(TraversityDiscoverer *discoverer,
-                                    GCancellable *cancellable,
-                                    GAsyncReadyCallback callback,
-                                    gpointer data)
+                                          GCancellable *cancellable,
+                                          GAsyncReadyCallback callback,
+                                          gpointer data)
 {
 	TraversityUpnpDiscoverer *upnp = NULL;
 	GError *error = NULL;
@@ -149,16 +185,16 @@
 		}
 	}
 
-	upnp->address = g_inet_socket_address_new_from_string(TRAVERSITY_UPNP_DISCOVERER_HTTPMU_HOST_ADDRESS,
-	                                                      TRAVERSITY_UPNP_DISCOVERER_HTTPMU_HOST_PORT);
+	upnp->address = g_inet_socket_address_new_from_string(upnp->host_address,
+	                                                      upnp->host_port);
 
 	traversity_upnp_discoverer_broadcast(upnp, task);
 }
 
 static gboolean
 traversity_upnp_discoverer_discover_finish(TraversityDiscoverer *discoverer,
-                                     GAsyncResult *result,
-                                     GError **error)
+                                           GAsyncResult *result,
+                                           GError **error)
 {
 	return FALSE;
 }
@@ -167,6 +203,48 @@
  * GObject Implementation
  *****************************************************************************/
 static void
+traversity_upnp_discoverer_get_property(GObject *obj, guint param_id,
+                                        GValue *value, GParamSpec *pspec)
+{
+	TraversityUpnpDiscoverer *upnp = TRAVERSITY_UPNP_DISCOVERER(obj);
+
+	switch(param_id) {
+		case PROP_HOST_ADDRESS:
+			g_value_set_string(value,
+			                   traversity_upnp_discoverer_get_host_address(upnp));
+			break;
+		case PROP_HOST_PORT:
+			g_value_set_int(value,
+			                traversity_upnp_discoverer_get_host_port(upnp));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+traversity_upnp_discoverer_set_property(GObject *obj, guint param_id,
+                                        const GValue *value, GParamSpec *pspec)
+{
+	TraversityUpnpDiscoverer *upnp = TRAVERSITY_UPNP_DISCOVERER(obj);
+
+	switch(param_id) {
+		case PROP_HOST_ADDRESS:
+			traversity_upnp_discoverer_set_host_address(upnp,
+			                                            g_value_get_string(value));
+			break;
+		case PROP_HOST_PORT:
+			traversity_upnp_discoverer_set_host_port(upnp,
+			                                         g_value_get_int(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
 traversity_upnp_discoverer_dispose(GObject *obj) {
 	TraversityUpnpDiscoverer *discoverer = TRAVERSITY_UPNP_DISCOVERER(obj);
 
@@ -176,17 +254,96 @@
 }
 
 static void
-traversity_upnp_discoverer_init(TraversityUpnpDiscoverer *discoverer) {
+traversity_upnp_discoverer_finalize(GObject *obj) {
+	TraversityUpnpDiscoverer *discoverer = TRAVERSITY_UPNP_DISCOVERER(obj);
+
+	g_clear_pointer(&discoverer->host_address, g_free);
+
+	G_OBJECT_CLASS(traversity_upnp_discoverer_parent_class)->finalize(obj);
+}
+
+static void
+traversity_upnp_discoverer_init(G_GNUC_UNUSED TraversityUpnpDiscoverer *discoverer)
+{
 }
 
 static void
 traversity_upnp_discoverer_class_init(TraversityUpnpDiscovererClass *klass) {
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-	TraversityDiscovererClass *discoverer_class = TRAVERSITY_DISCOVERER_CLASS(klass);
+	GObjectClass *obj_class = NULL;
+	TraversityDiscovererClass *discoverer_class = NULL;
 
+	obj_class = G_OBJECT_CLASS(klass);
+	obj_class->get_property = traversity_upnp_discoverer_get_property;
+	obj_class->set_property = traversity_upnp_discoverer_set_property;
 	obj_class->dispose = traversity_upnp_discoverer_dispose;
+	obj_class->finalize = traversity_upnp_discoverer_finalize;
 
+	discoverer_class = TRAVERSITY_DISCOVERER_CLASS(klass);
 	discoverer_class->discover_async = traversity_upnp_discoverer_discover_async;
 	discoverer_class->discover_finish = traversity_upnp_discoverer_discover_finish;
+
+	/**
+	 * TraversityUpnpDiscoverer:host-address:
+	 *
+	 * The address of the host to look for.
+	 *
+	 * This defaults to 239.255.255.250 which is the address to use per the
+	 * specification.
+	 *
+	 * Since: 1.0.0
+	 */
+	properties[PROP_HOST_ADDRESS] = g_param_spec_string(
+		"host-address", "host-address",
+		"The host address to use.",
+		TRAVERSITY_UPNP_DISCOVERER_HTTPMU_HOST_ADDRESS,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * TraversityUpnpDiscoverer:host-port:
+	 *
+	 * The UDP port number to use.
+	 *
+	 * This defaults to 1900 per the specification.
+	 *
+	 * Since: 1.0.0
+	 */
+	properties[PROP_HOST_PORT] = g_param_spec_int(
+		"host-port", "host-port",
+		"The host port to use.",
+		0, 65535, TRAVERSITY_UPNP_DISCOVERER_HTTPMU_HOST_PORT,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
 }
 
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+TraversityDiscoverer *
+traversity_upnp_discoverer_new(void) {
+	return g_object_new(TRAVERSITY_TYPE_DISCOVERER, NULL);
+}
+
+TraversityDiscoverer *
+traversity_upnp_discoverer_new_custom(char *host_address, int host_port) {
+	return g_object_new(
+		TRAVERSITY_TYPE_DISCOVERER,
+		"host-address", host_address,
+		"host-port", host_port,
+		NULL);
+}
+
+const char *
+traversity_upnp_discoverer_get_host_address(TraversityUpnpDiscoverer *upnp) {
+	g_return_val_if_fail(TRAVERSITY_IS_UPNP_DISCOVERER(upnp), NULL);
+
+	return upnp->host_address;
+}
+
+int
+traversity_upnp_discoverer_get_host_port(TraversityUpnpDiscoverer *upnp) {
+	g_return_val_if_fail(TRAVERSITY_IS_UPNP_DISCOVERER(upnp), 0);
+
+	return upnp->host_port;
+}
+
