[grilo-plugins] upnp: tag sources on the local machine



commit 2b4ab2b3e7e684a52f8daa6f2f00c7d97d61de43
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Thu Feb 6 17:08:11 2014 +0100

    upnp: tag sources on the local machine
    
    Recognize sources that correspond to rygel or similar
    software on the local machine, and add a "localhost"
    tag to the source. This will allow Totem or other
    applications to filter the source out.
    
    The actual recognition is a bit hackish, because all we see
    is the URI provided by SSDP. There is a fallback to comparing
    hostnames, but because usually DNS is not configured in a home
    network, we should almost always see an IP address there.
    From that, we need to check if any interface is configured to
    use it. We could ask NetworkManager or connman, but that would
    grow an heavy and unwanted dependency; we could ask netlink,
    but that would fail outside of Linux; the simplest, although
    quite hackish, solution is to try and bind() to the remote
    address - if that succeeds, the address is local after all.
    The biggest downside to this solution, besides being Unix only
    due to EADDRNOTAVAIL, is that socket() can fail because of
    EMFILE/ENFILE, in which case we can't perform the check.
    Not a big deal maybe.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=723780

 src/upnp/Makefile.am      |    6 ++-
 src/upnp/grl-upnp-utils.c |  129 +++++++++++++++++++++++++++++++++++++++++++++
 src/upnp/grl-upnp-utils.h |   32 +++++++++++
 src/upnp/grl-upnp.c       |   21 ++++++-
 4 files changed, 184 insertions(+), 4 deletions(-)
---
diff --git a/src/upnp/Makefile.am b/src/upnp/Makefile.am
index 4f3b158..eae5eb5 100644
--- a/src/upnp/Makefile.am
+++ b/src/upnp/Makefile.am
@@ -28,7 +28,11 @@ libgrlupnp_la_CFLAGS +=      \
 libgrlupnp_la_LIBADD +=        \
        $(XML_LIBS)
 
-libgrlupnp_la_SOURCES = grl-upnp.c grl-upnp.h
+libgrlupnp_la_SOURCES = \
+       grl-upnp.c              \
+       grl-upnp.h              \
+       grl-upnp-utils.c        \
+       grl-upnp-utils.h
 
 extdir                 = $(GRL_PLUGINS_DIR)
 upnpxmldir             = $(GRL_PLUGINS_DIR)
diff --git a/src/upnp/grl-upnp-utils.c b/src/upnp/grl-upnp-utils.c
new file mode 100644
index 0000000..0ad0833
--- /dev/null
+++ b/src/upnp/grl-upnp-utils.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <gio/gio.h>
+
+#ifdef G_OS_UNIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#include "grl-upnp-utils.h"
+
+/* 255 is the hardcoded limit in all interesting systems.
+   It's actually 64 on linux.
+*/
+#define HOSTNAME_LENGTH 255
+
+#ifdef G_OS_UNIX
+
+static gboolean
+is_our_ip_address (GInetAddress *address)
+{
+  /* The simplest way to determine if an address belongs
+     to the current machine is to try and bind something.
+
+     If no interface has that address, bind() will fail
+     with EADDRNOTAVAIL.
+  */
+
+  GSocketAddress *sockaddr;
+  struct sockaddr *native_sockaddr;
+  gsize native_len;
+  GSocket *socket;
+  GError *error;
+  gboolean ret = FALSE;
+  int bound;
+
+  sockaddr = g_inet_socket_address_new (address, 0);
+
+  native_len = g_socket_address_get_native_size (sockaddr);
+  native_sockaddr = g_alloca (native_len);
+  g_socket_address_to_native (sockaddr, native_sockaddr, native_len, NULL);
+
+  error = NULL;
+  socket = g_socket_new (g_inet_address_get_family (address), G_SOCKET_TYPE_STREAM, 
G_SOCKET_PROTOCOL_DEFAULT, &error);
+  if (socket == NULL)
+    goto out;
+
+  /* we need to resort to native bind() instead of g_socket_bind()
+     because we want to check for EADDRNOTAVAIL, which is not covered
+     by GIOError
+  */
+  bound = bind (g_socket_get_fd (socket), native_sockaddr, native_len);
+  if (bound < 0)
+    ret = (errno != EADDRNOTAVAIL);
+  else
+    ret = TRUE;
+
+  g_socket_close (socket, NULL);
+  g_object_unref (socket);
+
+ out:
+  g_clear_error (&error);
+  g_object_unref (sockaddr);
+
+  return ret;
+}
+
+gboolean
+grl_upnp_util_uri_is_localhost (SoupURI *uri)
+{
+  char hostname_buffer[HOSTNAME_LENGTH+1];
+  const char *host;
+  GInetAddress *ip_address;
+  gboolean ret;
+
+  host = soup_uri_get_host (uri);
+  if (host == NULL)
+    return FALSE;
+
+  gethostname (hostname_buffer, sizeof(hostname_buffer));
+  if (strcmp (hostname_buffer, host) == 0)
+    return TRUE;
+
+  ip_address = g_inet_address_new_from_string (host);
+  if (ip_address == NULL)
+    return FALSE;
+
+  ret = is_our_ip_address (ip_address);
+  g_object_unref (ip_address);
+
+  return ret;
+}
+
+#else
+
+gboolean
+grl_upnp_util_uri_is_localhost (SoupURI *uri)
+{
+  return FALSE;
+}
+
+#endif
diff --git a/src/upnp/grl-upnp-utils.h b/src/upnp/grl-upnp-utils.h
new file mode 100644
index 0000000..52332b6
--- /dev/null
+++ b/src/upnp/grl-upnp-utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_UPNP_UTILS_H_
+#define _GRL_UPNP_UTILS_H_
+
+#include <libsoup/soup.h>
+
+G_BEGIN_DECLS
+
+gboolean grl_upnp_util_uri_is_localhost (SoupURI  *uri);
+
+G_END_DECLS
+
+#endif
diff --git a/src/upnp/grl-upnp.c b/src/upnp/grl-upnp.c
index fa9a061..f9a7e39 100644
--- a/src/upnp/grl-upnp.c
+++ b/src/upnp/grl-upnp.c
@@ -36,6 +36,7 @@
 #include <glib/gi18n-lib.h>
 
 #include "grl-upnp.h"
+#include "grl-upnp-utils.h"
 
 #define GRL_UPNP_GET_PRIVATE(object)                                    \
   (G_TYPE_INSTANCE_GET_PRIVATE((object), GRL_UPNP_SOURCE_TYPE, GrlUpnpPrivate))
@@ -109,7 +110,7 @@ static void setup_key_mappings (void);
 
 static gchar *build_source_id (const gchar *udn);
 
-static GrlUpnpSource *grl_upnp_source_new (const gchar *id, const gchar *name, const gchar *icon_url);
+static GrlUpnpSource *grl_upnp_source_new (const gchar *id, const gchar *name, const gchar *icon_url, 
gboolean localmachine);
 
 gboolean grl_upnp_plugin_init (GrlRegistry *registry,
                                GrlPlugin *plugin,
@@ -207,11 +208,15 @@ GRL_PLUGIN_REGISTER (grl_upnp_plugin_init,
 G_DEFINE_TYPE (GrlUpnpSource, grl_upnp_source, GRL_TYPE_SOURCE);
 
 static GrlUpnpSource *
-grl_upnp_source_new (const gchar *source_id, const gchar *name, const gchar *icon_url)
+grl_upnp_source_new (const gchar *source_id,
+                     const gchar *name,
+                     const gchar *icon_url,
+                     gboolean     localhost)
 {
   gchar *source_desc;
   GrlUpnpSource *source;
   GIcon *icon = NULL;
+  gchar *tags[2];
 
   GRL_DEBUG ("grl_upnp_source_new");
   source_desc = g_strdup_printf (SOURCE_DESC_TEMPLATE, name);
@@ -224,11 +229,18 @@ grl_upnp_source_new (const gchar *source_id, const gchar *name, const gchar *ico
     g_object_unref (file);
   }
 
+  if (localhost) {
+    tags[0] = "localhost";
+    tags[1] = NULL;
+  } else
+    tags[0] = NULL;
+
   source = g_object_new (GRL_UPNP_SOURCE_TYPE,
                         "source-id", source_id,
                         "source-name", name,
                         "source-desc", source_desc,
                         "source-icon", icon,
+                         "source-tags", tags,
                         NULL);
 
   source->priv->upnp_name = g_strdup (name);
@@ -456,6 +468,7 @@ device_available_cb (GUPnPControlPoint *cp,
                     GUPnPDeviceProxy *device,
                     gpointer user_data)
 {
+  SoupURI *url_base;
   gchar* name;
   const gchar* udn;
   const char *type;
@@ -494,7 +507,9 @@ device_available_cb (GUPnPControlPoint *cp,
   /* We got a valid UPnP source */
   /* Now let's check if it supports search operations before registering */
   icon_url = get_device_icon (device);
-  GrlUpnpSource *source = grl_upnp_source_new (source_id, name, icon_url);
+  url_base = (SoupURI*) gupnp_device_info_get_url_base (GUPNP_DEVICE_INFO (device));
+  GrlUpnpSource *source = grl_upnp_source_new (source_id, name, icon_url,
+                                               grl_upnp_util_uri_is_localhost (url_base));
   g_free (icon_url);
   source->priv->device = g_object_ref (device);
   source->priv->service = g_object_ref (service);


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