[gupnp] Implement a simple ACL infrastructure



commit 036f9d8b3df1fee296f04c553aee59234f967eae
Author: Jens Georg <mail jensge org>
Date:   Sun Nov 24 01:25:05 2013 +0100

    Implement a simple ACL infrastructure
    
    Signed-off-by: Jens Georg <mail jensge org>

 configure.ac                     |    2 +-
 libgupnp/Makefile.am             |    5 +-
 libgupnp/gupnp-acl-private.h     |   85 +++++++++++++
 libgupnp/gupnp-acl.c             |  258 ++++++++++++++++++++++++++++++++++++++
 libgupnp/gupnp-acl.h             |  118 +++++++++++++++++
 libgupnp/gupnp-context-private.h |    7 +
 libgupnp/gupnp-context.c         |  221 ++++++++++++++++++++++++++++++++-
 libgupnp/gupnp-context.h         |   16 +++
 libgupnp/gupnp-service.c         |   31 +++--
 9 files changed, 729 insertions(+), 14 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index bd03565..6b073de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -137,7 +137,7 @@ AC_ARG_ENABLE(debug,
         AS_HELP_STRING([--enable-debug], [enable debugging]),,
         enable_debug=no)
 if test "x$enable_debug" = "xyes"; then
-        CFLAGS="$CFLAGS -g -Wall -Werror"
+        CFLAGS="$CFLAGS -g -Wall -Wextra -Werror"
 fi
 
 GOBJECT_INTROSPECTION_CHECK([0.6.4])
diff --git a/libgupnp/Makefile.am b/libgupnp/Makefile.am
index 5f65373..771434d 100644
--- a/libgupnp/Makefile.am
+++ b/libgupnp/Makefile.am
@@ -48,7 +48,8 @@ libgupnpincdir = $(includedir)/gupnp-1.0/libgupnp
 
 lib_LTLIBRARIES = libgupnp-1.0.la
 
-libgupnpinc_HEADERS = gupnp-context.h                  \
+libgupnpinc_HEADERS = gupnp-acl.h                      \
+                     gupnp-context.h                   \
                      gupnp-context-manager.h           \
                      gupnp-control-point.h             \
                      gupnp-device.h                    \
@@ -67,6 +68,7 @@ libgupnpinc_HEADERS = gupnp-context.h                 \
                      gupnp.h
 
 introspection_sources = $(libgupnpinc_HEADERS) \
+                       gupnp-acl.c                     \
                        gupnp-context.c                 \
                        gupnp-context-manager.c         \
                        gupnp-control-point.c           \
@@ -106,6 +108,7 @@ libgupnp_1_0_la_SOURCES = $(introspection_sources)          \
                          gupnp-error-private.h                 \
                          gupnp-resource-factory-private.h      \
                          gupnp-service-introspection-private.h \
+                          gupnp-acl-private.h                   \
                          gupnp-types-private.h                 \
                          gvalue-util.c                         \
                          gvalue-util.h                         \
diff --git a/libgupnp/gupnp-acl-private.h b/libgupnp/gupnp-acl-private.h
new file mode 100644
index 0000000..ec28ef7
--- /dev/null
+++ b/libgupnp/gupnp-acl-private.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013,2014 Jens Georg <mail jensge org>
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GUPNP_ACL_PRIVATE_H__
+#define __GUPNP_ACL_PRIVATE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libsoup/soup-session.h>
+
+#include "gupnp-acl.h"
+#include "gupnp-context.h"
+
+G_BEGIN_DECLS
+
+/**
+ * Closure for the ACL server handler that adds
+ * a) Some data from the original server handler such as user_data and callback
+ * b) Saves information for later use to pass on to ACL such as the service and context
+ */
+typedef struct _AclServerHandler
+{
+        GUPnPService *service;
+        GUPnPContext *context;
+        SoupServerCallback callback;
+        gpointer user_data;
+        GDestroyNotify notify;
+} AclServerHandler;
+
+/**
+ * Closure when doing an async ACL request. Stores everything passed into the server handler
+ */
+typedef struct _AclAsyncHandler
+{
+        SoupServer *server;
+        SoupMessage *message;
+        char *path;
+        GHashTable *query;
+        SoupClientContext *client;
+        AclServerHandler *handler;
+} AclAsyncHandler;
+
+G_GNUC_INTERNAL AclServerHandler *
+acl_server_handler_new (GUPnPService *service,
+                        GUPnPContext *context,
+                        SoupServerCallback callback,
+                        gpointer user_data,
+                        GDestroyNotify notify);
+
+G_GNUC_INTERNAL void
+acl_server_handler_free (AclServerHandler *handler);
+
+G_GNUC_INTERNAL AclAsyncHandler *
+acl_async_handler_new (SoupServer *server,
+                       SoupMessage *message,
+                       const char *path,
+                       GHashTable *query,
+                       SoupClientContext *client,
+                       AclServerHandler *handler);
+
+G_GNUC_INTERNAL void
+acl_async_handler_free (AclAsyncHandler *handler);
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgupnp/gupnp-acl.c b/libgupnp/gupnp-acl.c
new file mode 100644
index 0000000..d9accc8
--- /dev/null
+++ b/libgupnp/gupnp-acl.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2013,2014 Jens Georg <mail jensge org>
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "gupnp-acl.h"
+#include "gupnp-acl-private.h"
+
+G_DEFINE_INTERFACE(GUPnPAcl, gupnp_acl, G_TYPE_OBJECT)
+
+static void
+gupnp_acl_default_init (GUPnPAclInterface *klass)
+{
+}
+
+/**
+ * gupnp_acl_is_allowed:
+ * @self: an instance of #GUPnPAcl
+ * @device: (allow-none): The #GUPnPDevice associated with @path or %NULL if
+ * unknown.
+ * @service: (allow-none): The #GUPnPService associated with @path or %NULL if
+ * unknown.
+ * @path: The path being served.
+ * @address: IP address of the peer.
+ * @agent: (allow-none): The User-Agent header of the peer or %NULL if not
+ * unknown.
+ * @returns: %TRUE if the peer is allowed, %FALSE otherwise
+ *
+ * Check whether an IP address is allowed to access this resource.
+ *
+ * Since: 0.20.11
+ */
+gboolean
+gupnp_acl_is_allowed (GUPnPAcl     *self,
+                      GUPnPDevice  *device,
+                      GUPnPService *service,
+                      const char   *path,
+                      const char   *address,
+                      const char   *agent)
+{
+        g_return_val_if_fail (GUPNP_IS_ACL (self), FALSE);
+
+        return GUPNP_ACL_GET_INTERFACE (self)->is_allowed (self,
+                                                           device,
+                                                           service,
+                                                           path,
+                                                           address,
+                                                           agent);
+}
+
+/**
+ * gupnp_acl_is_allowed_async:
+ * @self: a #GUPnPAcl
+ * @device: (allow-none): The #GUPnPDevice associated with @path or %NULL if
+ * unknown.
+ * @service: (allow-none): The #GUPnPService associated with @path or %NULL if
+ * unknown.
+ * @path: The path being served.
+ * @address: IP address of the peer
+ * @agent: (allow-none): The User-Agent header of the peer or %NULL if not
+ * unknown.
+ * @cancellable: (allow-none): A #GCancellable which can be used to cancel the
+ * operation.
+ * @callback: Callback to call after the function is done.
+ * @user_data: Some user data.
+ *
+ * Optional. Check asynchronously whether an IP address is allowed to access
+ * this resource. Use this function if the process of verifying the access right
+ * is expected to take some time, for example when using D-Bus etc.
+ *
+ * If this function is supported, gupnp_acl_can_sync() should return %TRUE.
+ *
+ * Use gupnp_acl_is_allowed_finish() to retrieve the result.
+ *
+* Since: 0.20.11
+ */
+void
+gupnp_acl_is_allowed_async (GUPnPAcl           *self,
+                            GUPnPDevice        *device,
+                            GUPnPService       *service,
+                            const char         *path,
+                            const char         *address,
+                            const char         *agent,
+                            GCancellable       *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer            user_data)
+{
+        g_return_if_fail (GUPNP_IS_ACL (self));
+
+        GUPNP_ACL_GET_INTERFACE (self)->is_allowed_async (self,
+                                                          device,
+                                                          service,
+                                                          path,
+                                                          address,
+                                                          agent,
+                                                          cancellable,
+                                                          callback,
+                                                          user_data);
+}
+
+/**
+ * gupnp_acl_is_allowed_finish:
+ * @self: An instance of #GUPnPAcl
+ * @res: %GAsyncResult obtained from the callback in gupnp_acl_is_allowed_async()
+ * @error: (allow-none): A return location for a #GError describing the failure
+ * @returns %TRUE if the authentication was successful, %FALSE otherwise and on
+ * error. Check @error for details.
+ *
+ * Since: 0.20.11
+ */
+gboolean
+gupnp_acl_is_allowed_finish (GUPnPAcl      *self,
+                             GAsyncResult  *res,
+                             GError       **error)
+{
+        g_return_if_fail (GUPNP_IS_ACL (self));
+
+        return GUPNP_ACL_GET_INTERFACE (self)->is_allowed_finish (self,
+                                                                  res,
+                                                                  error);
+}
+
+/**
+ * gupnp_acl_can_sync:
+ * @self: A #GUPnPAcl
+ * @returns: %TRUE, if gupnp_acl_is_allowed_async() is supported, %FALSE
+ * otherwise.
+ *
+ * Check whether gupnp_acl_is_allowed_async() is supported.
+ *
+ * Since: 0.20.11
+ */
+gboolean
+gupnp_acl_can_sync (GUPnPAcl *self)
+{
+        g_return_if_fail (GUPNP_IS_ACL (self));
+
+        return GUPNP_ACL_GET_INTERFACE (self)->can_sync (self);
+}
+
+/**
+ * acl_server_handler_new:
+ *
+ * Allocate a new #AclServerHandler.
+ *
+ * @service: (allow-none): A #GUPnPContext or %NULL if unknown
+ * @context: The #GUPnPContext the server handler is run on.
+ * @callback: The #SoupServerCallback we're wrapping.
+ * @user_data: The user_data for @callback
+ * @notify: (allow-none): The #GDestroyNotify for @user_data or %NULL if none.
+ * @returns: A newly allocated #AclServerHandler
+ */
+AclServerHandler *
+acl_server_handler_new (GUPnPService *service,
+                        GUPnPContext *context,
+                        SoupServerCallback callback,
+                        gpointer user_data,
+                        GDestroyNotify notify)
+{
+        AclServerHandler *handler = g_new0 (AclServerHandler, 1);
+
+        handler->service = service ? g_object_ref (service) : NULL;
+        handler->context = g_object_ref (context);
+        handler->callback = callback;
+        handler->user_data = user_data;
+        handler->notify = notify;
+
+        return handler;
+}
+
+/**
+ * acl_server_handler_free:
+ *
+ * Free an #AclServerHandler previously allocated with acl_server_handler_new().
+ *
+ * @handler: An #AclServerHandler instance.
+ */
+void
+acl_server_handler_free (AclServerHandler *handler)
+{
+        g_clear_object (&handler->service);
+        g_clear_object (&handler->context);
+
+        if (handler->notify != NULL)
+                handler->notify (handler->user_data);
+
+        g_free (handler);
+}
+
+/**
+ * acl_async_handler_new:
+ *
+ * Create a new async closure.
+ *
+ * @server: A #SoupServer instance.
+ * @message: The #SoupMessage we want to handle.
+ * @path: The path we're trying to serve.
+ * @query: (allow-none): The query parameters of the request.
+ * @client: The #SoupClientContext for this request.
+ * @handler: The #AclServerHandler used with this request.
+ * @returns: A new instance of #AclAsyncHandler.
+ */
+AclAsyncHandler *
+acl_async_handler_new (SoupServer *server,
+                       SoupMessage *message,
+                       const char *path,
+                       GHashTable *query,
+                       SoupClientContext *client,
+                       AclServerHandler *handler)
+{
+        AclAsyncHandler *data = g_slice_new0 (AclAsyncHandler);
+
+        data->server = g_object_ref (server);
+        data->message = g_object_ref (message);
+        data->path = g_strdup (path);
+        if (query != NULL)
+                data->query = g_hash_table_ref (query);
+        data->client = g_boxed_copy (SOUP_TYPE_CLIENT_CONTEXT, client);
+        data->handler = handler;
+
+        return data;
+}
+
+/**
+ * acl_async_handler_free:
+ *
+ * Free an #AclAsyncHandler allocated with acl_async_handler_new().
+ *
+ * @handler: An instance allocated with acl_async_handler_new()
+ */
+void
+acl_async_handler_free (AclAsyncHandler *handler)
+{
+        g_object_unref (handler->server);
+        g_object_unref (handler->message);
+        g_free (handler->path);
+        if (handler->query != NULL)
+                g_hash_table_unref (handler->query);
+        g_boxed_free (SOUP_TYPE_CLIENT_CONTEXT, handler->client);
+
+        g_slice_free (AclAsyncHandler, handler);
+}
diff --git a/libgupnp/gupnp-acl.h b/libgupnp/gupnp-acl.h
new file mode 100644
index 0000000..a1ba497
--- /dev/null
+++ b/libgupnp/gupnp-acl.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013,2014 Jens Georg <mail jensge org>
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GUPNP_ACL_H__
+#define __GUPNP_ACL_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libsoup/soup-session.h>
+
+G_BEGIN_DECLS
+
+GType
+gupnp_acl_get_type (void) G_GNUC_CONST;
+
+#define GUPNP_TYPE_ACL (gupnp_acl_get_type())
+
+#define GUPNP_ACL(obj)                  \
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+     GUPNP_TYPE_ACL,                    \
+     GUPnPAcl))
+
+#define GUPNP_IS_ACL(obj)               \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj),  \
+     GUPNP_TYPE_ACL))
+
+#define GUPNP_ACL_GET_INTERFACE(obj)      \
+    (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \
+     GUPNP_TYPE_ACL, GUPnPAclInterface))
+
+typedef struct _GUPnPAcl GUPnPAcl;
+typedef struct _GUPnPAclInterface GUPnPAclInterface;
+
+/* Forward declarations to avoid recursive includes */
+typedef struct _GUPnPDevice GUPnPDevice;
+typedef struct _GUPnPService GUPnPService;
+
+struct _GUPnPAclInterface {
+    GTypeInterface parent;
+
+    gboolean (*is_allowed) (GUPnPAcl     *self,
+                            GUPnPDevice  *device,
+                            GUPnPService *service,
+                            const char   *path,
+                            const char   *address,
+                            const char   *agent);
+
+    void     (*is_allowed_async) (GUPnPAcl           *self,
+                                  GUPnPDevice        *device,
+                                  GUPnPService       *service,
+                                  const char         *path,
+                                  const char         *address,
+                                  const char         *agent,
+                                  GCancellable       *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer            user_data);
+
+    gboolean (*is_allowed_finish) (GUPnPAcl      *self,
+                                   GAsyncResult  *res,
+                                   GError       **error);
+
+    gboolean (*can_sync)          (GUPnPAcl *self);
+
+    /* future padding */
+    void (* _gupnp_reserved1) (void);
+    void (* _gupnp_reserved2) (void);
+    void (* _gupnp_reserved3) (void);
+    void (* _gupnp_reserved4) (void);
+};
+
+gboolean
+gupnp_acl_is_allowed (GUPnPAcl     *self,
+                      GUPnPDevice  *device,
+                      GUPnPService *service,
+                      const char   *path,
+                      const char   *address,
+                      const char   *agent);
+
+void
+gupnp_acl_is_allowed_async (GUPnPAcl           *self,
+                            GUPnPDevice        *device,
+                            GUPnPService       *service,
+                            const char         *path,
+                            const char         *address,
+                            const char         *agent,
+                            GCancellable       *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer            user_data);
+
+gboolean
+gupnp_acl_is_allowed_finish (GUPnPAcl      *self,
+                             GAsyncResult  *res,
+                             GError       **error);
+
+gboolean
+gupnp_acl_can_sync (GUPnPAcl *self);
+
+G_END_DECLS
+
+#endif
diff --git a/libgupnp/gupnp-context-private.h b/libgupnp/gupnp-context-private.h
index 18c4813..212d5e0 100644
--- a/libgupnp/gupnp-context-private.h
+++ b/libgupnp/gupnp-context-private.h
@@ -24,11 +24,18 @@
 
 #include <libsoup/soup.h>
 
+#include "gupnp-acl-private.h"
+
 G_BEGIN_DECLS
 
 G_GNUC_INTERNAL const char *
 _gupnp_context_get_server_url (GUPnPContext *context);
 
+G_GNUC_INTERNAL void
+_gupnp_context_add_server_handler_with_data (GUPnPContext *context,
+                                             const char *path,
+                                             AclServerHandler *data);
+
 G_END_DECLS
 
 #endif /* __GUPNP_CONTEXT_PRIVATE_H__ */
diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
index 07ded6d..f8855d4 100644
--- a/libgupnp/gupnp-context.c
+++ b/libgupnp/gupnp-context.c
@@ -51,6 +51,8 @@
 #include <libsoup/soup-address.h>
 #include <glib/gstdio.h>
 
+#include "gupnp-acl.h"
+#include "gupnp-acl-private.h"
 #include "gupnp-context.h"
 #include "gupnp-context-private.h"
 #include "gupnp-error.h"
@@ -61,6 +63,14 @@
 #define GUPNP_CONTEXT_DEFAULT_LANGUAGE "en"
 
 static void
+gupnp_acl_server_handler (SoupServer *server,
+                          SoupMessage *msg,
+                          const char *path,
+                          GHashTable *query,
+                          SoupClientContext *client,
+                          gpointer user_data);
+
+static void
 gupnp_context_initable_iface_init (gpointer g_iface,
                                    gpointer iface_data);
 
@@ -85,6 +95,8 @@ struct _GUPnPContextPrivate {
         char        *default_language;
 
         GList       *host_path_datas;
+
+        GUPnPAcl    *acl;
 };
 
 enum {
@@ -93,7 +105,8 @@ enum {
         PROP_SERVER,
         PROP_SESSION,
         PROP_SUBSCRIPTION_TIMEOUT,
-        PROP_DEFAULT_LANGUAGE
+        PROP_DEFAULT_LANGUAGE,
+        PROP_ACL
 };
 
 typedef struct {
@@ -253,6 +266,10 @@ gupnp_context_set_property (GObject      *object,
                 gupnp_context_set_default_language (context,
                                                     g_value_get_string (value));
                 break;
+        case PROP_ACL:
+                gupnp_context_set_acl (context, g_value_get_object (value));
+
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break;
@@ -292,6 +309,11 @@ gupnp_context_get_property (GObject    *object,
                                     gupnp_context_get_default_language
                                                                    (context));
                 break;
+        case PROP_ACL:
+                g_value_set_object (value,
+                                    gupnp_context_get_acl (context));
+
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break;
@@ -493,6 +515,17 @@ gupnp_context_class_init (GUPnPContextClass *klass)
                                       G_PARAM_STATIC_NAME |
                                       G_PARAM_STATIC_NICK |
                                       G_PARAM_STATIC_BLURB));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_ACL,
+                 g_param_spec_object ("acl",
+                                      "Access control list",
+                                      "Access control list",
+                                      GUPNP_TYPE_ACL,
+                                      G_PARAM_CONSTRUCT |
+                                      G_PARAM_READWRITE |
+                                      G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -1281,3 +1314,189 @@ gupnp_context_unhost_path (GUPnPContext *context,
         soup_server_remove_handler (server, server_path);
         host_path_data_free (path_data);
 }
+
+/**
+ * gupnp_context_get_acl:
+ * @context: A #GUPnPContext
+ *
+ * Returns:(transfer none): The access control list associated with this context or %NULL
+ * if no acl is set.
+ **/
+GUPnPAcl *
+gupnp_context_get_acl (GUPnPContext *context)
+{
+        g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL);
+
+        return context->priv->acl;
+}
+
+/**
+ * gupnp_context_set_acl:
+ * @context: A #GUPnPContext
+ * @acl:(allow-none): The new access control list or %NULL to remove the
+ * current list.
+ **/
+void
+gupnp_context_set_acl (GUPnPContext *context, GUPnPAcl *acl)
+{
+        g_return_if_fail (GUPNP_IS_CONTEXT (context));
+
+        if (context->priv->acl != NULL) {
+                g_object_unref (context->priv->acl);
+                context->priv->acl = NULL;
+        }
+
+        if (acl != NULL)
+                context->priv->acl = g_object_ref (acl);
+
+        g_object_notify (G_OBJECT (context), "acl");
+}
+
+static void
+gupnp_acl_async_callback (GUPnPAcl *acl,
+                          GAsyncResult *res,
+                          AclAsyncHandler *data)
+{
+        gboolean allowed;
+        GError *error = NULL;
+
+        allowed = gupnp_acl_is_allowed_finish (acl, res, &error);
+        soup_server_unpause_message (data->server, data->message);
+        if (!allowed)
+                soup_message_set_status (data->message, SOUP_STATUS_FORBIDDEN);
+        else
+                data->handler->callback (data->server,
+                                         data->message,
+                                         data->path,
+                                         data->query,
+                                         data->client,
+                                         data->handler->user_data);
+
+        acl_async_handler_free (data);
+}
+
+static void
+gupnp_acl_server_handler (SoupServer *server,
+                          SoupMessage *msg,
+                          const char *path,
+                          GHashTable *query,
+                          SoupClientContext *client,
+                          gpointer user_data)
+{
+        AclServerHandler *handler = (AclServerHandler *) user_data;
+        const char *agent;
+        GUPnPDevice *device = NULL;
+
+        if (handler->service) {
+                g_object_get (handler->service,
+                              "root-device", &device,
+                              NULL);
+        }
+
+        agent = soup_message_headers_get_one (msg->request_headers,
+                                              "User-Agent");
+
+        if (gupnp_acl_can_sync (handler->context->priv->acl)) {
+                if (!gupnp_acl_is_allowed (handler->context->priv->acl,
+                                           device,
+                                           handler->service,
+                                           path,
+                                           soup_client_context_get_host (client),
+                                           agent)) {
+                        soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+
+                        return;
+                }
+        } else {
+                AclAsyncHandler *data;
+
+                data = acl_async_handler_new (server, msg, path, query, client, handler);
+
+                soup_server_pause_message (server, msg);
+                gupnp_acl_is_allowed_async (handler->context->priv->acl,
+                                            device,
+                                            handler->service,
+                                            path,
+                                            soup_client_context_get_host (client),
+                                            agent,
+                                            NULL,
+                                            (GAsyncReadyCallback) gupnp_acl_async_callback,
+                                            data);
+
+                return;
+        }
+
+        /* Delegate to orignal callback */
+        handler->callback (server, msg, path, query, client, handler->user_data);
+}
+
+/**
+ * gupnp_context_add_server_handler:
+ * @context: a #GUPnPContext
+ * @use_acl: %TRUE, if the path should query the GUPnPContext::acl before
+ * serving the resource, %FALSE otherwise.
+ * @path: the toplevel path for the handler.
+ * @callback: callback to invoke for requests under @path
+ * @user_data:
+ * @destroy:
+ *
+ * Add a #SoupServerCallback to the #GUPnPContext<!-- -->'s #SoupServer.
+ *
+ */
+void
+gupnp_context_add_server_handler (GUPnPContext *context,
+                                  gboolean use_acl,
+                                  const char *path,
+                                  SoupServerCallback callback,
+                                  gpointer user_data,
+                                  GDestroyNotify destroy)
+{
+        g_return_if_fail (GUPNP_IS_CONTEXT (context));
+
+        if (use_acl) {
+                AclServerHandler *handler;
+                handler = acl_server_handler_new (NULL, context, callback, user_data, destroy);
+                soup_server_add_handler (context->priv->server,
+                                         path,
+                                         gupnp_acl_server_handler,
+                                         handler,
+                                         (GDestroyNotify) acl_server_handler_free);
+        } else
+                soup_server_add_handler (context->priv->server,
+                                         path,
+                                         callback,
+                                         user_data,
+                                         destroy);
+}
+
+void
+_gupnp_context_add_server_handler_with_data (GUPnPContext *context,
+                                             const char *path,
+                                             AclServerHandler *handler)
+{
+        g_return_if_fail (GUPNP_IS_CONTEXT (context));
+
+        soup_server_add_handler (context->priv->server,
+                                 path,
+                                 gupnp_acl_server_handler,
+                                 handler,
+                                 (GDestroyNotify) acl_server_handler_free);
+}
+
+/**
+ * gupnp_context_remove_server_handler:
+ * @context: a #GUPnPContext
+ * @use_acl: %TRUE, if the path should query the GUPnPContext::acl before
+ * serving the resource, %FALSE otherwise.
+ * @path: the toplevel path for the handler.
+ *
+ * Add a #SoupServerCallback to the #GUPnPContext<!-- -->'s #SoupServer.
+ *
+ */
+void
+gupnp_context_remove_server_handler (GUPnPContext *context, const char *path)
+{
+        g_return_if_fail (GUPNP_IS_CONTEXT (context));
+
+        soup_server_remove_handler (context->priv->server, path);
+}
diff --git a/libgupnp/gupnp-context.h b/libgupnp/gupnp-context.h
index a2e2f9e..233d2fa 100644
--- a/libgupnp/gupnp-context.h
+++ b/libgupnp/gupnp-context.h
@@ -26,6 +26,8 @@
 #include <libsoup/soup-server.h>
 #include <libsoup/soup-session.h>
 
+#include "gupnp-acl.h"
+
 G_BEGIN_DECLS
 
 GType
@@ -129,6 +131,20 @@ void
 gupnp_context_unhost_path              (GUPnPContext *context,
                                         const char   *server_path);
 
+GUPnPAcl *
+gupnp_context_get_acl                  (GUPnPContext *context);
+
+void
+gupnp_context_set_acl                  (GUPnPContext *context,
+                                        GUPnPAcl     *acl);
+
+void
+gupnp_context_add_server_handler       (GUPnPContext *context,
+                                        gboolean use_acl,
+                                        const char *path,
+                                        SoupServerCallback callback,
+                                        gpointer user_data,
+                                        GDestroyNotify destroy);
 G_END_DECLS
 
 #endif /* __GUPNP_CONTEXT_H__ */
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
index 684dbd1..f545dd4 100644
--- a/libgupnp/gupnp-service.c
+++ b/libgupnp/gupnp-service.c
@@ -31,11 +31,13 @@
 #include <gmodule.h>
 #include <libsoup/soup-date.h>
 #include <string.h>
+
 #include "gupnp-service.h"
 #include "gupnp-root-device.h"
 #include "gupnp-context-private.h"
 #include "gupnp-marshal.h"
 #include "gupnp-error.h"
+#include "gupnp-acl.h"
 #include "http-headers.h"
 #include "gena-protocol.h"
 #include "xml-util.h"
@@ -1302,6 +1304,8 @@ subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
                              gpointer                         user_data)
 {
         GUPnPService *service;
+        GUPnPContext *context;
+        GUPnPAcl *acl;
         const char *callback, *nt, *sid;
 
         service = GUPNP_SERVICE (user_data);
@@ -1432,7 +1436,7 @@ gupnp_service_constructor (GType                  type,
         GObject *object;
         GUPnPServiceInfo *info;
         GUPnPContext *context;
-        SoupServer *server;
+        AclServerHandler *handler;
         char *url;
         char *path;
 
@@ -1443,7 +1447,7 @@ gupnp_service_constructor (GType                  type,
                                             n_construct_params,
                                             construct_params);
 
-        info    = GUPNP_SERVICE_INFO (object);
+        info = GUPNP_SERVICE_INFO (object);
 
         /* Get introspection and save state variable names */
         gupnp_service_info_get_introspection_async (info,
@@ -1452,21 +1456,28 @@ gupnp_service_constructor (GType                  type,
 
         /* Get server */
         context = gupnp_service_info_get_context (info);
-        server = gupnp_context_get_server (context);
 
         /* Run listener on controlURL */
         url = gupnp_service_info_get_control_url (info);
         path = path_from_url (url);
-        soup_server_add_handler (server, path,
-                                 control_server_handler, object, NULL);
+        handler = acl_server_handler_new (GUPNP_SERVICE (object),
+                                          context,
+                                          control_server_handler,
+                                          g_object_ref (object),
+                                          g_object_unref);
+        _gupnp_context_add_server_handler_with_data (context, path, handler);
         g_free (path);
         g_free (url);
 
         /* Run listener on eventSubscriptionURL */
         url = gupnp_service_info_get_event_subscription_url (info);
         path = path_from_url (url);
-        soup_server_add_handler (server, path,
-                                 subscription_server_handler, object, NULL);
+        handler = acl_server_handler_new (GUPNP_SERVICE (object),
+                                          context,
+                                          subscription_server_handler,
+                                          g_object_ref (object),
+                                          g_object_unref);
+        _gupnp_context_add_server_handler_with_data (context, path, handler);
         g_free (path);
         g_free (url);
 
@@ -1553,7 +1564,6 @@ gupnp_service_dispose (GObject *object)
         GObjectClass *object_class;
         GUPnPServiceInfo *info;
         GUPnPContext *context;
-        SoupServer *server;
         char *url;
         char *path;
 
@@ -1562,19 +1572,18 @@ gupnp_service_dispose (GObject *object)
         /* Get server */
         info = GUPNP_SERVICE_INFO (service);
         context = gupnp_service_info_get_context (info);
-        server = gupnp_context_get_server (context);
 
         /* Remove listener on controlURL */
         url = gupnp_service_info_get_control_url (info);
         path = path_from_url (url);
-        soup_server_remove_handler (server, path);
+        gupnp_context_remove_server_handler (context, path);
         g_free (path);
         g_free (url);
 
         /* Remove listener on eventSubscriptionURL */
         url = gupnp_service_info_get_event_subscription_url (info);
         path = path_from_url (url);
-        soup_server_remove_handler (server, path);
+        gupnp_context_remove_server_handler (context, path);
         g_free (path);
         g_free (url);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]