Partial GSSDPServiceGroup implementation
authorJorn Baayen <jorn@openedhand.com>
Thu, 27 Apr 2006 13:38:43 +0000 (13:38 +0000)
committerJorn Baayen <jorn@openedhand.com>
Thu, 27 Apr 2006 13:38:43 +0000 (13:38 +0000)
git-svn-id: https://svn.o-hand.com/repos/gupnp/gssdp@134 d8cb91d7-bff9-0310-92b9-80b65e4482b2

libgssdp/Makefile.am
libgssdp/gssdp-protocol.h
libgssdp/gssdp-service-browser.c
libgssdp/gssdp-service-group.c [new file with mode: 0644]
libgssdp/gssdp-service-group.h [new file with mode: 0644]
libgssdp/gssdp.h

index f93b95a..de6c8cc 100644 (file)
@@ -7,6 +7,7 @@ lib_LTLIBRARIES = libgssdp-1.0.la
 libgssdpinc_HEADERS =  gssdp-client.h           \
                        gssdp-error.h            \
                        gssdp-service-browser.h  \
+                       gssdp-service-group.h    \
                        gssdp.h
 
 gssdp-marshal.c: gssdp-marshal.list
@@ -22,6 +23,7 @@ libgssdp_1_0_la_SOURCES = gssdp-client.c              \
                          gssdp-error.c                 \
                          gssdp-protocol.h              \
                          gssdp-service-browser.c       \
+                         gssdp-service-group.c         \
                          gssdp-socket-source.c         \
                          gssdp-socket-source.h         \
                          soup-headers.c                \
index c4eb857..e0ccb3c 100644 (file)
@@ -35,6 +35,16 @@ G_BEGIN_DECLS
         "ST: %s\r\n"                                \
         "MX: %d\r\n\r\n"
 
+#define SSDP_DISCOVERY_RESPONSE                     \
+        "HTTP/1.1 200 OK\r\n"                       \
+        "Location: %s\r\n"                          \
+        "Ext:\r\n"                                  \
+        "USN: %s\r\n"                               \
+        "Server: %s\r\n"                            \
+        "Cache-Control: max-age=%d\r\n"             \
+        "ST: %s\r\n"                                \
+        "Content-Length: 0\r\n\r\n"
+
 #define SSDP_SEARCH_METHOD "M-SEARCH"
 #define GENA_NOTIFY_METHOD "NOTIFY"
 
@@ -43,6 +53,8 @@ G_BEGIN_DECLS
 
 #define SSDP_MIN_MAX_AGE 1800
 
+#define SSDP_DEFAULT_MX 3
+
 G_END_DECLS
 
 #endif /* __GSSDP_PROTOCOL_H__ */
index b74dd37..94b2a0c 100644 (file)
@@ -34,9 +34,6 @@
 #include "gssdp-protocol.h"
 #include "gssdp-marshal.h"
 
-/* An MX of 3 seconds by default */
-#define DEFAULT_MX 3
-
 G_DEFINE_TYPE (GSSDPServiceBrowser,
                gssdp_service_browser,
                G_TYPE_OBJECT);
@@ -100,7 +97,7 @@ gssdp_service_browser_init (GSSDPServiceBrowser *service_browser)
                                          GSSDP_TYPE_SERVICE_BROWSER,
                                          GSSDPServiceBrowserPrivate);
 
-        service_browser->priv->mx = DEFAULT_MX;
+        service_browser->priv->mx = SSDP_DEFAULT_MX;
 
         service_browser->priv->services = g_hash_table_new_full (g_str_hash,
                                                                  g_str_equal,
@@ -262,7 +259,7 @@ gssdp_service_browser_class_init (GSSDPServiceBrowserClass *klass)
                           "other parties to respond.",
                           1,
                           G_MAXUSHORT,
-                          DEFAULT_MX,
+                          SSDP_DEFAULT_MX,
                           G_PARAM_READWRITE |
                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
                           G_PARAM_STATIC_BLURB));
@@ -307,7 +304,7 @@ gssdp_service_browser_class_init (GSSDPServiceBrowserClass *klass)
 
 /**
  * gssdp_service_browser_new
- * @main_context: The #GMainContext to associate with
+ * @client: The #GSSDPClient to associate with
  * @error: A location to return an error of type #GSSDP_ERROR_QUARK
  *
  * Return value: A new #GSSDPServiceBrowser object.
diff --git a/libgssdp/gssdp-service-group.c b/libgssdp/gssdp-service-group.c
new file mode 100644 (file)
index 0000000..4240c2e
--- /dev/null
@@ -0,0 +1,600 @@
+/* 
+ * Copyright (C) 2006 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn@openedhand.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "gssdp-service-group.h"
+#include "gssdp-error.h"
+#include "gssdp-client-private.h"
+#include "gssdp-protocol.h"
+
+G_DEFINE_TYPE (GSSDPServiceGroup,
+               gssdp_service_group,
+               G_TYPE_OBJECT);
+
+struct _GSSDPServiceGroupPrivate {
+        GSSDPClient *client;
+
+        guint        max_age;
+
+        gboolean     available;
+
+        GList       *services;
+
+        gulong       message_received_id;
+
+        guint        last_service_id;
+};
+
+enum {
+        PROP_0,
+        PROP_CLIENT,
+        PROP_MAX_AGE,
+        PROP_AVAILABLE
+};
+
+typedef struct {
+        char  *target;
+        char  *usn;
+        GList *locations;
+
+        guint  id;
+} Service;
+
+/* Function prototypes */
+static void
+gssdp_service_group_set_client (GSSDPServiceGroup *service_group,
+                                GSSDPClient         *client);
+static void
+message_received_cb            (GSSDPClient         *client,
+                                const char          *from_ip,
+                                _GSSDPMessageType    type,
+                                GHashTable          *headers,
+                                gpointer             user_data);
+static void
+service_free                   (Service             *service);
+static void
+send_discovery_response        (GSSDPServiceGroup   *service_group,
+                                const char          *dest_ip,
+                                Service             *service);
+
+static void
+gssdp_service_group_init (GSSDPServiceGroup *service_group)
+{
+        service_group->priv = G_TYPE_INSTANCE_GET_PRIVATE
+                                        (service_group,
+                                         GSSDP_TYPE_SERVICE_GROUP,
+                                         GSSDPServiceGroupPrivate);
+
+        service_group->priv->max_age = SSDP_MIN_MAX_AGE;
+}
+
+static void
+gssdp_service_group_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+        GSSDPServiceGroup *service_group;
+
+        service_group = GSSDP_SERVICE_GROUP (object);
+
+        switch (property_id) {
+        case PROP_CLIENT:
+                g_value_set_object
+                        (value,
+                         gssdp_service_group_get_client (service_group));
+                break;
+        case PROP_MAX_AGE:
+                g_value_set_uint
+                        (value,
+                         gssdp_service_group_get_max_age (service_group));
+                break;
+        case PROP_AVAILABLE:
+                g_value_set_boolean
+                        (value,
+                         gssdp_service_group_get_available (service_group));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gssdp_service_group_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+        GSSDPServiceGroup *service_group;
+
+        service_group = GSSDP_SERVICE_GROUP (object);
+
+        switch (property_id) {
+        case PROP_CLIENT:
+                gssdp_service_group_set_client (service_group,
+                                                g_value_get_object (value));
+                break;
+        case PROP_MAX_AGE:
+                gssdp_service_group_set_max_age (service_group,
+                                                 g_value_get_long (value));
+                break;
+        case PROP_AVAILABLE:
+                gssdp_service_group_set_available (service_group,
+                                                   g_value_get_boolean (value),
+                                                   NULL);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                break;
+        }
+}
+
+static void
+gssdp_service_group_dispose (GObject *object)
+{
+        GSSDPServiceGroup *service_group;
+
+        service_group = GSSDP_SERVICE_GROUP (object);
+
+        if (service_group->priv->client) {
+                if (g_signal_handler_is_connected
+                        (service_group->priv->client,
+                         service_group->priv->message_received_id)) {
+                        g_signal_handler_disconnect
+                                (service_group->priv->client,
+                                 service_group->priv->message_received_id);
+                }
+                                                   
+                g_object_unref (service_group->priv->client);
+                service_group->priv->client = NULL;
+        }
+}
+
+static void
+gssdp_service_group_finalize (GObject *object)
+{
+        GSSDPServiceGroup *service_group;
+
+        service_group = GSSDP_SERVICE_GROUP (object);
+
+        while (service_group->priv->services) {
+                service_free (service_group->priv->services->data);
+                service_group->priv->services =
+                        g_list_delete_link (service_group->priv->services,
+                                            service_group->priv->services);
+        }
+}
+
+static void
+gssdp_service_group_class_init (GSSDPServiceGroupClass *klass)
+{
+        GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+
+       object_class->set_property = gssdp_service_group_set_property;
+       object_class->get_property = gssdp_service_group_get_property;
+       object_class->dispose      = gssdp_service_group_dispose;
+       object_class->finalize     = gssdp_service_group_finalize;
+
+        g_type_class_add_private (klass, sizeof (GSSDPServiceGroupPrivate));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_CLIENT,
+                 g_param_spec_object
+                         ("client",
+                          "Client",
+                          "The associated client.",
+                          GSSDP_TYPE_CLIENT,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_BLURB));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_MAX_AGE,
+                 g_param_spec_uint
+                         ("max-age",
+                          "Max age",
+                          "The number of seconds advertisements are valid.",
+                          SSDP_MIN_MAX_AGE,
+                          G_MAXUINT,
+                          SSDP_MIN_MAX_AGE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_BLURB));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_AVAILABLE,
+                 g_param_spec_boolean
+                         ("available",
+                          "Available",
+                          "TRUE if the services are available (advertised).",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_BLURB));
+}
+
+/**
+ * gssdp_service_group_new
+ * @client: The #GSSDPClient to associate with
+ * @error: A location to return an error of type #GSSDP_ERROR_QUARK
+ *
+ * Return value: A new #GSSDPServiceGroup object.
+ **/
+GSSDPServiceGroup *
+gssdp_service_group_new (GSSDPClient *client)
+{
+        return g_object_new (GSSDP_TYPE_SERVICE_GROUP,
+                             "client", client,
+                             NULL);
+}
+
+/**
+ * Sets the #GSSDPClient @service_group is associated with to @client
+ **/
+static void
+gssdp_service_group_set_client (GSSDPServiceGroup *service_group,
+                                GSSDPClient         *client)
+{
+        g_return_if_fail (GSSDP_IS_SERVICE_GROUP (service_group));
+        g_return_if_fail (GSSDP_IS_CLIENT (client));
+
+        service_group->priv->client = g_object_ref (client);
+
+        service_group->priv->message_received_id =
+                g_signal_connect_object (service_group->priv->client,
+                                         "message-received",
+                                         G_CALLBACK (message_received_cb),
+                                         service_group,
+                                         0);
+
+        g_object_notify (G_OBJECT (service_group), "client");
+}
+
+/**
+ * gssdp_service_group_get_client
+ * @service_group: A #GSSDPServiceGroup
+ *
+ * Return value: The #GSSDPClient @service_group is associated with.
+ **/
+GSSDPClient *
+gssdp_service_group_get_client (GSSDPServiceGroup *service_group)
+{
+        g_return_val_if_fail (GSSDP_IS_SERVICE_GROUP (service_group), NULL);
+
+        return service_group->priv->client;
+}
+
+/**
+ * gssdp_service_group_set_max_age
+ * @service_group: A #GSSDPServiceGroup
+ * @mx: The number of seconds advertisements are valid
+ *
+ * Sets the number of seconds advertisements are valid to @max_age.
+ **/
+void
+gssdp_service_group_set_max_age (GSSDPServiceGroup *service_group,
+                                 guint              max_age)
+{
+        g_return_if_fail (GSSDP_IS_SERVICE_GROUP (service_group));
+
+        if (service_group->priv->max_age == max_age)
+                return;
+
+        service_group->priv->max_age = max_age;
+        
+        g_object_notify (G_OBJECT (service_group), "max-age");
+}
+
+/**
+ * gssdp_service_group_get_max_age
+ * @service_group: A #GSSDPServiceGroup
+ *
+ * Return value: The number of seconds advertisements are valid.
+ **/
+guint
+gssdp_service_group_get_max_age (GSSDPServiceGroup *service_group)
+{
+        g_return_val_if_fail (GSSDP_IS_SERVICE_GROUP (service_group), 0);
+
+        return service_group->priv->max_age;
+}
+
+/**
+ * gssdp_service_group_set_available
+ * @service_group: A #GSSDPServiceGroup
+ * @available: TRUE if @service_group should be available (advertised)
+ * @error: A location to return an error of type #GSSDP_ERROR_QUARK
+ *
+ * Sets @service_group<!-- -->s availability to @available.
+ *
+ * Return value: TRUE if the call succeeded.
+ **/
+gboolean
+gssdp_service_group_set_available (GSSDPServiceGroup *service_group,
+                                   gboolean           available,
+                                   GError           **error)
+{
+        g_return_val_if_fail (GSSDP_IS_SERVICE_GROUP (service_group), FALSE);
+
+        if (service_group->priv->available == available)
+                return TRUE;
+
+        /* XXX */
+        if (available) {
+        }
+
+        service_group->priv->available = available;
+        
+        g_object_notify (G_OBJECT (service_group), "available");
+
+        return TRUE;
+}
+
+/**
+ * gssdp_service_group_get_available
+ * @service_group: A #GSSDPServiceGroup
+ *
+ * Return value: TRUE if @service_group is available (advertised).
+ **/
+gboolean
+gssdp_service_group_get_available (GSSDPServiceGroup *service_group)
+{
+        g_return_val_if_fail (GSSDP_IS_SERVICE_GROUP (service_group), FALSE);
+
+        return service_group->priv->available;
+}
+
+/**
+ * gssdp_service_group_add_service
+ * @service_group: An @GSSDPServiceGroup
+ * @target: The service's target
+ * @usn: The service's USN
+ * @locations: A #GList of the service's locations
+ *
+ * Adds a service with target @target, USN @usn, and locations @locations
+ * to @service_group.
+ *
+ * Return value: The ID of the added service.
+ **/
+guint
+gssdp_service_group_add_service (GSSDPServiceGroup *service_group,
+                                 const char        *target,
+                                 const char        *usn,
+                                 GList             *locations)
+{
+        Service *service;
+        GList *l;
+
+        g_return_val_if_fail (GSSDP_IS_SERVICE_GROUP (service_group), 0);
+        g_return_val_if_fail (target != NULL, 0);
+        g_return_val_if_fail (usn != NULL, 0);
+        g_return_val_if_fail (locations != NULL, 0);
+        g_return_val_if_fail (!service_group->priv->available, 0);
+
+        service = g_slice_new0 (Service);
+
+        service->target = g_strdup (target);
+        service->usn    = g_strdup (usn);
+
+        for (l = locations; l; l = l->next) {
+                service->locations = g_list_append (service->locations,
+                                                    g_strdup (l->data));
+        }
+
+        service_group->priv->services =
+                g_list_prepend (service_group->priv->services, service);
+
+        service->id = ++service_group->priv->last_service_id;
+
+        return service->id;
+}
+
+/**
+ * gssdp_service_group_add_service_simple
+ * @service_group: An @GSSDPServiceGroup
+ * @target: The service's target
+ * @usn: The service's USN
+ * @location: The service's location
+ *
+ * Adds a service with target @target, USN @usn, and location @location
+ * to @service_group.
+ *
+ * Return value: The ID of the added service.
+ **/
+guint
+gssdp_service_group_add_service_simple (GSSDPServiceGroup *service_group,
+                                        const char        *target,
+                                        const char        *usn,
+                                        const char        *location)
+{
+        Service *service;
+
+        g_return_val_if_fail (GSSDP_IS_SERVICE_GROUP (service_group), 0);
+        g_return_val_if_fail (target != NULL, 0);
+        g_return_val_if_fail (usn != NULL, 0);
+        g_return_val_if_fail (location != NULL, 0);
+        g_return_val_if_fail (!service_group->priv->available, 0);
+
+        service = g_slice_new0 (Service);
+
+        service->target = g_strdup (target);
+        service->usn    = g_strdup (usn);
+
+        service->locations = g_list_append (service->locations,
+                                            g_strdup (location));
+
+        service_group->priv->services =
+                g_list_prepend (service_group->priv->services, service);
+
+        service->id = ++service_group->priv->last_service_id;
+
+        return service->id;
+}
+
+/**
+ * gssdp_service_group_remove_service
+ * @service_group: An @GSSDPServiceGroup
+ * @service_id: The ID of the service to remove
+ *
+ * Removes the service with ID @service_id from @service_group.
+ **/
+void
+gssdp_service_group_remove_service (GSSDPServiceGroup *service_group,
+                                    guint              service_id)
+{
+        GList *l;
+
+        g_return_if_fail (GSSDP_IS_SERVICE_GROUP (service_group));
+        g_return_if_fail (service_id > 0);
+        g_return_if_fail (!service_group->priv->available);
+
+        for (l = service_group->priv->services; l; l = l->next) {
+                Service *service;
+
+                service = l->data;
+
+                if (service->id == service_id) {
+                        service_group->priv->services = 
+                                g_list_remove (service_group->priv->services,
+                                               service);
+
+                        return;
+                }
+        }
+}
+
+/**
+ * Received a message
+ **/
+static void
+message_received_cb (GSSDPClient      *client,
+                     const char       *from_ip,
+                     _GSSDPMessageType type,
+                     GHashTable       *headers,
+                     gpointer          user_data)
+{
+        GSSDPServiceGroup *service_group;
+        GSList *list;
+        const char *target;
+        GList *l;
+
+        service_group = GSSDP_SERVICE_GROUP (user_data);
+
+        /* Only process if we are available */
+        if (!service_group->priv->available)
+                return;
+
+        /* We only handle discovery requests */
+        if (type != _GSSDP_DISCOVERY_REQUEST)
+                return;
+
+        /* Extract target */
+        list = g_hash_table_lookup (headers, "ST");
+        if (!list) {
+                g_warning ("Discovery request did not have an ST header");
+
+                return;
+        }
+
+        target = list->data;
+
+        /* Find matching service */
+        for (l = service_group->priv->services; l; l = l->next) {
+                Service *service;
+
+                service = l->data;
+
+                if (strcmp (service->target, target) == 0) {
+                        /* Match. Extract MX */
+                        int mx;
+                        guint timeout;
+
+                        list = g_hash_table_lookup (headers, "MX");
+                        if (list)
+                                mx = atoi (list->data);
+                        else
+                                mx = SSDP_DEFAULT_MX;
+
+                        /* Get a random timeout within the [0..mx] interval */
+                        timeout = g_random_int_range (0, mx * 1000);
+
+                        /* XXX run in timeout */
+                        send_discovery_response (service_group,
+                                                 from_ip,
+                                                 service);
+
+                        return;
+                }
+        }
+}
+
+/**
+ * Send a discovery response to @dest_ip containing @service
+ **/
+static void
+send_discovery_response (GSSDPServiceGroup *service_group,
+                         const char        *dest_ip,
+                         Service           *service)
+{
+        char *message;
+
+        /* XXX */
+        message = g_strdup_printf (SSDP_DISCOVERY_RESPONSE,
+                                   (char *) service->locations->data,
+                                   service->usn,
+                                   "HOER",
+                                   service_group->priv->max_age,
+                                   service->target);
+
+        _gssdp_client_send_message (service_group->priv->client,
+                                    dest_ip,
+                                    message,
+                                    NULL);
+
+        g_free (message);
+}
+
+/**
+ * Free a Service structure and its contained data
+ **/
+static void
+service_free (Service *service)
+{
+        g_free (service->usn);
+        g_free (service->target);
+
+        while (service->locations) {
+                g_free (service->locations->data);
+                service->locations = g_list_delete_link (service->locations,
+                                                         service->locations);
+        }
+
+        g_slice_free (Service, service);
+}
diff --git a/libgssdp/gssdp-service-group.h b/libgssdp/gssdp-service-group.h
new file mode 100644 (file)
index 0000000..36d0cd1
--- /dev/null
@@ -0,0 +1,110 @@
+/* 
+ * Copyright (C) 2006 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn@openedhand.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSSDP_SERVICE_GROUP_H__
+#define __GSSDP_SERVICE_GROUP_H__
+
+#include "gssdp-client.h"
+
+G_BEGIN_DECLS
+
+GType
+gssdp_service_group_get_type (void) G_GNUC_CONST;
+
+#define GSSDP_TYPE_SERVICE_GROUP \
+                (gssdp_service_group_get_type ())
+#define GSSDP_SERVICE_GROUP(obj) \
+                (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                 GSSDP_TYPE_SERVICE_GROUP, \
+                 GSSDPServiceGroup))
+#define GSSDP_SERVICE_GROUP_CLASS(klass) \
+                (G_TYPE_CHECK_CLASS_CAST ((klass), \
+                 GSSDP_TYPE_SERVICE_GROUP, \
+                 GSSDPServiceGroupClass))
+#define GSSDP_IS_SERVICE_GROUP(obj) \
+                (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                 GSSDP_TYPE_SERVICE_GROUP))
+#define GSSDP_IS_SERVICE_GROUP_CLASS(klass) \
+                (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+                 GSSDP_TYPE_SERVICE_GROUP))
+#define GSSDP_SERVICE_GROUP_GET_CLASS(obj) \
+                (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                 GSSDP_TYPE_SERVICE_GROUP, \
+                 GSSDPServiceGroupClass))
+
+typedef struct _GSSDPServiceGroupPrivate GSSDPServiceGroupPrivate;
+
+typedef struct {
+        GObject parent;
+
+        GSSDPServiceGroupPrivate *priv;
+} GSSDPServiceGroup;
+
+typedef struct {
+        GObjectClass parent_class;
+
+        /* future padding */
+        void (* _gssdp_reserved1) (void);
+        void (* _gssdp_reserved2) (void);
+        void (* _gssdp_reserved3) (void);
+        void (* _gssdp_reserved4) (void);
+} GSSDPServiceGroupClass;
+
+GSSDPServiceGroup *
+gssdp_service_group_new                (GSSDPClient       *client);
+
+GSSDPClient *
+gssdp_service_group_get_client         (GSSDPServiceGroup *service_group);
+
+void
+gssdp_service_group_set_max_age        (GSSDPServiceGroup *service_group,
+                                        guint              max_age);
+
+guint
+gssdp_service_group_get_max_age        (GSSDPServiceGroup *service_group);
+
+gboolean
+gssdp_service_group_set_available      (GSSDPServiceGroup *service_group,
+                                        gboolean           available,
+                                        GError           **error);
+
+gboolean
+gssdp_service_group_get_available      (GSSDPServiceGroup *service_group);
+
+guint
+gssdp_service_group_add_service        (GSSDPServiceGroup *service_group,
+                                        const char        *target,
+                                        const char        *usn,
+                                        GList             *locations);
+
+guint
+gssdp_service_group_add_service_simple (GSSDPServiceGroup *service_group,
+                                        const char        *target,
+                                        const char        *usn,
+                                        const char        *location);
+
+void
+gssdp_service_group_remove_service     (GSSDPServiceGroup *service_group,
+                                        guint              service_id);
+
+G_END_DECLS
+
+#endif /* __GSSDP_SERVICE_GROUP_H__ */
index 6ae32de..6619a6f 100644 (file)
@@ -1,5 +1,5 @@
 /* 
- * (C) 2006 OpenedHand Ltd.
+ * Copyright (C) 2006 OpenedHand Ltd.
  *
  * Author: Jorn Baayen <jorn@openedhand.com>
  *
@@ -22,3 +22,4 @@
 #include "gssdp-client.h"
 #include "gssdp-error.h"
 #include "gssdp-service-browser.h"
+#include "gssdp-service-group.h"