upnp: move UpnpInstanceWrapper to upnp-wrapper
authorShaleen Jain <shaleen@jain.sh>
Wed, 27 Jun 2018 12:14:30 +0000 (17:44 +0530)
committerHugo Beauzée-Luyssen <hugo@beauzee.fr>
Fri, 29 Jun 2018 11:19:41 +0000 (13:19 +0200)
Move out UpnpInstanceWrapper into its own file
since it no longer has any dependencies on existing
modules and allow other modules to get references to it.
Move as well as the various helper functions that are
directly required by the UpnpInstanceWrapper class
and inline them for a small performance gain.

Signed-off-by: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
modules/services_discovery/Makefile.am
modules/services_discovery/upnp-wrapper.cpp [new file with mode: 0644]
modules/services_discovery/upnp-wrapper.hpp [new file with mode: 0644]
modules/services_discovery/upnp.cpp
modules/services_discovery/upnp.hpp

index 41c7871..f63df23 100644 (file)
@@ -26,7 +26,9 @@ libmtp_plugin_la_LIBADD = $(MTP_LIBS)
 EXTRA_LTLIBRARIES += libmtp_plugin.la
 sd_LTLIBRARIES += $(LTLIBmtp)
 
-libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp services_discovery/upnp.hpp
+libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp services_discovery/upnp.hpp \
+                           services_discovery/upnp-wrapper.hpp \
+                           services_discovery/upnp-wrapper.cpp
 libupnp_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(UPNP_CFLAGS)
 libupnp_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)'
 libupnp_plugin_la_LIBADD = $(UPNP_LIBS)
diff --git a/modules/services_discovery/upnp-wrapper.cpp b/modules/services_discovery/upnp-wrapper.cpp
new file mode 100644 (file)
index 0000000..9bdeebc
--- /dev/null
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ * upnp-wrapper.cpp :  UPnP Instance Wrapper class
+ *****************************************************************************
+ * Copyright © 2004-2018 VLC authors and VideoLAN
+ *
+ * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
+ *          Christian Henz <henz # c-lab.de>
+ *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
+ *          Hugo Beauzée-Luyssen <hugo@beauzee.fr>
+ *          Shaleen Jain <shaleen@jain.sh>
+ *
+ * This program 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; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "upnp-wrapper.hpp"
+
+UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
+UpnpInstanceWrapper::Listeners UpnpInstanceWrapper::s_listeners;
+vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
+
+UpnpInstanceWrapper::UpnpInstanceWrapper()
+    : m_handle( -1 )
+    , m_refcount( 0 )
+{
+}
+
+UpnpInstanceWrapper::~UpnpInstanceWrapper()
+{
+    UpnpUnRegisterClient( m_handle );
+    UpnpFinish();
+}
+
+UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
+{
+    vlc_mutex_locker lock( &s_lock );
+    if ( s_instance == NULL )
+    {
+        UpnpInstanceWrapper* instance = new(std::nothrow) UpnpInstanceWrapper;
+        if ( unlikely( !instance ) )
+        {
+            return NULL;
+        }
+
+    #ifdef UPNP_ENABLE_IPV6
+        char* psz_miface = var_InheritString( p_obj, "miface" );
+        if (psz_miface == NULL)
+            psz_miface = getPreferedAdapter();
+        msg_Info( p_obj, "Initializing libupnp on '%s' interface", psz_miface ? psz_miface : "default" );
+        int i_res = UpnpInit2( psz_miface, 0 );
+        free( psz_miface );
+    #else
+        /* If UpnpInit2 isnt available, initialize on first IPv4-capable interface */
+        char *psz_hostip = getIpv4ForMulticast();
+        int i_res = UpnpInit( psz_hostip, 0 );
+        free(psz_hostip);
+    #endif /* UPNP_ENABLE_IPV6 */
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );
+            delete instance;
+            return NULL;
+        }
+
+        ixmlRelaxParser( 1 );
+
+        /* Register a control point */
+        i_res = UpnpRegisterClient( Callback, instance, &instance->m_handle );
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
+            delete instance;
+            return NULL;
+        }
+
+        /* libupnp does not treat a maximum content length of 0 as unlimited
+         * until 64dedf (~ pupnp v1.6.7) and provides no sane way to discriminate
+         * between versions */
+        if( (i_res = UpnpSetMaxContentLength( INT_MAX )) != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Failed to set maximum content length: %s",
+                    UpnpGetErrorMessage( i_res ));
+            delete instance;
+            return NULL;
+        }
+        s_instance = instance;
+    }
+    s_instance->m_refcount++;
+    return s_instance;
+}
+
+void UpnpInstanceWrapper::release()
+{
+    UpnpInstanceWrapper *p_delete = NULL;
+    vlc_mutex_lock( &s_lock );
+    if (--s_instance->m_refcount == 0)
+    {
+        p_delete = s_instance;
+        s_instance = NULL;
+    }
+    vlc_mutex_unlock( &s_lock );
+    delete p_delete;
+}
+
+UpnpClient_Handle UpnpInstanceWrapper::handle() const
+{
+    return m_handle;
+}
+
+int UpnpInstanceWrapper::Callback(Upnp_EventType event_type, UpnpEventPtr p_event, void *p_user_data)
+{
+    for (Listeners::iterator iter = s_listeners.begin(); iter != s_listeners.end(); ++iter)
+    {
+        (*iter)->onEvent(event_type, p_event, p_user_data);
+    }
+
+    return 0;
+}
+
+void UpnpInstanceWrapper::addListener(ListenerPtr listener)
+{
+    vlc_mutex_lock( &s_lock );
+    if ( std::find( s_listeners.begin(), s_listeners.end(), listener) != s_listeners.end() )
+    {
+        vlc_mutex_unlock( &s_lock );
+        return;
+    }
+    s_listeners.push_back( std::move(listener) );
+    vlc_mutex_unlock( &s_lock );
+}
+
+void UpnpInstanceWrapper::removeListener(ListenerPtr listener)
+{
+    vlc_mutex_lock( &s_lock );
+    Listeners::iterator iter = std::find( s_listeners.begin(), s_listeners.end(), listener );
+    if ( iter == s_listeners.end() )
+    {
+        vlc_mutex_unlock( &s_lock );
+        return;
+    }
+
+    s_listeners.erase( iter );
+    vlc_mutex_unlock( &s_lock );
+}
diff --git a/modules/services_discovery/upnp-wrapper.hpp b/modules/services_discovery/upnp-wrapper.hpp
new file mode 100644 (file)
index 0000000..33412cb
--- /dev/null
@@ -0,0 +1,349 @@
+/*****************************************************************************
+ * upnp-wrapper.hpp :  UPnP Instance Wrapper class header
+ *****************************************************************************
+ * Copyright © 2004-2018 VLC authors and VideoLAN
+ *
+ * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
+ *          Christian Henz <henz # c-lab.de>
+ *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
+ *          Hugo Beauzée-Luyssen <hugo@beauzee.fr>
+ *          Shaleen Jain <shaleen@jain.sh>
+ *
+ * This program 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; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <assert.h>
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include <vlc_common.h>
+#include <vlc_charset.h>
+
+#if UPNP_VERSION < 10800
+typedef void* UpnpEventPtr;
+#else
+typedef const void* UpnpEventPtr;
+#endif
+
+/**
+ * libUpnp allows only one instance per process, so we create a wrapper
+ * class around it that acts and behaves as a singleton. Letting us get
+ * multiple references to it but only ever having a single instance in memory.
+ * At the same time we let any module wishing to get a callback from the library
+ * to register a UpnpInstanceWrapper::Listener to get the Listener#onEvent()
+ * callback without having any hard dependencies.
+ */
+class UpnpInstanceWrapper
+{
+public:
+    class Listener
+    {
+        public:
+            virtual ~Listener() {}
+            virtual int onEvent( Upnp_EventType event_type,
+                                 UpnpEventPtr p_event,
+                                 void* p_user_data ) = 0;
+    };
+
+private:
+    static UpnpInstanceWrapper* s_instance;
+    static vlc_mutex_t s_lock;
+    UpnpClient_Handle m_handle;
+    int m_refcount;
+    typedef std::shared_ptr<Listener> ListenerPtr;
+    typedef std::vector<ListenerPtr> Listeners;
+    static Listeners s_listeners;
+
+public:
+    // This increases the refcount before returning the instance
+    static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
+    void release();
+    UpnpClient_Handle handle() const;
+    void addListener(ListenerPtr listener);
+    void removeListener(ListenerPtr listener);
+
+private:
+    static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
+
+    UpnpInstanceWrapper();
+    ~UpnpInstanceWrapper();
+};
+
+// **************************
+// Helper functions
+// **************************
+
+#if UPNP_VERSION < 10623
+/*
+ * Compat functions and typedefs for libupnp prior to 1.8
+ */
+
+typedef Upnp_Discovery UpnpDiscovery;
+typedef Upnp_Action_Complete UpnpActionComplete;
+
+inline const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
+{
+  return p_discovery->Location;
+}
+
+inline const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
+{
+  return p_discovery->DeviceId;
+}
+
+inline static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
+{
+  return p_result->ActionResult;
+}
+#endif
+
+/*
+ * Returns the value of a child element, or NULL on error
+ */
+inline const char* xml_getChildElementValue( IXML_Element* p_parent,
+                                      const char*   psz_tag_name )
+{
+    assert( p_parent );
+    assert( psz_tag_name );
+
+    IXML_NodeList* p_node_list;
+    p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
+    if ( !p_node_list ) return NULL;
+
+    IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
+    ixmlNodeList_free( p_node_list );
+    if ( !p_element )   return NULL;
+
+    IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
+    if ( !p_text_node ) return NULL;
+
+    return ixmlNode_getNodeValue( p_text_node );
+}
+
+#ifdef _WIN32
+
+inline IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
+{
+    const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
+
+    IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
+    while (p_multicast != NULL)
+    {
+        if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
+            return p_multicast;
+        p_multicast = p_multicast->Next;
+    }
+    return NULL;
+}
+
+inline bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
+{
+    if ( p_adapter->OperStatus != IfOperStatusUp )
+        return false;
+    if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
+    {
+        IP_ADAPTER_ADDRESSES_XP* p_adapter_xp = reinterpret_cast<IP_ADAPTER_ADDRESSES_XP*>( p_adapter );
+        // On Windows Server 2003 and Windows XP, this member is zero if IPv4 is not available on the interface.
+        if (ipv6)
+            return p_adapter_xp->Ipv6IfIndex != 0;
+        return p_adapter_xp->IfIndex != 0;
+    }
+    IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
+    if (p_adapter_lh->FirstGatewayAddress == NULL)
+        return false;
+    if (ipv6)
+        return p_adapter_lh->Ipv6Enabled;
+    return p_adapter_lh->Ipv4Enabled;
+}
+
+inline IP_ADAPTER_ADDRESSES* ListAdapters()
+{
+    ULONG addrSize;
+    const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
+    IP_ADAPTER_ADDRESSES* addresses = NULL;
+    HRESULT hr;
+
+    /**
+     * https://msdn.microsoft.com/en-us/library/aa365915.aspx
+     *
+     * The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a
+     * 15KB working buffer pointed to by the AdapterAddresses parameter. On typical computers,
+     * this dramatically reduces the chances that the GetAdaptersAddresses function returns
+     * ERROR_BUFFER_OVERFLOW, which would require calling GetAdaptersAddresses function multiple
+     * times. The example code illustrates this method of use.
+     */
+    addrSize = 15 * 1024;
+    do
+    {
+        free(addresses);
+        addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
+        if (addresses == NULL)
+            return NULL;
+        hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
+    } while (hr == ERROR_BUFFER_OVERFLOW);
+    if (hr != NO_ERROR) {
+        free(addresses);
+        return NULL;
+    }
+    return addresses;
+}
+
+#ifdef UPNP_ENABLE_IPV6
+
+inline char* getPreferedAdapter()
+{
+    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
+
+    addresses = ListAdapters();
+    if (addresses == NULL)
+        return NULL;
+
+    /* find one with multicast capabilities */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable( p_adapter, true ))
+        {
+            /* make sure it supports 239.255.255.250 */
+            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
+            if (p_multicast != NULL)
+            {
+                char* res = FromWide( p_adapter->FriendlyName );
+                free( addresses );
+                return res;
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+    free(addresses);
+    return NULL;
+}
+
+#else
+
+inline char *getIpv4ForMulticast()
+{
+    IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
+    wchar_t psz_uri[32];
+    DWORD strSize;
+    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
+
+    addresses = ListAdapters();
+    if (addresses == NULL)
+        return NULL;
+
+    /* find one with multicast capabilities */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable( p_adapter, false ))
+        {
+            /* make sure it supports 239.255.255.250 */
+            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
+            if (p_multicast != NULL)
+            {
+                /* get an IPv4 address */
+                IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
+                while (p_unicast != NULL)
+                {
+                    strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+                    if( WSAAddressToString( p_unicast->Address.lpSockaddr,
+                                            p_unicast->Address.iSockaddrLength,
+                                            NULL, psz_uri, &strSize ) == 0 )
+                    {
+                        if ( p_best_ip == NULL ||
+                             p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
+                        {
+                            p_best_ip = p_unicast;
+                        }
+                    }
+                    p_unicast = p_unicast->Next;
+                }
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+
+    if ( p_best_ip != NULL )
+        goto done;
+
+    /* find any with IPv4 */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable(p_adapter, false))
+        {
+            /* get an IPv4 address */
+            IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
+            while (p_unicast != NULL)
+            {
+                strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+                if( WSAAddressToString( p_unicast->Address.lpSockaddr,
+                                        p_unicast->Address.iSockaddrLength,
+                                        NULL, psz_uri, &strSize ) == 0 )
+                {
+                    if ( p_best_ip == NULL ||
+                         p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
+                    {
+                        p_best_ip = p_unicast;
+                    }
+                }
+                p_unicast = p_unicast->Next;
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+
+done:
+    if (p_best_ip != NULL)
+    {
+        strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+        WSAAddressToString( p_best_ip->Address.lpSockaddr,
+                            p_best_ip->Address.iSockaddrLength,
+                            NULL, psz_uri, &strSize );
+        free(addresses);
+        return FromWide( psz_uri );
+    }
+    free(addresses);
+    return NULL;
+}
+#endif /* UPNP_ENABLE_IPV6 */
+#else /* _WIN32 */
+
+#ifdef UPNP_ENABLE_IPV6
+
+inline char *getPreferedAdapter()
+{
+    return NULL;
+}
+
+#else
+
+inline char *getIpv4ForMulticast()
+{
+    return NULL;
+}
+
+#endif
+
+#endif /* _WIN32 */
index 7f8ed23..ae23507 100644 (file)
@@ -30,7 +30,6 @@
 #include <vlc_plugin.h>
 #include <vlc_interrupt.h>
 #include <vlc_services_discovery.h>
-#include <vlc_charset.h>
 
 #include <assert.h>
 #include <limits.h>
 #include <set>
 #include <string>
 
-#if UPNP_VERSION < 10623
-/*
- * Compat functions and typedefs for libupnp prior to 1.8
- */
-
-typedef Upnp_Discovery UpnpDiscovery;
-typedef Upnp_Action_Complete UpnpActionComplete;
-
-static const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
-{
-  return p_discovery->Location;
-}
-
-static const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
-{
-  return p_discovery->DeviceId;
-}
-
-static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
-{
-  return p_result->ActionResult;
-}
-#endif
-
 /*
  * Constants
 */
@@ -93,10 +68,6 @@ struct access_sys_t
     UpnpInstanceWrapper* p_upnp;
 };
 
-UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
-UpnpInstanceWrapper::Listeners UpnpInstanceWrapper::s_listeners;
-vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
-
 /*
  * VLC callback prototypes
  */
@@ -140,30 +111,6 @@ vlc_module_begin()
     VLC_SD_PROBE_SUBMODULE
 vlc_module_end()
 
-
-/*
- * Returns the value of a child element, or NULL on error
- */
-const char* xml_getChildElementValue( IXML_Element* p_parent,
-                                      const char*   psz_tag_name )
-{
-    assert( p_parent );
-    assert( psz_tag_name );
-
-    IXML_NodeList* p_node_list;
-    p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
-    if ( !p_node_list ) return NULL;
-
-    IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
-    ixmlNodeList_free( p_node_list );
-    if ( !p_element )   return NULL;
-
-    IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
-    if ( !p_text_node ) return NULL;
-
-    return ixmlNode_getNodeValue( p_text_node );
-}
-
 /*
  * Extracts the result document from a SOAP response
  */
@@ -1313,329 +1260,3 @@ static void Close( vlc_object_t* p_this )
 }
 
 }
-
-UpnpInstanceWrapper::UpnpInstanceWrapper()
-    : m_handle( -1 )
-    , m_refcount( 0 )
-{
-}
-
-UpnpInstanceWrapper::~UpnpInstanceWrapper()
-{
-    UpnpUnRegisterClient( m_handle );
-    UpnpFinish();
-}
-
-#ifdef _WIN32
-
-static IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
-{
-    const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
-
-    IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
-    while (p_multicast != NULL)
-    {
-        if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
-            return p_multicast;
-        p_multicast = p_multicast->Next;
-    }
-    return NULL;
-}
-
-static bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
-{
-    if ( p_adapter->OperStatus != IfOperStatusUp )
-        return false;
-    if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
-    {
-        IP_ADAPTER_ADDRESSES_XP* p_adapter_xp = reinterpret_cast<IP_ADAPTER_ADDRESSES_XP*>( p_adapter );
-        // On Windows Server 2003 and Windows XP, this member is zero if IPv4 is not available on the interface.
-        if (ipv6)
-            return p_adapter_xp->Ipv6IfIndex != 0;
-        return p_adapter_xp->IfIndex != 0;
-    }
-    IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
-    if (p_adapter_lh->FirstGatewayAddress == NULL)
-        return false;
-    if (ipv6)
-        return p_adapter_lh->Ipv6Enabled;
-    return p_adapter_lh->Ipv4Enabled;
-}
-
-static IP_ADAPTER_ADDRESSES* ListAdapters()
-{
-    ULONG addrSize;
-    const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
-    IP_ADAPTER_ADDRESSES* addresses = NULL;
-    HRESULT hr;
-
-    /**
-     * https://msdn.microsoft.com/en-us/library/aa365915.aspx
-     *
-     * The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a
-     * 15KB working buffer pointed to by the AdapterAddresses parameter. On typical computers,
-     * this dramatically reduces the chances that the GetAdaptersAddresses function returns
-     * ERROR_BUFFER_OVERFLOW, which would require calling GetAdaptersAddresses function multiple
-     * times. The example code illustrates this method of use.
-     */
-    addrSize = 15 * 1024;
-    do
-    {
-        free(addresses);
-        addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
-        if (addresses == NULL)
-            return NULL;
-        hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
-    } while (hr == ERROR_BUFFER_OVERFLOW);
-    if (hr != NO_ERROR) {
-        free(addresses);
-        return NULL;
-    }
-    return addresses;
-}
-
-#ifdef UPNP_ENABLE_IPV6
-
-static char* getPreferedAdapter()
-{
-    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
-
-    addresses = ListAdapters();
-    if (addresses == NULL)
-        return NULL;
-
-    /* find one with multicast capabilities */
-    p_adapter = addresses;
-    while (p_adapter != NULL)
-    {
-        if (isAdapterSuitable( p_adapter, true ))
-        {
-            /* make sure it supports 239.255.255.250 */
-            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
-            if (p_multicast != NULL)
-            {
-                char* res = FromWide( p_adapter->FriendlyName );
-                free( addresses );
-                return res;
-            }
-        }
-        p_adapter = p_adapter->Next;
-    }
-    free(addresses);
-    return NULL;
-}
-
-#else
-
-static char *getIpv4ForMulticast()
-{
-    IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
-    wchar_t psz_uri[32];
-    DWORD strSize;
-    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
-
-    addresses = ListAdapters();
-    if (addresses == NULL)
-        return NULL;
-
-    /* find one with multicast capabilities */
-    p_adapter = addresses;
-    while (p_adapter != NULL)
-    {
-        if (isAdapterSuitable( p_adapter, false ))
-        {
-            /* make sure it supports 239.255.255.250 */
-            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
-            if (p_multicast != NULL)
-            {
-                /* get an IPv4 address */
-                IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
-                while (p_unicast != NULL)
-                {
-                    strSize = sizeof( psz_uri ) / sizeof( wchar_t );
-                    if( WSAAddressToString( p_unicast->Address.lpSockaddr,
-                                            p_unicast->Address.iSockaddrLength,
-                                            NULL, psz_uri, &strSize ) == 0 )
-                    {
-                        if ( p_best_ip == NULL ||
-                             p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
-                        {
-                            p_best_ip = p_unicast;
-                        }
-                    }
-                    p_unicast = p_unicast->Next;
-                }
-            }
-        }
-        p_adapter = p_adapter->Next;
-    }
-
-    if ( p_best_ip != NULL )
-        goto done;
-
-    /* find any with IPv4 */
-    p_adapter = addresses;
-    while (p_adapter != NULL)
-    {
-        if (isAdapterSuitable(p_adapter, false))
-        {
-            /* get an IPv4 address */
-            IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
-            while (p_unicast != NULL)
-            {
-                strSize = sizeof( psz_uri ) / sizeof( wchar_t );
-                if( WSAAddressToString( p_unicast->Address.lpSockaddr,
-                                        p_unicast->Address.iSockaddrLength,
-                                        NULL, psz_uri, &strSize ) == 0 )
-                {
-                    if ( p_best_ip == NULL ||
-                         p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
-                    {
-                        p_best_ip = p_unicast;
-                    }
-                }
-                p_unicast = p_unicast->Next;
-            }
-        }
-        p_adapter = p_adapter->Next;
-    }
-
-done:
-    if (p_best_ip != NULL)
-    {
-        strSize = sizeof( psz_uri ) / sizeof( wchar_t );
-        WSAAddressToString( p_best_ip->Address.lpSockaddr,
-                            p_best_ip->Address.iSockaddrLength,
-                            NULL, psz_uri, &strSize );
-        free(addresses);
-        return FromWide( psz_uri );
-    }
-    free(addresses);
-    return NULL;
-}
-#endif /* UPNP_ENABLE_IPV6 */
-#else /* _WIN32 */
-
-#ifdef UPNP_ENABLE_IPV6
-
-static char *getPreferedAdapter()
-{
-    return NULL;
-}
-
-#else
-
-static char *getIpv4ForMulticast()
-{
-    return NULL;
-}
-
-#endif
-
-#endif /* _WIN32 */
-
-UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
-{
-    vlc_mutex_locker lock( &s_lock );
-    if ( s_instance == NULL )
-    {
-        UpnpInstanceWrapper* instance = new(std::nothrow) UpnpInstanceWrapper;
-        if ( unlikely( !instance ) )
-        {
-            return NULL;
-        }
-
-    #ifdef UPNP_ENABLE_IPV6
-        char* psz_miface = var_InheritString( p_obj, "miface" );
-        if (psz_miface == NULL)
-            psz_miface = getPreferedAdapter();
-        msg_Info( p_obj, "Initializing libupnp on '%s' interface", psz_miface ? psz_miface : "default" );
-        int i_res = UpnpInit2( psz_miface, 0 );
-        free( psz_miface );
-    #else
-        /* If UpnpInit2 isnt available, initialize on first IPv4-capable interface */
-        char *psz_hostip = getIpv4ForMulticast();
-        int i_res = UpnpInit( psz_hostip, 0 );
-        free(psz_hostip);
-    #endif /* UPNP_ENABLE_IPV6 */
-        if( i_res != UPNP_E_SUCCESS )
-        {
-            msg_Err( p_obj, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );
-            delete instance;
-            return NULL;
-        }
-
-        ixmlRelaxParser( 1 );
-
-        /* Register a control point */
-        i_res = UpnpRegisterClient( Callback, instance, &instance->m_handle );
-        if( i_res != UPNP_E_SUCCESS )
-        {
-            msg_Err( p_obj, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
-            delete instance;
-            return NULL;
-        }
-
-        /* libupnp does not treat a maximum content length of 0 as unlimited
-         * until 64dedf (~ pupnp v1.6.7) and provides no sane way to discriminate
-         * between versions */
-        if( (i_res = UpnpSetMaxContentLength( INT_MAX )) != UPNP_E_SUCCESS )
-        {
-            msg_Err( p_obj, "Failed to set maximum content length: %s",
-                    UpnpGetErrorMessage( i_res ));
-            delete instance;
-            return NULL;
-        }
-        s_instance = instance;
-    }
-    s_instance->m_refcount++;
-    return s_instance;
-}
-
-void UpnpInstanceWrapper::release()
-{
-    UpnpInstanceWrapper *p_delete = NULL;
-    vlc_mutex_lock( &s_lock );
-    if (--s_instance->m_refcount == 0)
-    {
-        p_delete = s_instance;
-        s_instance = NULL;
-    }
-    vlc_mutex_unlock( &s_lock );
-    delete p_delete;
-}
-
-UpnpClient_Handle UpnpInstanceWrapper::handle() const
-{
-    return m_handle;
-}
-
-int UpnpInstanceWrapper::Callback(Upnp_EventType event_type, UpnpEventPtr p_event, void *p_user_data)
-{
-    for (Listeners::iterator iter = s_listeners.begin(); iter != s_listeners.end(); ++iter)
-    {
-        (*iter)->onEvent(event_type, p_event, p_user_data);
-    }
-
-    return 0;
-}
-
-void UpnpInstanceWrapper::addListener(ListenerPtr listener)
-{
-    vlc_mutex_lock( &s_lock );
-    if ( std::find( s_listeners.begin(), s_listeners.end(), listener) != s_listeners.end() )
-        return;
-    s_listeners.push_back( std::move(listener) );
-    vlc_mutex_unlock( &s_lock );
-}
-
-void UpnpInstanceWrapper::removeListener(ListenerPtr listener)
-{
-    vlc_mutex_lock( &s_lock );
-    Listeners::iterator iter = std::find( s_listeners.begin(), s_listeners.end(), listener );
-    if ( iter == s_listeners.end() )
-        return;
-
-    s_listeners.erase( iter );
-    vlc_mutex_unlock( &s_lock );
-}
index 325e125..3ff63e8 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "upnp-wrapper.hpp"
 
 #include <vector>
 #include <string>
-#include <memory>
 
 #ifdef _WIN32
 #include <windows.h>
 #include <wincrypt.h>
 #endif
-#include <upnp/upnp.h>
-#include <upnp/upnptools.h>
 
-#include <vlc_common.h>
 #include <vlc_url.h>
 
-#if UPNP_VERSION < 10800
-typedef void* UpnpEventPtr;
-#else
-typedef const void* UpnpEventPtr;
-#endif
-
-namespace SD
-{
-    class MediaServerList;
-}
-
-/*
- * libUpnp allows only one instance per process, so we have to share one for
- * both SD & Access module
- * Since the callback is bound to the UpnpClient_Handle, we have to register
- * a wrapper callback, in order for the access module to be able to initialize
- * libUpnp first.
- * When a SD wishes to use libUpnp, it will provide its own callback, that the
- * wrapper will forward.
- * This way, we always have a register callback & a client handle.
- */
-class UpnpInstanceWrapper
-{
-public:
-    class Listener
-    {
-        public:
-        virtual ~Listener() {}
-        virtual int onEvent( Upnp_EventType event_type,
-                             UpnpEventPtr p_event,
-                             void* p_user_data ) = 0;
-    };
-
-private:
-    static UpnpInstanceWrapper* s_instance;
-    static vlc_mutex_t s_lock;
-    UpnpClient_Handle m_handle;
-    int m_refcount;
-    typedef std::shared_ptr<Listener> ListenerPtr;
-    typedef std::vector<ListenerPtr> Listeners;
-    static Listeners s_listeners;
-
-public:
-    // This increases the refcount before returning the instance
-    static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
-    void release();
-    UpnpClient_Handle handle() const;
-    void addListener(ListenerPtr listener);
-    void removeListener(ListenerPtr listener);
-
-private:
-    static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
-
-    UpnpInstanceWrapper();
-    ~UpnpInstanceWrapper();
-};
-
 namespace SD
 {