3997 lines
99 KiB
C
3997 lines
99 KiB
C
/*******************************************************************************
|
|
*
|
|
* Copyright (c) 2000-2003 Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* - Neither name of Intel Corporation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
/*!
|
|
* \file
|
|
*/
|
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
#ifdef WIN32
|
|
/* Do not include these files */
|
|
#else
|
|
#include <sys/param.h>
|
|
#if defined(__sun)
|
|
#include <fcntl.h>
|
|
#include <sys/sockio.h>
|
|
#elif defined(BSD) && BSD >= 199306
|
|
#include <ifaddrs.h>
|
|
/* Do not move or remove the include below for "sys/socket"!
|
|
* Will break FreeBSD builds. */
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/utsname.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
|
|
#include "upnpapi.h"
|
|
#include "httpreadwrite.h"
|
|
#include "membuffer.h"
|
|
#include "ssdplib.h"
|
|
#include "soaplib.h"
|
|
#include "ThreadPool.h"
|
|
#include "sysdep.h"
|
|
#include "uuid.h"
|
|
|
|
|
|
/* Needed for GENA */
|
|
#include "gena.h"
|
|
#include "miniserver.h"
|
|
#include "service_table.h"
|
|
|
|
|
|
#ifdef INTERNAL_WEB_SERVER
|
|
#include "urlconfig.h"
|
|
#include "VirtualDir.h"
|
|
#include "webserver.h"
|
|
#endif /* INTERNAL_WEB_SERVER */
|
|
|
|
|
|
#ifndef IN6_IS_ADDR_GLOBAL
|
|
#define IN6_IS_ADDR_GLOBAL(a) \
|
|
(((((__const uint8_t *) (a))[0] & htonl(0xff000000)) <= htonl(0x3f000000) \
|
|
&& (((__const uint8_t *) (a))[0] & htonl(0xff000000)) >= htonl(0x20000000)))
|
|
#endif /* IS ADDR GLOBAL */
|
|
|
|
#ifndef IN6_IS_ADDR_ULA
|
|
#define IN6_IS_ADDR_ULA(a) \
|
|
((((__const uint32_t *) (a))[0] & htonl(0xfe000000)) \
|
|
== htonl (0xfc000000))
|
|
#endif /* IS ADDR ULA */
|
|
|
|
/*! This structure is for virtual directory callbacks */
|
|
struct VirtualDirCallbacks virtualDirCallback;
|
|
|
|
/*! Pointer to the virtual directory list. */
|
|
virtualDirList *pVirtualDirList;
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
/*! Mutex to synchronize the subscription handling at the client side. */
|
|
ithread_mutex_t GlobalClientSubscribeMutex;
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
/*! rwlock to synchronize handles (root device or control point handle). */
|
|
ithread_rwlock_t GlobalHndRWLock;
|
|
|
|
|
|
/*! Mutex to synchronize the uuid creation process. */
|
|
ithread_mutex_t gUUIDMutex;
|
|
|
|
/*! Initialization mutex. */
|
|
ithread_mutex_t gSDKInitMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/*! Global timer thread. */
|
|
TimerThread gTimerThread;
|
|
|
|
/*! Send thread pool. */
|
|
ThreadPool gSendThreadPool;
|
|
|
|
/*! Receive thread pool. */
|
|
ThreadPool gRecvThreadPool;
|
|
|
|
/*! Mini server thread pool. */
|
|
ThreadPool gMiniServerThreadPool;
|
|
|
|
/*! Flag to indicate the state of web server */
|
|
WebServerState bWebServerState = WEB_SERVER_DISABLED;
|
|
|
|
/*! Static buffer to contain interface name. (extern'ed in upnp.h) */
|
|
char gIF_NAME[LINE_SIZE] = { '\0' };
|
|
|
|
/*! Static buffer to contain interface IPv4 address. (extern'ed in upnp.h) */
|
|
char gIF_IPV4[22]/* INET_ADDRSTRLEN*/ = { '\0' };
|
|
|
|
/*! Static buffer to contain interface IPv6 address. (extern'ed in upnp.h) */
|
|
char gIF_IPV6[65]/* INET6_ADDRSTRLEN*/ = { '\0' };
|
|
|
|
/*! Static buffer to contain interface ULA or GUA IPv6 address. (extern'ed in upnp.h) */
|
|
char gIF_IPV6_ULA_GUA[INET6_ADDRSTRLEN] = { '\0' };
|
|
|
|
/*! Contains interface index. (extern'ed in upnp.h) */
|
|
unsigned gIF_INDEX = (unsigned)-1;
|
|
|
|
/*! local IPv4 port for the mini-server */
|
|
unsigned short LOCAL_PORT_V4;
|
|
|
|
/*! local IPv6 port for the mini-server */
|
|
unsigned short LOCAL_PORT_V6;
|
|
|
|
/*! UPnP device and control point handle table */
|
|
#define NUM_HANDLE 200
|
|
static void *HandleTable[NUM_HANDLE];
|
|
|
|
/*! a local dir which serves as webserver root */
|
|
extern membuffer gDocumentRootDir;
|
|
|
|
/*! Maximum content-length (in bytes) that the SDK will process on an incoming
|
|
* packet. Content-Length exceeding this size will be not processed and
|
|
* error 413 (HTTP Error Code) will be returned to the remote end point. */
|
|
size_t g_maxContentLength = DEFAULT_SOAP_CONTENT_LENGTH;
|
|
|
|
/*! Global variable to denote the state of Upnp SDK == 0 if uninitialized,
|
|
* == 1 if initialized. */
|
|
int UpnpSdkInit = 0;
|
|
|
|
/*! Global variable to denote the state of Upnp SDK client registration.
|
|
* == 0 if unregistered, == 1 if registered. */
|
|
int UpnpSdkClientRegistered = 0;
|
|
|
|
/*! Global variable to denote the state of Upnp SDK IPv4 device registration.
|
|
* == 0 if unregistered, == 1 if registered. */
|
|
int UpnpSdkDeviceRegisteredV4 = 0;
|
|
|
|
/*! Global variable to denote the state of Upnp SDK IPv6 device registration.
|
|
* == 0 if unregistered, == 1 if registered. */
|
|
int UpnpSdkDeviceregisteredV6 = 0;
|
|
|
|
/*! Global variable used in discovery notifications. */
|
|
Upnp_SID gUpnpSdkNLSuuid;
|
|
|
|
|
|
/*!
|
|
* \brief (Windows Only) Initializes the Windows Winsock library.
|
|
*
|
|
* \return UPNP_E_SUCCESS on success, UPNP_E_INIT_FAILED on failure.
|
|
*/
|
|
static int WinsockInit(void)
|
|
{
|
|
int retVal = UPNP_E_SUCCESS;
|
|
#ifdef WIN32
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int err;
|
|
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
err = WSAStartup(wVersionRequested, &wsaData);
|
|
if (err != 0) {
|
|
/* Tell the user that we could not find a usable */
|
|
/* WinSock DLL. */
|
|
retVal = UPNP_E_INIT_FAILED;
|
|
goto exit_function;
|
|
}
|
|
/* Confirm that the WinSock DLL supports 2.2.
|
|
* Note that if the DLL supports versions greater
|
|
* than 2.2 in addition to 2.2, it will still return
|
|
* 2.2 in wVersion since that is the version we
|
|
* requested. */
|
|
if (LOBYTE(wsaData.wVersion) != 2 ||
|
|
HIBYTE(wsaData.wVersion) != 2) {
|
|
/* Tell the user that we could not find a usable
|
|
* WinSock DLL. */
|
|
WSACleanup();
|
|
retVal = UPNP_E_INIT_FAILED;
|
|
goto exit_function;
|
|
}
|
|
/* The WinSock DLL is acceptable. Proceed. */
|
|
exit_function:
|
|
#else
|
|
#endif
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Initializes the global mutexes used by the UPnP SDK.
|
|
*
|
|
* \return UPNP_E_SUCCESS on success or UPNP_E_INIT_FAILED if a mutex could not
|
|
* be initialized.
|
|
*/
|
|
static int UpnpInitMutexes(void)
|
|
{
|
|
#ifdef __CYGWIN__
|
|
/* On Cygwin, pthread_mutex_init() fails without this memset. */
|
|
/* TODO: Fix Cygwin so we don't need this memset(). */
|
|
memset(&GlobalHndRWLock, 0, sizeof(GlobalHndRWLock));
|
|
#endif
|
|
if (ithread_rwlock_init(&GlobalHndRWLock, NULL) != 0) {
|
|
return UPNP_E_INIT_FAILED;
|
|
}
|
|
|
|
if (ithread_mutex_init(&gUUIDMutex, NULL) != 0) {
|
|
return UPNP_E_INIT_FAILED;
|
|
}
|
|
/* initialize subscribe mutex. */
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
if (ithread_mutex_init(&GlobalClientSubscribeMutex, NULL) != 0) {
|
|
return UPNP_E_INIT_FAILED;
|
|
}
|
|
#endif
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Initializes the global threadm pools used by the UPnP SDK.
|
|
*
|
|
* \return UPNP_E_SUCCESS on success or UPNP_E_INIT_FAILED if a mutex could not
|
|
* be initialized.
|
|
*/
|
|
static int UpnpInitThreadPools(void)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
ThreadPoolAttr attr;
|
|
|
|
TPAttrInit(&attr);
|
|
TPAttrSetMaxThreads(&attr, MAX_THREADS);
|
|
TPAttrSetMinThreads(&attr, MIN_THREADS);
|
|
TPAttrSetStackSize(&attr, THREAD_STACK_SIZE);
|
|
TPAttrSetJobsPerThread(&attr, JOBS_PER_THREAD);
|
|
TPAttrSetIdleTime(&attr, THREAD_IDLE_TIME);
|
|
TPAttrSetMaxJobsTotal(&attr, MAX_JOBS_TOTAL);
|
|
|
|
if (ThreadPoolInit(&gSendThreadPool, &attr) != UPNP_E_SUCCESS) {
|
|
ret = UPNP_E_INIT_FAILED;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (ThreadPoolInit(&gRecvThreadPool, &attr) != UPNP_E_SUCCESS) {
|
|
ret = UPNP_E_INIT_FAILED;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (ThreadPoolInit(&gMiniServerThreadPool, &attr) != UPNP_E_SUCCESS) {
|
|
ret = UPNP_E_INIT_FAILED;
|
|
goto exit_function;
|
|
}
|
|
|
|
exit_function:
|
|
if (ret != UPNP_E_SUCCESS) {
|
|
UpnpSdkInit = 0;
|
|
UpnpFinish();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Performs the initial steps in initializing the UPnP SDK.
|
|
*
|
|
* \li Winsock library is initialized for the process (Windows specific).
|
|
* \li The logging (for debug messages) is initialized.
|
|
* \li Mutexes, Handle table and thread pools are allocated and initialized.
|
|
* \li Callback functions for SOAP and GENA are set, if they're enabled.
|
|
* \li The SDK timer thread is initialized.
|
|
*
|
|
* \return UPNP_E_SUCCESS on success.
|
|
*/
|
|
static int UpnpInitPreamble(void)
|
|
{
|
|
int retVal = UPNP_E_SUCCESS;
|
|
int i;
|
|
uuid_upnp nls_uuid;
|
|
|
|
retVal = WinsockInit();
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
return retVal;
|
|
}
|
|
|
|
/* needed by SSDP or other parts. */
|
|
srand((unsigned int)time(NULL));
|
|
|
|
/* Initialize debug output. */
|
|
retVal = UpnpInitLog();
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
/* UpnpInitLog does not return a valid UPNP_E_*. */
|
|
return UPNP_E_INIT_FAILED;
|
|
}
|
|
|
|
UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, "Inside UpnpInitPreamble\n" );
|
|
|
|
/* Initialize SDK global mutexes. */
|
|
retVal = UpnpInitMutexes();
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
return retVal;
|
|
}
|
|
|
|
/* Create the NLS uuid. */
|
|
uuid_create(&nls_uuid);
|
|
uuid_unpack(&nls_uuid, gUpnpSdkNLSuuid);
|
|
|
|
/* Initializes the handle list. */
|
|
HandleLock();
|
|
for (i = 0; i < NUM_HANDLE; ++i) {
|
|
HandleTable[i] = NULL;
|
|
}
|
|
HandleUnlock();
|
|
|
|
/* Initialize SDK global thread pools. */
|
|
retVal = UpnpInitThreadPools();
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
return retVal;
|
|
}
|
|
|
|
#if EXCLUDE_SOAP == 0
|
|
SetSoapCallback(soap_device_callback);
|
|
#endif
|
|
|
|
#if EXCLUDE_GENA == 0
|
|
SetGenaCallback(genaCallback);
|
|
#endif
|
|
|
|
/* Initialize the SDK timer thread. */
|
|
retVal = TimerThreadInit( &gTimerThread, &gSendThreadPool );
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
UpnpFinish();
|
|
|
|
return retVal;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Finishes initializing the UPnP SDK.
|
|
* \li The MiniServer is started, if enabled.
|
|
* \li The WebServer is started, if enabled.
|
|
*
|
|
* \return UPNP_E_SUCCESS on success or UPNP_E_INIT_FAILED if a mutex could not
|
|
* be initialized.
|
|
*/
|
|
static int UpnpInitStartServers(
|
|
/*! [in] Local Port to listen for incoming connections. */
|
|
unsigned short DestPort)
|
|
{
|
|
int retVal = 0;
|
|
|
|
UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Entering UpnpInitStartServers\n" );
|
|
|
|
#if EXCLUDE_MINISERVER == 0
|
|
LOCAL_PORT_V4 = DestPort;
|
|
LOCAL_PORT_V6 = DestPort;
|
|
retVal = StartMiniServer(&LOCAL_PORT_V4, &LOCAL_PORT_V6);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Miniserver failed to start");
|
|
UpnpFinish();
|
|
return retVal;
|
|
}
|
|
#endif
|
|
|
|
#if EXCLUDE_WEB_SERVER == 0
|
|
membuffer_init(&gDocumentRootDir);
|
|
retVal = UpnpEnableWebserver(WEB_SERVER_ENABLED);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
UpnpFinish();
|
|
return retVal;
|
|
}
|
|
#endif
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Exiting UpnpInitStartServers\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
int UpnpInit(const char *HostIP, unsigned short DestPort)
|
|
{
|
|
int retVal = UPNP_E_SUCCESS;
|
|
|
|
/* Initializes the ithread library */
|
|
ithread_initialize_library();
|
|
|
|
ithread_mutex_lock(&gSDKInitMutex);
|
|
|
|
/* Check if we're already initialized. */
|
|
if (UpnpSdkInit == 1) {
|
|
retVal = UPNP_E_INIT;
|
|
goto exit_function;
|
|
}
|
|
|
|
/* Perform initialization preamble. */
|
|
retVal = UpnpInitPreamble();
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
goto exit_function;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"UpnpInit with HostIP=%s, DestPort=%d.\n",
|
|
HostIP ? HostIP : "", DestPort);
|
|
|
|
/* Verify HostIP, if provided, or find it ourselves. */
|
|
if (HostIP != NULL) {
|
|
strncpy(gIF_IPV4, HostIP, sizeof(gIF_IPV4));
|
|
} else {
|
|
if( getlocalhostname( gIF_IPV4, sizeof(gIF_IPV4) ) != UPNP_E_SUCCESS ) {
|
|
retVal = UPNP_E_INIT_FAILED;
|
|
goto exit_function;
|
|
}
|
|
}
|
|
|
|
/* Set the UpnpSdkInit flag to 1 to indicate we're successfully initialized. */
|
|
UpnpSdkInit = 1;
|
|
|
|
/* Finish initializing the SDK. */
|
|
retVal = UpnpInitStartServers(DestPort);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
UpnpSdkInit = 0;
|
|
goto exit_function;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Host Ip: %s Host Port: %d\n", gIF_IPV4,
|
|
LOCAL_PORT_V4);
|
|
|
|
exit_function:
|
|
ithread_mutex_unlock(&gSDKInitMutex);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
#ifdef UPNP_ENABLE_IPV6
|
|
int UpnpInit2(const char *IfName, unsigned short DestPort)
|
|
{
|
|
int retVal;
|
|
|
|
/* Initializes the ithread library */
|
|
ithread_initialize_library();
|
|
|
|
ithread_mutex_lock(&gSDKInitMutex);
|
|
|
|
/* Check if we're already initialized. */
|
|
if (UpnpSdkInit == 1) {
|
|
retVal = UPNP_E_INIT;
|
|
goto exit_function;
|
|
}
|
|
|
|
/* Perform initialization preamble. */
|
|
retVal = UpnpInitPreamble();
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
goto exit_function;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"UpnpInit2 with IfName=%s, DestPort=%d.\n",
|
|
IfName ? IfName : "", DestPort);
|
|
|
|
/* Retrieve interface information (Addresses, index, etc). */
|
|
retVal = UpnpGetIfInfo( IfName );
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
goto exit_function;
|
|
}
|
|
|
|
/* Set the UpnpSdkInit flag to 1 to indicate we're successfully initialized. */
|
|
UpnpSdkInit = 1;
|
|
|
|
/* Finish initializing the SDK. */
|
|
retVal = UpnpInitStartServers(DestPort);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
UpnpSdkInit = 0;
|
|
goto exit_function;
|
|
}
|
|
|
|
exit_function:
|
|
ithread_mutex_unlock(&gSDKInitMutex);
|
|
|
|
return retVal;
|
|
}
|
|
#endif
|
|
|
|
|
|
int UpnpFinish(void)
|
|
{
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
UpnpDevice_Handle device_handle;
|
|
#endif
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
UpnpClient_Handle client_handle;
|
|
#endif
|
|
struct Handle_Info *temp;
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Inside UpnpFinish: UpnpSdkInit is %d\n", UpnpSdkInit);
|
|
if (UpnpSdkInit == 1) {
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"UpnpFinish: UpnpSdkInit is ONE\n");
|
|
}
|
|
PrintThreadPoolStats(&gSendThreadPool, __FILE__, __LINE__, "Send Thread Pool");
|
|
PrintThreadPoolStats(&gRecvThreadPool, __FILE__, __LINE__, "Recv Thread Pool");
|
|
PrintThreadPoolStats(&gMiniServerThreadPool, __FILE__, __LINE__, "MiniServer Thread Pool");
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
if (GetDeviceHandleInfo(AF_INET, &device_handle, &temp) == HND_DEVICE ) {
|
|
UpnpUnRegisterRootDevice(device_handle);
|
|
}
|
|
if (GetDeviceHandleInfo(AF_INET6, &device_handle, &temp) == HND_DEVICE ) {
|
|
UpnpUnRegisterRootDevice(device_handle);
|
|
}
|
|
#endif
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
if (GetClientHandleInfo(&client_handle, &temp) == HND_CLIENT) {
|
|
UpnpUnRegisterClient(client_handle);
|
|
}
|
|
#endif
|
|
|
|
TimerThreadShutdown(&gTimerThread);
|
|
StopMiniServer();
|
|
|
|
#if EXCLUDE_WEB_SERVER == 0
|
|
web_server_destroy();
|
|
#endif
|
|
|
|
ThreadPoolShutdown(&gMiniServerThreadPool);
|
|
PrintThreadPoolStats(&gMiniServerThreadPool, __FILE__, __LINE__, "MiniServer Thread Pool");
|
|
ThreadPoolShutdown(&gRecvThreadPool);
|
|
PrintThreadPoolStats(&gSendThreadPool, __FILE__, __LINE__, "Send Thread Pool");
|
|
ThreadPoolShutdown(&gSendThreadPool);
|
|
PrintThreadPoolStats(&gRecvThreadPool, __FILE__, __LINE__, "Recv Thread Pool");
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ithread_mutex_destroy(&GlobalClientSubscribeMutex);
|
|
#endif
|
|
ithread_rwlock_destroy(&GlobalHndRWLock);
|
|
ithread_mutex_destroy(&gUUIDMutex);
|
|
|
|
/* remove all virtual dirs */
|
|
UpnpRemoveAllVirtualDirs();
|
|
|
|
/* Clean-up ithread library resources */
|
|
ithread_cleanup_library();
|
|
|
|
UpnpSdkInit = 0;
|
|
UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Exiting UpnpFinish: UpnpSdkInit is :%d:\n", UpnpSdkInit);
|
|
UpnpCloseLog();
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
unsigned short UpnpGetServerPort(void)
|
|
{
|
|
if (UpnpSdkInit != 1) {
|
|
return 0;
|
|
}
|
|
|
|
return LOCAL_PORT_V4;
|
|
}
|
|
|
|
#ifdef UPNP_ENABLE_IPV6
|
|
unsigned short UpnpGetServerPort6(void)
|
|
{
|
|
if (UpnpSdkInit != 1) {
|
|
return 0;
|
|
}
|
|
|
|
return LOCAL_PORT_V6;
|
|
}
|
|
#endif
|
|
|
|
|
|
char *UpnpGetServerIpAddress(void)
|
|
{
|
|
if (UpnpSdkInit != 1) {
|
|
return NULL;
|
|
}
|
|
|
|
return gIF_IPV4;
|
|
}
|
|
|
|
|
|
char *UpnpGetServerIp6Address(void)
|
|
{
|
|
if( UpnpSdkInit != 1 ) {
|
|
return NULL;
|
|
}
|
|
|
|
return gIF_IPV6;
|
|
}
|
|
|
|
char *UpnpGetServerUlaGuaIp6Address(void)
|
|
{
|
|
if( UpnpSdkInit != 1 ) {
|
|
return NULL;
|
|
}
|
|
|
|
return gIF_IPV6_ULA_GUA;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Get a free handle.
|
|
*
|
|
* \return On success, an integer greater than zero or UPNP_E_OUTOF_HANDLE on
|
|
* failure.
|
|
*/
|
|
static int GetFreeHandle()
|
|
{
|
|
/* Handle 0 is not used as NULL translates to 0 when passed as a handle */
|
|
int i = 1;
|
|
|
|
while (i < NUM_HANDLE && HandleTable[i] != NULL) {
|
|
++i;
|
|
}
|
|
|
|
if (i == NUM_HANDLE) {
|
|
return UPNP_E_OUTOF_HANDLE;
|
|
} else {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Free handle.
|
|
*
|
|
* \return UPNP_E_SUCCESS if successful or UPNP_E_INVALID_HANDLE if not
|
|
*/
|
|
static int FreeHandle(
|
|
/*! [in] Handle index. */
|
|
int Upnp_Handle)
|
|
{
|
|
int ret = UPNP_E_INVALID_HANDLE;
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"FreeHandle: entering, Handle is %d\n", Upnp_Handle);
|
|
|
|
if (Upnp_Handle < 1 || Upnp_Handle >= NUM_HANDLE) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"FreeHandle: Handle %d is out of range\n",
|
|
Upnp_Handle);
|
|
} else if (HandleTable[Upnp_Handle] == NULL) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"FreeHandle: HandleTable[%d] is NULL\n",
|
|
Upnp_Handle);
|
|
} else {
|
|
free( HandleTable[Upnp_Handle] );
|
|
HandleTable[Upnp_Handle] = NULL;
|
|
ret = UPNP_E_SUCCESS;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"FreeHandle: exiting, ret = %d.\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpRegisterRootDevice(
|
|
const char *DescUrl,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie,
|
|
UpnpDevice_Handle *Hnd)
|
|
{
|
|
struct Handle_Info *HInfo = NULL;
|
|
int retVal = 0;
|
|
int hasServiceTable = 0;
|
|
|
|
HandleLock();
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpRegisterRootDevice\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
retVal = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (Hnd == NULL ||
|
|
Fun == NULL ||
|
|
DescUrl == NULL ||
|
|
strlen(DescUrl) == 0) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (UpnpSdkDeviceRegisteredV4 == 1) {
|
|
retVal = UPNP_E_ALREADY_REGISTERED;
|
|
goto exit_function;
|
|
}
|
|
|
|
*Hnd = GetFreeHandle();
|
|
if (*Hnd == UPNP_E_OUTOF_HANDLE) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
|
|
HInfo = (struct Handle_Info *)malloc(sizeof (struct Handle_Info));
|
|
if (HInfo == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
HandleTable[*Hnd] = HInfo;
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Root device URL is %s\n", DescUrl );
|
|
|
|
HInfo->aliasInstalled = 0;
|
|
HInfo->HType = HND_DEVICE;
|
|
strcpy(HInfo->DescURL, DescUrl);
|
|
HInfo->Callback = Fun;
|
|
HInfo->Cookie = (void *)Cookie;
|
|
HInfo->MaxAge = DEFAULT_MAXAGE;
|
|
HInfo->DeviceList = NULL;
|
|
HInfo->ServiceList = NULL;
|
|
HInfo->DescDocument = NULL;
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListInit(&HInfo->SsdpSearchList, NULL, NULL);
|
|
HInfo->ClientSubList = NULL;
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
HInfo->MaxSubscriptions = UPNP_INFINITE;
|
|
HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE;
|
|
HInfo->DeviceAf = AF_INET;
|
|
|
|
retVal = UpnpDownloadXmlDoc(HInfo->DescURL, &(HInfo->DescDocument));
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice: error downloading Document: %d\n",
|
|
retVal);
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListDestroy(&HInfo->SsdpSearchList, 0);
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
FreeHandle(*Hnd);
|
|
goto exit_function;
|
|
}
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice: Valid Description\n"
|
|
"UpnpRegisterRootDevice: DescURL : %s\n",
|
|
HInfo->DescURL);
|
|
|
|
HInfo->DeviceList =
|
|
ixmlDocument_getElementsByTagName(HInfo->DescDocument, "device");
|
|
if (!HInfo->DeviceList) {
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListDestroy(&HInfo->SsdpSearchList, 0);
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
ixmlDocument_free(HInfo->DescDocument);
|
|
FreeHandle(*Hnd);
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice: No devices found for RootDevice\n");
|
|
retVal = UPNP_E_INVALID_DESC;
|
|
goto exit_function;
|
|
}
|
|
|
|
HInfo->ServiceList = ixmlDocument_getElementsByTagName(
|
|
HInfo->DescDocument, "serviceList");
|
|
if (!HInfo->ServiceList) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice: No services found for RootDevice\n");
|
|
}
|
|
|
|
/*
|
|
* GENA SET UP
|
|
*/
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice: Gena Check\n");
|
|
hasServiceTable = getServiceTable(
|
|
(IXML_Node *)HInfo->DescDocument,
|
|
&HInfo->ServiceTable,
|
|
HInfo->DescURL);
|
|
if (hasServiceTable) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice: GENA Service Table\n"
|
|
"Here are the known services:\n");
|
|
printServiceTable( &HInfo->ServiceTable, UPNP_ALL, API );
|
|
} else {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"\nUpnpRegisterRootDevice: Empty service table\n");
|
|
}
|
|
|
|
UpnpSdkDeviceRegisteredV4 = 1;
|
|
|
|
retVal = UPNP_E_SUCCESS;
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting RegisterRootDevice, return value == %d\n", retVal);
|
|
HandleUnlock();
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
/*!
|
|
* \brief Fills the sockadr_in with miniserver information.
|
|
*/
|
|
static int GetDescDocumentAndURL(
|
|
/* [in] pointer to server address structure. */
|
|
Upnp_DescType descriptionType,
|
|
/* [in] . */
|
|
char *description,
|
|
/* [in] . */
|
|
int config_baseURL,
|
|
/* [in] . */
|
|
int AddressFamily,
|
|
/* [out] . */
|
|
IXML_Document **xmlDoc,
|
|
/* [out] . */
|
|
char *descURL);
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpRegisterRootDevice2(
|
|
Upnp_DescType descriptionType,
|
|
const char *description_const,
|
|
size_t bufferLen, /* ignored */
|
|
int config_baseURL,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie,
|
|
UpnpDevice_Handle *Hnd)
|
|
{
|
|
struct Handle_Info *HInfo = NULL;
|
|
int retVal = 0;
|
|
int hasServiceTable = 0;
|
|
char *description = (char *)description_const;
|
|
|
|
HandleLock();
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpRegisterRootDevice2\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
retVal = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (Hnd == NULL || Fun == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (UpnpSdkDeviceRegisteredV4 == 1) {
|
|
retVal = UPNP_E_ALREADY_REGISTERED;
|
|
goto exit_function;
|
|
}
|
|
|
|
*Hnd = GetFreeHandle();
|
|
if (*Hnd == UPNP_E_OUTOF_HANDLE) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
|
|
HInfo = (struct Handle_Info *)malloc(sizeof (struct Handle_Info));
|
|
if (HInfo == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
HandleTable[*Hnd] = HInfo;
|
|
|
|
/* prevent accidental removal of a non-existent alias */
|
|
HInfo->aliasInstalled = 0;
|
|
|
|
retVal = GetDescDocumentAndURL(
|
|
descriptionType, description,
|
|
config_baseURL, AF_INET,
|
|
&HInfo->DescDocument, HInfo->DescURL);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
FreeHandle(*Hnd);
|
|
goto exit_function;
|
|
}
|
|
|
|
HInfo->aliasInstalled = config_baseURL != 0;
|
|
HInfo->HType = HND_DEVICE;
|
|
HInfo->Callback = Fun;
|
|
HInfo->Cookie = (void *)Cookie;
|
|
HInfo->MaxAge = DEFAULT_MAXAGE;
|
|
HInfo->DeviceList = NULL;
|
|
HInfo->ServiceList = NULL;
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListInit(&HInfo->SsdpSearchList, NULL, NULL);
|
|
HInfo->ClientSubList = NULL;
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
HInfo->MaxSubscriptions = UPNP_INFINITE;
|
|
HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE;
|
|
HInfo->DeviceAf = AF_INET;
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice2: Valid Description\n"
|
|
"UpnpRegisterRootDevice2: DescURL : %s\n",
|
|
HInfo->DescURL);
|
|
|
|
HInfo->DeviceList =
|
|
ixmlDocument_getElementsByTagName( HInfo->DescDocument, "device" );
|
|
if (!HInfo->DeviceList) {
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListDestroy(&HInfo->SsdpSearchList, 0);
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
ixmlDocument_free(HInfo->DescDocument);
|
|
FreeHandle(*Hnd);
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice2: No devices found for RootDevice\n" );
|
|
retVal = UPNP_E_INVALID_DESC;
|
|
goto exit_function;
|
|
}
|
|
|
|
HInfo->ServiceList = ixmlDocument_getElementsByTagName(
|
|
HInfo->DescDocument, "serviceList" );
|
|
if (!HInfo->ServiceList) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice2: No services found for RootDevice\n");
|
|
}
|
|
|
|
/*
|
|
* GENA SET UP
|
|
*/
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice2: Gena Check\n" );
|
|
hasServiceTable = getServiceTable(
|
|
(IXML_Node *)HInfo->DescDocument,
|
|
&HInfo->ServiceTable,
|
|
HInfo->DescURL);
|
|
if (hasServiceTable) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice2: GENA Service Table\n"
|
|
"Here are the known services: \n");
|
|
printServiceTable(&HInfo->ServiceTable, UPNP_ALL, API);
|
|
} else {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"\nUpnpRegisterRootDevice2: Empty service table\n");
|
|
}
|
|
|
|
UpnpSdkDeviceRegisteredV4 = 1;
|
|
|
|
retVal = UPNP_E_SUCCESS;
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting RegisterRootDevice2, return value == %d\n", retVal);
|
|
HandleUnlock();
|
|
|
|
return retVal;
|
|
bufferLen = bufferLen;
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpRegisterRootDevice3(
|
|
const char *DescUrl,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie,
|
|
UpnpDevice_Handle *Hnd,
|
|
const int AddressFamily)
|
|
{
|
|
struct Handle_Info *HInfo;
|
|
int retVal = 0;
|
|
int hasServiceTable = 0;
|
|
int handler_index = 0;
|
|
|
|
HandleLock();
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpRegisterRootDevice3\n");
|
|
if (UpnpSdkInit != 1) {
|
|
retVal = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
if (Hnd == NULL ||
|
|
Fun == NULL ||
|
|
DescUrl == NULL ||
|
|
strlen(DescUrl) == 0 ||
|
|
(AddressFamily != AF_INET && AddressFamily != AF_INET6)) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
/* Test for already regsitered IPV4. */
|
|
if (AddressFamily == AF_INET && UpnpSdkDeviceRegisteredV4 == 1) {
|
|
retVal = UPNP_E_ALREADY_REGISTERED;
|
|
goto exit_function;
|
|
}
|
|
/* Test for already registered IPV6. IPV6 devices might register on multiple
|
|
* IPv6 addresses (link local and GUA or ULA), so we must to check the
|
|
* description URL in the HandleTable. */
|
|
while (handler_index < NUM_HANDLE && HandleTable[handler_index] != NULL) {
|
|
if (strcmp(((struct Handle_Info *)HandleTable[handler_index])->DescURL, DescUrl)) {
|
|
retVal = UPNP_E_ALREADY_REGISTERED;
|
|
goto exit_function;
|
|
}
|
|
handler_index++;
|
|
}
|
|
*Hnd = GetFreeHandle();
|
|
if (*Hnd == UPNP_E_OUTOF_HANDLE) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
HInfo = (struct Handle_Info *)malloc(sizeof (struct Handle_Info));
|
|
if (HInfo == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
HandleTable[*Hnd] = HInfo;
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Root device URL is %s\n", DescUrl);
|
|
HInfo->aliasInstalled = 0;
|
|
HInfo->HType = HND_DEVICE;
|
|
strcpy(HInfo->DescURL, DescUrl);
|
|
HInfo->Callback = Fun;
|
|
HInfo->Cookie = (void *)Cookie;
|
|
HInfo->MaxAge = DEFAULT_MAXAGE;
|
|
HInfo->DeviceList = NULL;
|
|
HInfo->ServiceList = NULL;
|
|
HInfo->DescDocument = NULL;
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListInit(&HInfo->SsdpSearchList, NULL, NULL);
|
|
HInfo->ClientSubList = NULL;
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
HInfo->MaxSubscriptions = UPNP_INFINITE;
|
|
HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE;
|
|
HInfo->DeviceAf = AddressFamily;
|
|
retVal = UpnpDownloadXmlDoc(HInfo->DescURL, &(HInfo->DescDocument));
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListDestroy(&HInfo->SsdpSearchList, 0);
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
FreeHandle(*Hnd);
|
|
goto exit_function;
|
|
}
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice3: Valid Description\n"
|
|
"UpnpRegisterRootDevice3: DescURL : %s\n",
|
|
HInfo->DescURL);
|
|
|
|
HInfo->DeviceList = ixmlDocument_getElementsByTagName(
|
|
HInfo->DescDocument, "device");
|
|
if (!HInfo->DeviceList) {
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListDestroy(&HInfo->SsdpSearchList, 0);
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
ixmlDocument_free(HInfo->DescDocument);
|
|
FreeHandle(*Hnd);
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice3: No devices found for RootDevice\n");
|
|
retVal = UPNP_E_INVALID_DESC;
|
|
goto exit_function;
|
|
}
|
|
|
|
HInfo->ServiceList = ixmlDocument_getElementsByTagName(
|
|
HInfo->DescDocument, "serviceList" );
|
|
if (!HInfo->ServiceList) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice3: No services found for RootDevice\n");
|
|
}
|
|
|
|
/*
|
|
* GENA SET UP
|
|
*/
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice3: Gena Check\n" );
|
|
hasServiceTable = getServiceTable(
|
|
(IXML_Node *)HInfo->DescDocument,
|
|
&HInfo->ServiceTable,
|
|
HInfo->DescURL);
|
|
if (hasServiceTable) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"UpnpRegisterRootDevice3: GENA Service Table \n"
|
|
"Here are the known services: \n" );
|
|
printServiceTable(&HInfo->ServiceTable, UPNP_ALL, API);
|
|
} else {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"\nUpnpRegisterRootDevice3: Empty service table\n");
|
|
}
|
|
|
|
if (AddressFamily == AF_INET) {
|
|
UpnpSdkDeviceRegisteredV4 = 1;
|
|
} else {
|
|
UpnpSdkDeviceregisteredV6 = 1;
|
|
}
|
|
|
|
retVal = UPNP_E_SUCCESS;
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting RegisterRootDevice3, return value == %d\n", retVal);
|
|
HandleUnlock();
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpUnRegisterRootDevice(UpnpDevice_Handle Hnd)
|
|
{
|
|
int retVal = 0;
|
|
struct Handle_Info *HInfo = NULL;
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Inside UpnpUnRegisterRootDevice\n");
|
|
#if EXCLUDE_GENA == 0
|
|
if( genaUnregisterDevice( Hnd ) != UPNP_E_SUCCESS )
|
|
return UPNP_E_INVALID_HANDLE;
|
|
#endif
|
|
|
|
HandleLock();
|
|
if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
#if EXCLUDE_SSDP == 0
|
|
retVal = AdvertiseAndReply(-1, Hnd, 0, (struct sockaddr *)NULL,
|
|
(char *)NULL, (char *)NULL, (char *)NULL, HInfo->MaxAge);
|
|
#endif
|
|
|
|
HandleLock();
|
|
if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
/*info = (struct Handle_Info *) HandleTable[Hnd]; */
|
|
ixmlNodeList_free( HInfo->DeviceList );
|
|
ixmlNodeList_free( HInfo->ServiceList );
|
|
ixmlDocument_free( HInfo->DescDocument );
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
ListDestroy( &HInfo->SsdpSearchList, 0 );
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
#ifdef INTERNAL_WEB_SERVER
|
|
if( HInfo->aliasInstalled ) {
|
|
web_server_set_alias( NULL, NULL, 0, 0 );
|
|
}
|
|
#endif /* INTERNAL_WEB_SERVER */
|
|
|
|
if( HInfo->DeviceAf == AF_INET ) {
|
|
UpnpSdkDeviceRegisteredV4 = 0;
|
|
} else if( HInfo->DeviceAf == AF_INET6 ) {
|
|
UpnpSdkDeviceregisteredV6 = 0;
|
|
}
|
|
|
|
FreeHandle(Hnd);
|
|
HandleUnlock();
|
|
|
|
UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Exiting UpnpUnRegisterRootDevice\n" );
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpRegisterClient(
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie,
|
|
UpnpClient_Handle *Hnd)
|
|
{
|
|
struct Handle_Info *HInfo;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpRegisterClient \n" );
|
|
if( Fun == NULL || Hnd == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
HandleLock();
|
|
|
|
if( UpnpSdkClientRegistered ) {
|
|
HandleUnlock();
|
|
return UPNP_E_ALREADY_REGISTERED;
|
|
}
|
|
if( ( *Hnd = GetFreeHandle() ) == UPNP_E_OUTOF_HANDLE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
HInfo = ( struct Handle_Info * )malloc( sizeof( struct Handle_Info ) );
|
|
if( HInfo == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
|
|
HInfo->HType = HND_CLIENT;
|
|
HInfo->Callback = Fun;
|
|
HInfo->Cookie = ( void * )Cookie;
|
|
HInfo->ClientSubList = NULL;
|
|
ListInit( &HInfo->SsdpSearchList, NULL, NULL );
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
HInfo->MaxAge = 0;
|
|
HInfo->MaxSubscriptions = UPNP_INFINITE;
|
|
HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE;
|
|
#endif
|
|
|
|
HandleTable[*Hnd] = HInfo;
|
|
UpnpSdkClientRegistered = 1;
|
|
|
|
HandleUnlock();
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpRegisterClient \n" );
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpUnRegisterClient(UpnpClient_Handle Hnd)
|
|
{
|
|
struct Handle_Info *HInfo;
|
|
ListNode *node = NULL;
|
|
SsdpSearchArg *searchArg = NULL;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpUnRegisterClient \n" );
|
|
HandleLock();
|
|
if( !UpnpSdkClientRegistered ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
#if EXCLUDE_GENA == 0
|
|
if( genaUnregisterClient( Hnd ) != UPNP_E_SUCCESS )
|
|
return UPNP_E_INVALID_HANDLE;
|
|
#endif
|
|
HandleLock();
|
|
if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
/* clean up search list */
|
|
node = ListHead( &HInfo->SsdpSearchList );
|
|
while( node != NULL ) {
|
|
searchArg = ( SsdpSearchArg * ) node->item;
|
|
if( searchArg ) {
|
|
free( searchArg->searchTarget );
|
|
free( searchArg );
|
|
}
|
|
ListDelNode( &HInfo->SsdpSearchList, node, 0 );
|
|
node = ListHead( &HInfo->SsdpSearchList );
|
|
}
|
|
|
|
ListDestroy( &HInfo->SsdpSearchList, 0 );
|
|
FreeHandle(Hnd);
|
|
UpnpSdkClientRegistered = 0;
|
|
HandleUnlock();
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpUnRegisterClient \n" );
|
|
return UPNP_E_SUCCESS;
|
|
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
#ifdef INTERNAL_WEB_SERVER
|
|
/*!
|
|
* \brief Determines alias for given name which is a file name or URL.
|
|
*
|
|
* \return UPNP_E_SUCCESS on success, nonzero on failure.
|
|
*/
|
|
static int GetNameForAlias(
|
|
/*! [in] Name of the file. */
|
|
char *name,
|
|
/*! [out] Pointer to alias string. */
|
|
char **alias)
|
|
{
|
|
char *ext;
|
|
char *al;
|
|
|
|
ext = strrchr( name, '.' );
|
|
if( ext == NULL || strcasecmp( ext, ".xml" ) != 0 ) {
|
|
return UPNP_E_EXT_NOT_XML;
|
|
}
|
|
|
|
al = strrchr( name, '/' );
|
|
if( al == NULL ) {
|
|
*alias = name;
|
|
} else {
|
|
*alias = al;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
/*!
|
|
* \brief Fill the sockadr with IPv4 miniserver information.
|
|
*/
|
|
static void get_server_addr(
|
|
/*! [out] pointer to server address structure. */
|
|
struct sockaddr *serverAddr)
|
|
{
|
|
struct sockaddr_in* sa4 = (struct sockaddr_in*)serverAddr;
|
|
|
|
memset( serverAddr, 0, sizeof(struct sockaddr_storage) );
|
|
|
|
sa4->sin_family = AF_INET;
|
|
inet_pton( AF_INET, gIF_IPV4, &sa4->sin_addr );
|
|
sa4->sin_port = htons( LOCAL_PORT_V4 );
|
|
}
|
|
|
|
/*!
|
|
* \brief Fill the sockadr with IPv6 miniserver information.
|
|
*/
|
|
static void get_server_addr6(
|
|
/*! [out] pointer to server address structure. */
|
|
struct sockaddr *serverAddr)
|
|
{
|
|
struct sockaddr_in6* sa6 = (struct sockaddr_in6*)serverAddr;
|
|
|
|
memset( serverAddr, 0, sizeof(struct sockaddr_storage) );
|
|
|
|
sa6->sin6_family = AF_INET6;
|
|
inet_pton(AF_INET6, gIF_IPV6, &sa6->sin6_addr );
|
|
sa6->sin6_port = htons( LOCAL_PORT_V6 );
|
|
}
|
|
|
|
|
|
static int GetDescDocumentAndURL(
|
|
Upnp_DescType descriptionType,
|
|
char *description,
|
|
int config_baseURL,
|
|
int AddressFamily,
|
|
IXML_Document **xmlDoc,
|
|
char *descURL)
|
|
{
|
|
int retVal = 0;
|
|
char *membuf = NULL;
|
|
char aliasStr[LINE_SIZE];
|
|
char *temp_str = NULL;
|
|
FILE *fp = NULL;
|
|
size_t fileLen;
|
|
size_t num_read;
|
|
time_t last_modified;
|
|
struct stat file_info;
|
|
struct sockaddr_storage serverAddr;
|
|
int rc = UPNP_E_SUCCESS;
|
|
|
|
if (description == NULL)
|
|
return UPNP_E_INVALID_PARAM;
|
|
/* non-URL description must have configuration specified */
|
|
if (descriptionType != UPNPREG_URL_DESC && !config_baseURL)
|
|
return UPNP_E_INVALID_PARAM;
|
|
/* Get XML doc and last modified time */
|
|
if (descriptionType == UPNPREG_URL_DESC) {
|
|
retVal = UpnpDownloadXmlDoc(description, xmlDoc);
|
|
if (retVal != UPNP_E_SUCCESS)
|
|
return retVal;
|
|
last_modified = time(NULL);
|
|
} else if (descriptionType == UPNPREG_FILENAME_DESC) {
|
|
retVal = stat(description, &file_info);
|
|
if (retVal == -1)
|
|
return UPNP_E_FILE_NOT_FOUND;
|
|
fileLen = (size_t)file_info.st_size;
|
|
last_modified = file_info.st_mtime;
|
|
fp = fopen(description, "rb");
|
|
if (fp == NULL)
|
|
return UPNP_E_FILE_NOT_FOUND;
|
|
membuf = (char *)malloc(fileLen + 1);
|
|
if (membuf == NULL) {
|
|
fclose(fp);
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
num_read = fread(membuf, 1, fileLen, fp);
|
|
if (num_read != fileLen) {
|
|
fclose(fp);
|
|
free(membuf);
|
|
return UPNP_E_FILE_READ_ERROR;
|
|
}
|
|
membuf[fileLen] = 0;
|
|
fclose(fp);
|
|
rc = ixmlParseBufferEx(membuf, xmlDoc);
|
|
free(membuf);
|
|
} else if (descriptionType == UPNPREG_BUF_DESC) {
|
|
last_modified = time(NULL);
|
|
rc = ixmlParseBufferEx(description, xmlDoc);
|
|
} else {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
if (rc != IXML_SUCCESS && descriptionType != UPNPREG_URL_DESC) {
|
|
if (rc == IXML_INSUFFICIENT_MEMORY)
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
else
|
|
return UPNP_E_INVALID_DESC;
|
|
}
|
|
/* Determine alias */
|
|
if (config_baseURL) {
|
|
if (descriptionType == UPNPREG_BUF_DESC) {
|
|
strcpy(aliasStr, "description.xml");
|
|
} else {
|
|
/* URL or filename */
|
|
retVal = GetNameForAlias(description, &temp_str);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
ixmlDocument_free(*xmlDoc);
|
|
return retVal;
|
|
}
|
|
if (strlen(temp_str) > (LINE_SIZE - 1)) {
|
|
ixmlDocument_free(*xmlDoc);
|
|
free(temp_str);
|
|
return UPNP_E_URL_TOO_BIG;
|
|
}
|
|
strcpy(aliasStr, temp_str);
|
|
}
|
|
if (AddressFamily == AF_INET) {
|
|
get_server_addr((struct sockaddr *)&serverAddr);
|
|
} else {
|
|
get_server_addr6((struct sockaddr *)&serverAddr);
|
|
}
|
|
|
|
/* config */
|
|
retVal =
|
|
configure_urlbase(*xmlDoc, (struct sockaddr *)&serverAddr,
|
|
aliasStr, last_modified, descURL);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
ixmlDocument_free(*xmlDoc);
|
|
return retVal;
|
|
}
|
|
} else {
|
|
/* Manual */
|
|
if (strlen(description) > (LINE_SIZE - 1)) {
|
|
ixmlDocument_free(*xmlDoc);
|
|
return UPNP_E_URL_TOO_BIG;
|
|
}
|
|
strcpy(descURL, description);
|
|
}
|
|
|
|
assert(*xmlDoc != NULL);
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
#else /* INTERNAL_WEB_SERVER */ /* no web server */
|
|
static int GetDescDocumentAndURL(
|
|
Upnp_DescType descriptionType,
|
|
char *description,
|
|
int config_baseURL,
|
|
int AddressFamily,
|
|
IXML_Document **xmlDoc,
|
|
char *descURL)
|
|
{
|
|
int retVal = 0;
|
|
|
|
if (descriptionType != UPNPREG_URL_DESC || config_baseURL) {
|
|
return UPNP_E_NO_WEB_SERVER;
|
|
}
|
|
|
|
if (description == NULL) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
if (strlen(description) > (LINE_SIZE - 1)) {
|
|
return UPNP_E_URL_TOO_BIG;
|
|
}
|
|
strcpy(descURL, description);
|
|
|
|
retVal = UpnpDownloadXmlDoc(description, xmlDoc);
|
|
if (retVal != UPNP_E_SUCCESS) {
|
|
return retVal;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
#endif /* INTERNAL_WEB_SERVER */
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SSDP interface
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
#if EXCLUDE_SSDP == 0
|
|
int UpnpSendAdvertisement(UpnpDevice_Handle Hnd, int Exp)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal = 0,
|
|
*ptrMx;
|
|
upnp_timeout *adEvent;
|
|
ThreadPoolJob job;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSendAdvertisement \n" );
|
|
|
|
HandleLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
if( Exp < 1 )
|
|
Exp = DEFAULT_MAXAGE;
|
|
SInfo->MaxAge = Exp;
|
|
HandleUnlock();
|
|
retVal = AdvertiseAndReply( 1, Hnd, 0, ( struct sockaddr * )NULL,
|
|
( char * )NULL, ( char * )NULL,
|
|
( char * )NULL, Exp );
|
|
|
|
if( retVal != UPNP_E_SUCCESS )
|
|
return retVal;
|
|
ptrMx = ( int * )malloc( sizeof( int ) );
|
|
if( ptrMx == NULL )
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
adEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) );
|
|
|
|
if( adEvent == NULL ) {
|
|
free( ptrMx );
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
*ptrMx = Exp;
|
|
adEvent->handle = Hnd;
|
|
adEvent->Event = ptrMx;
|
|
|
|
HandleLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) {
|
|
HandleUnlock();
|
|
free( adEvent );
|
|
free( ptrMx );
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
#ifdef SSDP_PACKET_DISTRIBUTE
|
|
TPJobInit( &job, ( start_routine ) AutoAdvertise, adEvent );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout );
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
if( ( retVal = TimerThreadSchedule( &gTimerThread,
|
|
( ( Exp / 2 ) -
|
|
( AUTO_ADVERTISEMENT_TIME ) ),
|
|
REL_SEC, &job, SHORT_TERM,
|
|
&( adEvent->eventId ) ) )
|
|
!= UPNP_E_SUCCESS ) {
|
|
HandleUnlock();
|
|
free( adEvent );
|
|
free( ptrMx );
|
|
return retVal;
|
|
}
|
|
#else
|
|
TPJobInit( &job, ( start_routine ) AutoAdvertise, adEvent );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout );
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
if( ( retVal = TimerThreadSchedule( &gTimerThread,
|
|
Exp - AUTO_ADVERTISEMENT_TIME,
|
|
REL_SEC, &job, SHORT_TERM,
|
|
&( adEvent->eventId ) ) )
|
|
!= UPNP_E_SUCCESS ) {
|
|
HandleUnlock();
|
|
free( adEvent );
|
|
free( ptrMx );
|
|
return retVal;
|
|
}
|
|
#endif
|
|
|
|
HandleUnlock();
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSendAdvertisement \n" );
|
|
|
|
return retVal;
|
|
|
|
}
|
|
#endif /* EXCLUDE_SSDP == 0 */
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#if EXCLUDE_SSDP == 0
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
|
|
|
|
int UpnpSearchAsync(
|
|
UpnpClient_Handle Hnd,
|
|
int Mx,
|
|
const char *Target_const,
|
|
const void *Cookie_const )
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
char *Target = ( char * )Target_const;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSearchAsync\n" );
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
if( Mx < 1 )
|
|
Mx = DEFAULT_MX;
|
|
|
|
if( Target == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
HandleUnlock();
|
|
SearchByTarget( Mx, Target, ( void * )Cookie_const );
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSearchAsync \n" );
|
|
|
|
return UPNP_E_SUCCESS;
|
|
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
#endif
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* GENA interface
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
#if EXCLUDE_GENA == 0
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpSetMaxSubscriptions(UpnpDevice_Handle Hnd, int MaxSubscriptions)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSetMaxSubscriptions \n" );
|
|
|
|
HandleLock();
|
|
if( ( ( MaxSubscriptions != UPNP_INFINITE )
|
|
&& ( MaxSubscriptions < 0 ) )
|
|
|| ( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
SInfo->MaxSubscriptions = MaxSubscriptions;
|
|
HandleUnlock();
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSetMaxSubscriptions \n" );
|
|
|
|
return UPNP_E_SUCCESS;
|
|
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpSetMaxSubscriptionTimeOut(UpnpDevice_Handle Hnd, int MaxSubscriptionTimeOut)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSetMaxSubscriptionTimeOut\n" );
|
|
|
|
HandleLock();
|
|
|
|
if( ( ( MaxSubscriptionTimeOut != UPNP_INFINITE )
|
|
&& ( MaxSubscriptionTimeOut < 0 ) )
|
|
|| ( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
|
|
SInfo->MaxSubscriptionTimeOut = MaxSubscriptionTimeOut;
|
|
HandleUnlock();
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSetMaxSubscriptionTimeOut\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpSubscribeAsync(
|
|
UpnpClient_Handle Hnd,
|
|
const char *EvtUrl_const,
|
|
int TimeOut,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie_const)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
struct UpnpNonblockParam *Param;
|
|
char *EvtUrl = ( char * )EvtUrl_const;
|
|
ThreadPoolJob job;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSubscribeAsync\n");
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
if( EvtUrl == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( TimeOut != UPNP_INFINITE && TimeOut < 1 ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( Fun == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
HandleUnlock();
|
|
|
|
Param = (struct UpnpNonblockParam *)
|
|
malloc(sizeof (struct UpnpNonblockParam));
|
|
if( Param == NULL ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
|
|
Param->FunName = SUBSCRIBE;
|
|
Param->Handle = Hnd;
|
|
strcpy( Param->Url, EvtUrl );
|
|
Param->TimeOut = TimeOut;
|
|
Param->Fun = Fun;
|
|
Param->Cookie = (void *)Cookie_const;
|
|
|
|
TPJobInit(&job, (start_routine)UpnpThreadDistribution, Param);
|
|
TPJobSetFreeFunction(&job, (free_routine)free);
|
|
TPJobSetPriority(&job, MED_PRIORITY);
|
|
ThreadPoolAdd(&gSendThreadPool, &job, NULL);
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSubscribeAsync\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpSubscribe(
|
|
UpnpClient_Handle Hnd,
|
|
const char *EvtUrl_const,
|
|
int *TimeOut,
|
|
Upnp_SID SubsId)
|
|
{
|
|
int retVal;
|
|
struct Handle_Info *SInfo = NULL;
|
|
UpnpString *EvtUrl = UpnpString_new();
|
|
UpnpString *SubsIdTmp = UpnpString_new();
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, "Inside UpnpSubscribe\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
retVal = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (EvtUrl == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
if (EvtUrl_const == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
UpnpString_set_String(EvtUrl, EvtUrl_const);
|
|
|
|
if (SubsIdTmp == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
if (SubsId == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
UpnpString_set_String(SubsIdTmp, SubsId);
|
|
|
|
if (TimeOut == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
|
|
HandleReadLock();
|
|
if (GetHandleInfo(Hnd, &SInfo) != HND_CLIENT) {
|
|
HandleUnlock();
|
|
retVal = UPNP_E_INVALID_HANDLE;
|
|
goto exit_function;
|
|
}
|
|
HandleUnlock();
|
|
|
|
retVal = genaSubscribe(Hnd, EvtUrl, TimeOut, SubsIdTmp);
|
|
strcpy(SubsId, UpnpString_get_String(SubsIdTmp));
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSubscribe, retVal=%d\n", retVal);
|
|
|
|
UpnpString_delete(SubsIdTmp);
|
|
UpnpString_delete(EvtUrl);
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpUnSubscribe(UpnpClient_Handle Hnd, const Upnp_SID SubsId)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal;
|
|
UpnpString *SubsIdTmp = UpnpString_new();
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, "Inside UpnpUnSubscribe\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
retVal = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (SubsIdTmp == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
if (SubsId == NULL) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
UpnpString_set_String(SubsIdTmp, SubsId);
|
|
|
|
HandleReadLock();
|
|
if (GetHandleInfo(Hnd, &SInfo) != HND_CLIENT) {
|
|
HandleUnlock();
|
|
retVal = UPNP_E_INVALID_HANDLE;
|
|
goto exit_function;
|
|
}
|
|
HandleUnlock();
|
|
|
|
retVal = genaUnSubscribe(Hnd, SubsIdTmp);
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpUnSubscribe, retVal=%d\n", retVal);
|
|
|
|
UpnpString_delete(SubsIdTmp);
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpUnSubscribeAsync(
|
|
UpnpClient_Handle Hnd,
|
|
Upnp_SID SubsId,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie_const)
|
|
{
|
|
int retVal;
|
|
ThreadPoolJob job;
|
|
struct Handle_Info *SInfo = NULL;
|
|
struct UpnpNonblockParam *Param;
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, "Inside UpnpUnSubscribeAsync\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
retVal = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
if (SubsId == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
if (Fun == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
|
|
HandleReadLock();
|
|
if (GetHandleInfo(Hnd, &SInfo) != HND_CLIENT) {
|
|
HandleUnlock();
|
|
retVal = UPNP_E_INVALID_HANDLE;
|
|
goto exit_function;
|
|
}
|
|
HandleUnlock();
|
|
|
|
Param = (struct UpnpNonblockParam *)malloc(sizeof(struct UpnpNonblockParam));
|
|
if (Param == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
|
|
Param->FunName = UNSUBSCRIBE;
|
|
Param->Handle = Hnd;
|
|
strcpy( Param->SubsId, SubsId );
|
|
Param->Fun = Fun;
|
|
Param->Cookie = (void *)Cookie_const;
|
|
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free );
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, "Exiting UpnpUnSubscribeAsync\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpRenewSubscription(
|
|
UpnpClient_Handle Hnd,
|
|
int *TimeOut,
|
|
const Upnp_SID SubsId)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal;
|
|
UpnpString *SubsIdTmp = UpnpString_new();
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, "Inside UpnpRenewSubscription\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
if (SubsIdTmp == NULL) {
|
|
retVal = UPNP_E_OUTOF_MEMORY;
|
|
goto exit_function;
|
|
}
|
|
if (SubsId == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
UpnpString_set_String(SubsIdTmp, SubsId);
|
|
|
|
if (TimeOut == NULL) {
|
|
retVal = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
|
|
HandleReadLock();
|
|
if (GetHandleInfo(Hnd, &SInfo) != HND_CLIENT) {
|
|
HandleUnlock();
|
|
retVal = UPNP_E_INVALID_HANDLE;
|
|
goto exit_function;
|
|
}
|
|
HandleUnlock();
|
|
|
|
retVal = genaRenewSubscription(Hnd, SubsIdTmp, TimeOut);
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpRenewSubscription, retVal=%d\n", retVal);
|
|
|
|
UpnpString_delete(SubsIdTmp);
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpRenewSubscriptionAsync(
|
|
UpnpClient_Handle Hnd,
|
|
int TimeOut,
|
|
Upnp_SID SubsId,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie_const)
|
|
{
|
|
ThreadPoolJob job;
|
|
struct Handle_Info *SInfo = NULL;
|
|
struct UpnpNonblockParam *Param;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpRenewSubscriptionAsync\n");
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
if( TimeOut != UPNP_INFINITE && TimeOut < 1 ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( SubsId == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( Fun == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
HandleUnlock();
|
|
|
|
Param =
|
|
( struct UpnpNonblockParam * )
|
|
malloc( sizeof( struct UpnpNonblockParam ) );
|
|
if( Param == NULL ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
|
|
Param->FunName = RENEW;
|
|
Param->Handle = Hnd;
|
|
strcpy( Param->SubsId, SubsId );
|
|
Param->Fun = Fun;
|
|
Param->Cookie = ( void * )Cookie_const;
|
|
Param->TimeOut = TimeOut;
|
|
|
|
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free );
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpRenewSubscriptionAsync\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpNotify(
|
|
UpnpDevice_Handle Hnd,
|
|
const char *DevID_const,
|
|
const char *ServName_const,
|
|
const char **VarName_const,
|
|
const char **NewVal_const,
|
|
int cVariables)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal;
|
|
char *DevID = (char *)DevID_const;
|
|
char *ServName = (char *)ServName_const;
|
|
char **VarName = (char **)VarName_const;
|
|
char **NewVal = (char **)NewVal_const;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpNotify\n");
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
if( DevID == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( ServName == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( VarName == NULL || NewVal == NULL || cVariables < 0 ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
HandleUnlock();
|
|
retVal =
|
|
genaNotifyAll( Hnd, DevID, ServName, VarName, NewVal, cVariables );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpNotify\n");
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
int UpnpNotifyExt(
|
|
UpnpDevice_Handle Hnd,
|
|
const char *DevID_const,
|
|
const char *ServName_const,
|
|
IXML_Document *PropSet)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal;
|
|
char *DevID = (char *)DevID_const;
|
|
char *ServName = (char *)ServName_const;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpNotify \n" );
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
if( DevID == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( ServName == NULL ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
HandleUnlock();
|
|
retVal = genaNotifyAllExt( Hnd, DevID, ServName, PropSet );
|
|
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpNotify \n" );
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
int UpnpAcceptSubscription(
|
|
UpnpDevice_Handle Hnd,
|
|
const char *DevID_const,
|
|
const char *ServName_const,
|
|
const char **VarName_const,
|
|
const char **NewVal_const,
|
|
int cVariables,
|
|
const Upnp_SID SubsId)
|
|
{
|
|
int ret = 0;
|
|
int line = 0;
|
|
struct Handle_Info *SInfo = NULL;
|
|
char *DevID = (char *)DevID_const;
|
|
char *ServName = (char *)ServName_const;
|
|
char **VarName = (char **)VarName_const;
|
|
char **NewVal = (char **)NewVal_const;
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpAcceptSubscription\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
line = __LINE__;
|
|
ret = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
HandleReadLock();
|
|
|
|
if (GetHandleInfo(Hnd, &SInfo) != HND_DEVICE) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_HANDLE;
|
|
goto exit_function;
|
|
}
|
|
if (DevID == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
if (ServName == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
if (SubsId == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
/* Now accepts an empty state list, so the code below is commented out */
|
|
#if 0
|
|
if (VarName == NULL || NewVal == NULL || cVariables < 0) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
#endif
|
|
|
|
HandleUnlock();
|
|
|
|
line = __LINE__;
|
|
ret = genaInitNotify(
|
|
Hnd, DevID, ServName, VarName, NewVal, cVariables, SubsId);
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, line,
|
|
"Exiting UpnpAcceptSubscription, ret = %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int UpnpAcceptSubscriptionExt(
|
|
UpnpDevice_Handle Hnd,
|
|
const char *DevID_const,
|
|
const char *ServName_const,
|
|
IXML_Document *PropSet,
|
|
Upnp_SID SubsId)
|
|
{
|
|
int ret = 0;
|
|
int line = 0;
|
|
struct Handle_Info *SInfo = NULL;
|
|
char *DevID = (char *)DevID_const;
|
|
char *ServName = (char *)ServName_const;
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpAcceptSubscription\n");
|
|
|
|
if (UpnpSdkInit != 1) {
|
|
line = __LINE__;
|
|
ret = UPNP_E_FINISH;
|
|
goto exit_function;
|
|
}
|
|
|
|
HandleReadLock();
|
|
|
|
if (GetHandleInfo(Hnd, &SInfo) != HND_DEVICE) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_HANDLE;
|
|
goto exit_function;
|
|
}
|
|
if (DevID == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
if (ServName == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
if (SubsId == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
/* Now accepts an empty state list, so the code below is commented out */
|
|
#if 0
|
|
if (PropSet == NULL) {
|
|
HandleUnlock();
|
|
line = __LINE__;
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
goto exit_function;
|
|
}
|
|
#endif
|
|
|
|
HandleUnlock();
|
|
|
|
line = __LINE__;
|
|
ret = genaInitNotifyExt(Hnd, DevID, ServName, PropSet, SubsId);
|
|
|
|
exit_function:
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, line,
|
|
"Exiting UpnpAcceptSubscription, ret = %d.\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
#endif /* EXCLUDE_GENA == 0 */
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SOAP interface
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
#if EXCLUDE_SOAP == 0
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
int UpnpSendAction(
|
|
UpnpClient_Handle Hnd,
|
|
const char *ActionURL_const,
|
|
const char *ServiceType_const,
|
|
const char *DevUDN_const,
|
|
IXML_Document *Action,
|
|
IXML_Document **RespNodePtr)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal = 0;
|
|
char *ActionURL = (char *)ActionURL_const;
|
|
char *ServiceType = (char *)ServiceType_const;
|
|
/* udn not used? */
|
|
/*char *DevUDN = (char *)DevUDN_const;*/
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSendAction\n");
|
|
if (DevUDN_const !=NULL) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"non NULL DevUDN is ignored\n");
|
|
}
|
|
DevUDN_const = NULL;
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
if( ActionURL == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
if( ServiceType == NULL || Action == NULL || RespNodePtr == NULL
|
|
|| DevUDN_const != NULL ) {
|
|
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
retVal = SoapSendAction( ActionURL, ServiceType, Action, RespNodePtr );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSendAction\n");
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
int UpnpSendActionEx(
|
|
UpnpClient_Handle Hnd,
|
|
const char *ActionURL_const,
|
|
const char *ServiceType_const,
|
|
const char *DevUDN_const,
|
|
IXML_Document *Header,
|
|
IXML_Document *Action,
|
|
IXML_Document **RespNodePtr)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal = 0;
|
|
char *ActionURL = (char *)ActionURL_const;
|
|
char *ServiceType = (char *)ServiceType_const;
|
|
/* udn not used? */
|
|
/*char *DevUDN = (char *)DevUDN_const;*/
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSendActionEx\n");
|
|
|
|
if( Header == NULL ) {
|
|
retVal = UpnpSendAction( Hnd, ActionURL_const, ServiceType_const,
|
|
DevUDN_const, Action, RespNodePtr );
|
|
return retVal;
|
|
}
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
if( ActionURL == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( ServiceType == NULL || Action == NULL || RespNodePtr == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
retVal = SoapSendActionEx( ActionURL, ServiceType, Header,
|
|
Action, RespNodePtr );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSendAction \n");
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
int UpnpSendActionAsync(
|
|
UpnpClient_Handle Hnd,
|
|
const char *ActionURL_const,
|
|
const char *ServiceType_const,
|
|
const char *DevUDN_const,
|
|
IXML_Document *Act,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie_const)
|
|
{
|
|
int rc;
|
|
ThreadPoolJob job;
|
|
struct Handle_Info *SInfo = NULL;
|
|
struct UpnpNonblockParam *Param;
|
|
DOMString tmpStr;
|
|
char *ActionURL = (char *)ActionURL_const;
|
|
char *ServiceType = (char *)ServiceType_const;
|
|
/* udn not used? */
|
|
/*char *DevUDN = (char *)DevUDN_const;*/
|
|
|
|
if(UpnpSdkInit != 1) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSendActionAsync\n");
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
if( ActionURL == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( ServiceType == NULL ||
|
|
Act == NULL || Fun == NULL || DevUDN_const != NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
tmpStr = ixmlPrintNode( ( IXML_Node * ) Act );
|
|
if( tmpStr == NULL ) {
|
|
return UPNP_E_INVALID_ACTION;
|
|
}
|
|
|
|
Param =
|
|
( struct UpnpNonblockParam * )
|
|
malloc( sizeof( struct UpnpNonblockParam ) );
|
|
|
|
if( Param == NULL ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
|
|
Param->FunName = ACTION;
|
|
Param->Handle = Hnd;
|
|
strcpy( Param->Url, ActionURL );
|
|
strcpy( Param->ServiceType, ServiceType );
|
|
|
|
rc = ixmlParseBufferEx( tmpStr, &( Param->Act ) );
|
|
if( rc != IXML_SUCCESS ) {
|
|
free( Param );
|
|
ixmlFreeDOMString( tmpStr );
|
|
if( rc == IXML_INSUFFICIENT_MEMORY ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
} else {
|
|
return UPNP_E_INVALID_ACTION;
|
|
}
|
|
}
|
|
ixmlFreeDOMString( tmpStr );
|
|
Param->Cookie = ( void * )Cookie_const;
|
|
Param->Fun = Fun;
|
|
|
|
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free );
|
|
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSendActionAsync \n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
int UpnpSendActionExAsync(
|
|
UpnpClient_Handle Hnd,
|
|
const char *ActionURL_const,
|
|
const char *ServiceType_const,
|
|
const char *DevUDN_const,
|
|
IXML_Document *Header,
|
|
IXML_Document *Act,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie_const)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
struct UpnpNonblockParam *Param;
|
|
DOMString tmpStr;
|
|
DOMString headerStr = NULL;
|
|
char *ActionURL = ( char * )ActionURL_const;
|
|
char *ServiceType = ( char * )ServiceType_const;
|
|
ThreadPoolJob job;
|
|
int retVal = 0;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpSendActionExAsync\n");
|
|
|
|
if( Header == NULL ) {
|
|
retVal = UpnpSendActionAsync( Hnd, ActionURL_const,
|
|
ServiceType_const, DevUDN_const, Act,
|
|
Fun, Cookie_const );
|
|
return retVal;
|
|
}
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
if( ActionURL == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( ServiceType == NULL || Act == NULL || Fun == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
headerStr = ixmlPrintNode( ( IXML_Node * ) Header );
|
|
|
|
tmpStr = ixmlPrintNode( ( IXML_Node * ) Act );
|
|
if( tmpStr == NULL ) {
|
|
return UPNP_E_INVALID_ACTION;
|
|
}
|
|
|
|
Param =
|
|
( struct UpnpNonblockParam * )
|
|
malloc( sizeof( struct UpnpNonblockParam ) );
|
|
if( Param == NULL ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
|
|
Param->FunName = ACTION;
|
|
Param->Handle = Hnd;
|
|
strcpy( Param->Url, ActionURL );
|
|
strcpy( Param->ServiceType, ServiceType );
|
|
retVal = ixmlParseBufferEx( headerStr, &( Param->Header ) );
|
|
if( retVal != IXML_SUCCESS ) {
|
|
ixmlFreeDOMString( tmpStr );
|
|
ixmlFreeDOMString( headerStr );
|
|
if( retVal == IXML_INSUFFICIENT_MEMORY ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
} else {
|
|
return UPNP_E_INVALID_ACTION;
|
|
}
|
|
}
|
|
|
|
retVal = ixmlParseBufferEx( tmpStr, &( Param->Act ) );
|
|
if( retVal != IXML_SUCCESS ) {
|
|
ixmlFreeDOMString( tmpStr );
|
|
ixmlFreeDOMString( headerStr );
|
|
ixmlDocument_free( Param->Header );
|
|
if( retVal == IXML_INSUFFICIENT_MEMORY ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
} else {
|
|
return UPNP_E_INVALID_ACTION;
|
|
}
|
|
|
|
}
|
|
|
|
ixmlFreeDOMString( tmpStr );
|
|
ixmlFreeDOMString( headerStr );
|
|
|
|
Param->Cookie = ( void * )Cookie_const;
|
|
Param->Fun = Fun;
|
|
|
|
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free );
|
|
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpSendActionAsync\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
int UpnpGetServiceVarStatusAsync(
|
|
UpnpClient_Handle Hnd,
|
|
const char *ActionURL_const,
|
|
const char *VarName_const,
|
|
Upnp_FunPtr Fun,
|
|
const void *Cookie_const)
|
|
{
|
|
ThreadPoolJob job;
|
|
struct Handle_Info *SInfo = NULL;
|
|
struct UpnpNonblockParam *Param;
|
|
char *ActionURL = (char *)ActionURL_const;
|
|
char *VarName = (char *)VarName_const;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpGetServiceVarStatusAsync\n");
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
HandleUnlock();
|
|
|
|
if( ActionURL == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( VarName == NULL || Fun == NULL )
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
Param =
|
|
( struct UpnpNonblockParam * )
|
|
malloc( sizeof( struct UpnpNonblockParam ) );
|
|
if( Param == NULL ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
|
|
Param->FunName = STATUS;
|
|
Param->Handle = Hnd;
|
|
strcpy( Param->Url, ActionURL );
|
|
strcpy( Param->VarName, VarName );
|
|
Param->Fun = Fun;
|
|
Param->Cookie = ( void * )Cookie_const;
|
|
|
|
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
|
|
TPJobSetFreeFunction( &job, ( free_routine ) free );
|
|
|
|
TPJobSetPriority( &job, MED_PRIORITY );
|
|
|
|
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpGetServiceVarStatusAsync\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
int UpnpGetServiceVarStatus(
|
|
UpnpClient_Handle Hnd,
|
|
const char *ActionURL_const,
|
|
const char *VarName_const,
|
|
DOMString *StVar)
|
|
{
|
|
struct Handle_Info *SInfo = NULL;
|
|
int retVal = 0;
|
|
char *StVarPtr;
|
|
char *ActionURL = (char *)ActionURL_const;
|
|
char *VarName = (char *)VarName_const;
|
|
|
|
if(UpnpSdkInit != 1) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpGetServiceVarStatus\n");
|
|
|
|
HandleReadLock();
|
|
if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) {
|
|
HandleUnlock();
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
|
|
HandleUnlock();
|
|
|
|
if( ActionURL == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if( VarName == NULL || StVar == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
retVal = SoapGetServiceVarStatus( ActionURL, VarName, &StVarPtr );
|
|
*StVar = StVarPtr;
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpGetServiceVarStatus \n");
|
|
|
|
return retVal;
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
#endif /* EXCLUDE_SOAP */
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Client API
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
int UpnpOpenHttpPost(
|
|
const char *url,
|
|
void **handle,
|
|
const char *contentType,
|
|
int contentLength,
|
|
int timeout)
|
|
{
|
|
return http_OpenHttpPost(
|
|
url, handle, contentType, contentLength, timeout);
|
|
}
|
|
|
|
|
|
int UpnpWriteHttpPost(
|
|
void *handle,
|
|
char *buf,
|
|
unsigned int *size,
|
|
int timeout)
|
|
{
|
|
return http_WriteHttpPost(handle, buf, size, timeout);
|
|
}
|
|
|
|
|
|
int UpnpCloseHttpPost(
|
|
void *handle,
|
|
int *httpStatus,
|
|
int timeout)
|
|
{
|
|
return http_CloseHttpPost(handle, httpStatus, timeout);
|
|
}
|
|
|
|
|
|
int UpnpOpenHttpGet(
|
|
const char *url_str,
|
|
void **Handle,
|
|
char **contentType,
|
|
int *contentLength,
|
|
int *httpStatus,
|
|
int timeout)
|
|
{
|
|
return http_OpenHttpGet(
|
|
url_str, Handle, contentType, contentLength, httpStatus, timeout);
|
|
}
|
|
|
|
|
|
int UpnpOpenHttpGetProxy(
|
|
const char *url_str,
|
|
const char *proxy_str,
|
|
void **Handle,
|
|
char **contentType,
|
|
int *contentLength,
|
|
int *httpStatus,
|
|
int timeout)
|
|
{
|
|
return http_OpenHttpGetProxy(
|
|
url_str, proxy_str, Handle, contentType, contentLength,
|
|
httpStatus, timeout);
|
|
}
|
|
|
|
|
|
int UpnpOpenHttpGetEx(
|
|
const char *url_str,
|
|
void **Handle,
|
|
char **contentType,
|
|
int *contentLength,
|
|
int *httpStatus,
|
|
int lowRange,
|
|
int highRange,
|
|
int timeout)
|
|
{
|
|
return http_OpenHttpGetEx(
|
|
url_str, Handle, contentType, contentLength, httpStatus,
|
|
lowRange, highRange, timeout);
|
|
}
|
|
|
|
|
|
int UpnpCancelHttpGet(void *Handle)
|
|
{
|
|
return http_CancelHttpGet(Handle);
|
|
}
|
|
|
|
|
|
int UpnpCloseHttpGet(void *Handle)
|
|
{
|
|
return http_CloseHttpGet(Handle);
|
|
}
|
|
|
|
|
|
int UpnpReadHttpGet(void *Handle, char *buf, size_t *size, int timeout)
|
|
{
|
|
return http_ReadHttpGet(Handle, buf, size, timeout);
|
|
}
|
|
|
|
|
|
int UpnpHttpGetProgress(void *Handle, size_t *length, size_t *total)
|
|
{
|
|
return http_HttpGetProgress(Handle, length, total);
|
|
}
|
|
|
|
|
|
int UpnpDownloadUrlItem(const char *url, char **outBuf, char *contentType)
|
|
{
|
|
int ret_code;
|
|
int dummy;
|
|
|
|
if( url == NULL || outBuf == NULL || contentType == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
ret_code = http_Download( url, HTTP_DEFAULT_TIMEOUT, outBuf, &dummy,
|
|
contentType );
|
|
if( ret_code > 0 ) {
|
|
/* error reply was received */
|
|
ret_code = UPNP_E_INVALID_URL;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
|
|
int UpnpDownloadXmlDoc(const char *url, IXML_Document **xmlDoc)
|
|
{
|
|
int ret_code;
|
|
char *xml_buf;
|
|
char content_type[LINE_SIZE];
|
|
|
|
if (url == NULL || xmlDoc == NULL) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
ret_code = UpnpDownloadUrlItem(url, &xml_buf, content_type);
|
|
if (ret_code != UPNP_E_SUCCESS) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Error downloading document, retCode: %d\n", ret_code);
|
|
return ret_code;
|
|
}
|
|
|
|
if (strncasecmp(content_type, "text/xml", strlen("text/xml"))) {
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "Not text/xml\n");
|
|
/* Linksys WRT54G router returns
|
|
* "CONTENT-TYPE: application/octet-stream".
|
|
* Let's be nice to Linksys and try to parse document anyway.
|
|
* If the data sended is not a xml file, ixmlParseBufferEx
|
|
* will fail and the function will return UPNP_E_INVALID_DESC too. */
|
|
#if 0
|
|
free(xml_buf);
|
|
return UPNP_E_INVALID_DESC;
|
|
#endif
|
|
}
|
|
|
|
ret_code = ixmlParseBufferEx(xml_buf, xmlDoc);
|
|
free(xml_buf);
|
|
if (ret_code != IXML_SUCCESS) {
|
|
if (ret_code == IXML_INSUFFICIENT_MEMORY) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Out of memory, ixml error code: %d\n",
|
|
ret_code);
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
} else {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Invalid Description, ixml error code: %d\n",
|
|
ret_code);
|
|
return UPNP_E_INVALID_DESC;
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
xml_buf = ixmlPrintNode((IXML_Node *)*xmlDoc);
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Printing the Parsed xml document \n %s\n", xml_buf);
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"****************** END OF Parsed XML Doc *****************\n");
|
|
ixmlFreeDOMString(xml_buf);
|
|
#endif
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpDownloadXmlDoc\n");
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
int UpnpGetIfInfo(const char *IfName)
|
|
{
|
|
#ifdef WIN32
|
|
/* ---------------------------------------------------- */
|
|
/* WIN32 implementation will use the IpHlpAPI library. */
|
|
/* ---------------------------------------------------- */
|
|
PIP_ADAPTER_ADDRESSES adapts = NULL;
|
|
PIP_ADAPTER_ADDRESSES adapts_item;
|
|
PIP_ADAPTER_UNICAST_ADDRESS uni_addr;
|
|
SOCKADDR *ip_addr;
|
|
struct in_addr v4_addr;
|
|
struct in6_addr v6_addr;
|
|
ULONG adapts_sz = 0;
|
|
ULONG ret;
|
|
int ifname_found = 0;
|
|
int valid_addr_found = 0;
|
|
|
|
/* Get Adapters addresses required size. */
|
|
ret = GetAdaptersAddresses(AF_UNSPEC,
|
|
GAA_FLAG_SKIP_ANYCAST |
|
|
GAA_FLAG_SKIP_DNS_SERVER, NULL, adapts,
|
|
&adapts_sz);
|
|
if (ret != ERROR_BUFFER_OVERFLOW) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"GetAdaptersAddresses failed to find list of adapters\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
/* Allocate enough memory. */
|
|
adapts = (PIP_ADAPTER_ADDRESSES) malloc(adapts_sz);
|
|
if (adapts == NULL) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
/* Do the call that will actually return the info. */
|
|
ret = GetAdaptersAddresses(AF_UNSPEC,
|
|
GAA_FLAG_SKIP_ANYCAST |
|
|
GAA_FLAG_SKIP_DNS_SERVER, NULL, adapts,
|
|
&adapts_sz);
|
|
if (ret != ERROR_SUCCESS) {
|
|
free(adapts);
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"GetAdaptersAddresses failed to find list of adapters\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
/* Copy interface name, if it was provided. */
|
|
if (IfName != NULL) {
|
|
if (strlen(IfName) > sizeof(gIF_NAME))
|
|
return UPNP_E_INVALID_INTERFACE;
|
|
|
|
strncpy(gIF_NAME, IfName, sizeof(gIF_NAME));
|
|
ifname_found = 1;
|
|
}
|
|
adapts_item = adapts;
|
|
while (adapts_item != NULL) {
|
|
if (adapts_item->Flags & IP_ADAPTER_NO_MULTICAST) {
|
|
continue;
|
|
}
|
|
if (ifname_found == 0) {
|
|
/* We have found a valid interface name. Keep it. */
|
|
strncpy(gIF_NAME, adapts_item->FriendlyName,
|
|
sizeof(gIF_NAME));
|
|
ifname_found = 1;
|
|
} else {
|
|
if (strncmp
|
|
(gIF_NAME, adapts_item->FriendlyName,
|
|
sizeof(gIF_NAME)) != 0) {
|
|
/* This is not the interface we're looking for. */
|
|
continue;
|
|
}
|
|
}
|
|
/* Loop thru this adapter's unicast IP addresses. */
|
|
uni_addr = adapts_item->FirstUnicastAddress;
|
|
while (uni_addr) {
|
|
ip_addr = uni_addr->Address.lpSockaddr;
|
|
switch (ip_addr->sa_family) {
|
|
case AF_INET:
|
|
memcpy(&v4_addr,
|
|
&((struct sockaddr_in *)ip_addr)->
|
|
sin_addr, sizeof(v4_addr));
|
|
valid_addr_found = 1;
|
|
break;
|
|
case AF_INET6:
|
|
/* Only keep IPv6 link-local addresses. */
|
|
if (IN6_IS_ADDR_LINKLOCAL
|
|
(&((struct sockaddr_in6 *)ip_addr)->
|
|
sin6_addr)) {
|
|
memcpy(&v6_addr,
|
|
&((struct sockaddr_in6 *)
|
|
ip_addr)->sin6_addr,
|
|
sizeof(v6_addr));
|
|
valid_addr_found = 1;
|
|
}
|
|
break;
|
|
default:
|
|
if (valid_addr_found == 0) {
|
|
/* Address is not IPv4 or IPv6 and no valid address has */
|
|
/* yet been found for this interface. Discard interface name. */
|
|
ifname_found = 0;
|
|
}
|
|
break;
|
|
}
|
|
/* Next address. */
|
|
uni_addr = uni_addr->Next;
|
|
}
|
|
if (valid_addr_found == 1) {
|
|
gIF_INDEX = adapts_item->IfIndex;
|
|
break;
|
|
}
|
|
/* Next adapter. */
|
|
adapts_item = adapts_item->Next;
|
|
}
|
|
/* Failed to find a valid interface, or valid address. */
|
|
if (ifname_found == 0 || valid_addr_found == 0) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Failed to find an adapter with valid IP addresses for use.\n");
|
|
return UPNP_E_INVALID_INTERFACE;
|
|
}
|
|
inet_ntop(AF_INET, &v4_addr, gIF_IPV4, sizeof(gIF_IPV4));
|
|
inet_ntop(AF_INET6, &v6_addr, gIF_IPV6, sizeof(gIF_IPV6));
|
|
#elif (defined(BSD) && BSD >= 199306) || defined(__FreeBSD_kernel__)
|
|
struct ifaddrs *ifap, *ifa;
|
|
struct in_addr v4_addr;
|
|
struct in6_addr v6_addr;
|
|
int ifname_found = 0;
|
|
int valid_addr_found = 0;
|
|
|
|
/* Copy interface name, if it was provided. */
|
|
if (IfName != NULL) {
|
|
if (strlen(IfName) > sizeof(gIF_NAME))
|
|
return UPNP_E_INVALID_INTERFACE;
|
|
|
|
strncpy(gIF_NAME, IfName, sizeof(gIF_NAME));
|
|
ifname_found = 1;
|
|
}
|
|
/* Get system interface addresses. */
|
|
if (getifaddrs(&ifap) != 0) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"getifaddrs failed to find list of addresses\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
/* cycle through available interfaces and their addresses. */
|
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
|
|
/* Skip LOOPBACK interfaces, DOWN interfaces and interfaces that */
|
|
/* don't support MULTICAST. */
|
|
if ((ifa->ifa_flags & IFF_LOOPBACK)
|
|
|| (!(ifa->ifa_flags & IFF_UP))
|
|
|| (!(ifa->ifa_flags & IFF_MULTICAST))) {
|
|
continue;
|
|
}
|
|
if (ifname_found == 0) {
|
|
/* We have found a valid interface name. Keep it. */
|
|
strncpy(gIF_NAME, ifa->ifa_name, sizeof(gIF_NAME));
|
|
ifname_found = 1;
|
|
} else {
|
|
if (strncmp(gIF_NAME, ifa->ifa_name, sizeof(gIF_NAME))
|
|
!= 0) {
|
|
/* This is not the interface we're looking for. */
|
|
continue;
|
|
}
|
|
}
|
|
/* Keep interface addresses for later. */
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
case AF_INET:
|
|
memcpy(&v4_addr,
|
|
&((struct sockaddr_in *)(ifa->ifa_addr))->
|
|
sin_addr, sizeof(v4_addr));
|
|
valid_addr_found = 1;
|
|
break;
|
|
case AF_INET6:
|
|
/* Only keep IPv6 link-local addresses. */
|
|
if (IN6_IS_ADDR_LINKLOCAL
|
|
(&((struct sockaddr_in6 *)(ifa->ifa_addr))->
|
|
sin6_addr)) {
|
|
memcpy(&v6_addr,
|
|
&((struct sockaddr_in6 *)(ifa->
|
|
ifa_addr))->
|
|
sin6_addr, sizeof(v6_addr));
|
|
valid_addr_found = 1;
|
|
}
|
|
break;
|
|
default:
|
|
if (valid_addr_found == 0) {
|
|
/* Address is not IPv4 or IPv6 and no valid address has */
|
|
/* yet been found for this interface. Discard interface name. */
|
|
ifname_found = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
freeifaddrs(ifap);
|
|
/* Failed to find a valid interface, or valid address. */
|
|
if (ifname_found == 0 || valid_addr_found == 0) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Failed to find an adapter with valid IP addresses for use.\n");
|
|
return UPNP_E_INVALID_INTERFACE;
|
|
}
|
|
inet_ntop(AF_INET, &v4_addr, gIF_IPV4, sizeof(gIF_IPV4));
|
|
inet_ntop(AF_INET6, &v6_addr, gIF_IPV6, sizeof(gIF_IPV6));
|
|
gIF_INDEX = if_nametoindex(gIF_NAME);
|
|
#else
|
|
char szBuffer[MAX_INTERFACES * sizeof(struct ifreq)];
|
|
struct ifconf ifConf;
|
|
struct ifreq ifReq;
|
|
FILE *inet6_procfd;
|
|
size_t i;
|
|
int LocalSock;
|
|
struct in6_addr v6_addr;
|
|
unsigned if_idx;
|
|
char addr6[8][5];
|
|
char buf[65]; /* INET6_ADDRSTRLEN */
|
|
int ifname_found = 0;
|
|
int valid_addr_found = 0;
|
|
|
|
/* Copy interface name, if it was provided. */
|
|
if (IfName != NULL) {
|
|
if (strlen(IfName) > sizeof(gIF_NAME))
|
|
return UPNP_E_INVALID_INTERFACE;
|
|
|
|
strncpy(gIF_NAME, IfName, sizeof(gIF_NAME));
|
|
ifname_found = 1;
|
|
}
|
|
/* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */
|
|
if ((LocalSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Can't create addrlist socket\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
/* Get the interface configuration information... */
|
|
ifConf.ifc_len = sizeof szBuffer;
|
|
ifConf.ifc_ifcu.ifcu_buf = (caddr_t) szBuffer;
|
|
|
|
if (ioctl(LocalSock, SIOCGIFCONF, &ifConf) < 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"DiscoverInterfaces: SIOCGIFCONF returned error\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
/* Cycle through the list of interfaces looking for IP addresses. */
|
|
for (i = 0; i < (size_t)ifConf.ifc_len;) {
|
|
struct ifreq *pifReq =
|
|
(struct ifreq *)((caddr_t) ifConf.ifc_req + i);
|
|
i += sizeof *pifReq;
|
|
/* See if this is the sort of interface we want to deal with. */
|
|
strcpy(ifReq.ifr_name, pifReq->ifr_name);
|
|
if (ioctl(LocalSock, SIOCGIFFLAGS, &ifReq) < 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Can't get interface flags for %s:\n",
|
|
ifReq.ifr_name);
|
|
}
|
|
/* Skip LOOPBACK interfaces, DOWN interfaces and interfaces that */
|
|
/* don't support MULTICAST. */
|
|
if ((ifReq.ifr_flags & IFF_LOOPBACK)
|
|
|| (!(ifReq.ifr_flags & IFF_UP))
|
|
|| (!(ifReq.ifr_flags & IFF_MULTICAST))) {
|
|
continue;
|
|
}
|
|
if (ifname_found == 0) {
|
|
/* We have found a valid interface name. Keep it. */
|
|
strncpy(gIF_NAME, pifReq->ifr_name, sizeof(gIF_NAME));
|
|
ifname_found = 1;
|
|
} else {
|
|
if (strncmp
|
|
(gIF_NAME, pifReq->ifr_name,
|
|
sizeof(gIF_NAME)) != 0) {
|
|
/* This is not the interface we're looking for. */
|
|
continue;
|
|
}
|
|
}
|
|
/* Check address family. */
|
|
if (pifReq->ifr_addr.sa_family == AF_INET) {
|
|
/* Copy interface name, IPv4 address and interface index. */
|
|
strncpy(gIF_NAME, pifReq->ifr_name, sizeof(gIF_NAME));
|
|
inet_ntop(AF_INET,
|
|
&((struct sockaddr_in *)&pifReq->ifr_addr)->
|
|
sin_addr, gIF_IPV4, sizeof(gIF_IPV4));
|
|
gIF_INDEX = if_nametoindex(gIF_NAME);
|
|
valid_addr_found = 1;
|
|
break;
|
|
} else {
|
|
/* Address is not IPv4 */
|
|
ifname_found = 0;
|
|
}
|
|
}
|
|
close(LocalSock);
|
|
/* Failed to find a valid interface, or valid address. */
|
|
if (ifname_found == 0 || valid_addr_found == 0) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"Failed to find an adapter with valid IP addresses for use.\n");
|
|
|
|
return UPNP_E_INVALID_INTERFACE;
|
|
}
|
|
/* Try to get the IPv6 address for the same interface */
|
|
/* from "/proc/net/if_inet6", if possible. */
|
|
inet6_procfd = fopen("/proc/net/if_inet6", "r");
|
|
if (inet6_procfd) {
|
|
while (fscanf(inet6_procfd,
|
|
"%4s%4s%4s%4s%4s%4s%4s%4s %02x %*02x %*02x %*02x %*20s\n",
|
|
addr6[0], addr6[1], addr6[2], addr6[3],
|
|
addr6[4], addr6[5], addr6[6], addr6[7],
|
|
&if_idx) != EOF) {
|
|
/* Get same interface as IPv4 address retrieved. */
|
|
if (gIF_INDEX == if_idx) {
|
|
snprintf(buf, sizeof(buf),
|
|
"%s:%s:%s:%s:%s:%s:%s:%s", addr6[0],
|
|
addr6[1], addr6[2], addr6[3], addr6[4],
|
|
addr6[5], addr6[6], addr6[7]);
|
|
/* Validate formed address and check for link-local. */
|
|
if (inet_pton(AF_INET6, buf, &v6_addr) > 0) {
|
|
if (IN6_IS_ADDR_ULA(&v6_addr)) {
|
|
/* Got valid IPv6 ula. */
|
|
strncpy(gIF_IPV6_ULA_GUA, buf,
|
|
sizeof
|
|
(gIF_IPV6_ULA_GUA));
|
|
} else if (IN6_IS_ADDR_GLOBAL(&v6_addr)
|
|
&& strlen(gIF_IPV6_ULA_GUA)
|
|
== 0) {
|
|
/* got a GUA, should store it while no ULA is found */
|
|
strncpy(gIF_IPV6_ULA_GUA, buf,
|
|
sizeof
|
|
(gIF_IPV6_ULA_GUA));
|
|
} else
|
|
if (IN6_IS_ADDR_LINKLOCAL(&v6_addr)
|
|
&& strlen(gIF_IPV6) == 0) {
|
|
/* got a Link local IPv6 address. */
|
|
strncpy(gIF_IPV6, buf,
|
|
sizeof(gIF_IPV6));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(inet6_procfd);
|
|
}
|
|
#endif
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"Interface name=%s, index=%d, v4=%s, v6=%s, ULA or GUA v6=%s\n",
|
|
gIF_NAME, gIF_INDEX, gIF_IPV4, gIF_IPV6, gIF_IPV6_ULA_GUA);
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Schedule async functions in threadpool.
|
|
*/
|
|
#ifdef INCLUDE_CLIENT_APIS
|
|
void UpnpThreadDistribution(struct UpnpNonblockParam *Param)
|
|
{
|
|
/*int errCode = 0;*/
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside UpnpThreadDistribution \n");
|
|
|
|
switch (Param->FunName) {
|
|
#if EXCLUDE_GENA == 0
|
|
case SUBSCRIBE: {
|
|
struct Upnp_Event_Subscribe Evt;
|
|
/* Cast away constness */
|
|
/*UpnpString *Sid = (UpnpString *)UpnpEventSubscribe_get_SID(evt);*/
|
|
UpnpString *Sid = UpnpString_new();
|
|
UpnpString *Url = UpnpString_new();
|
|
UpnpString_set_String(Url, Param->Url);
|
|
UpnpString_set_String(Sid, (char *)Evt.Sid);
|
|
Evt.ErrCode = genaSubscribe(
|
|
Param->Handle,
|
|
Url,
|
|
(int *)&Param->TimeOut,
|
|
Sid);
|
|
strcpy(Evt.PublisherUrl, Param->Url);
|
|
Evt.TimeOut = Param->TimeOut;
|
|
Param->Fun(UPNP_EVENT_SUBSCRIBE_COMPLETE, &Evt, Param->Cookie);
|
|
UpnpString_delete(Sid);
|
|
UpnpString_delete(Url);
|
|
free(Param);
|
|
break;
|
|
}
|
|
case UNSUBSCRIBE: {
|
|
struct Upnp_Event_Subscribe Evt;
|
|
UpnpString *Sid = UpnpString_new();
|
|
UpnpString_set_String(Sid, Param->SubsId);
|
|
Evt.ErrCode = genaUnSubscribe(
|
|
Param->Handle,
|
|
Sid);
|
|
strcpy((char *)Evt.Sid, UpnpString_get_String(Sid));
|
|
strcpy(Evt.PublisherUrl, "");
|
|
Evt.TimeOut = 0;
|
|
Param->Fun(UPNP_EVENT_UNSUBSCRIBE_COMPLETE, &Evt, Param->Cookie);
|
|
UpnpString_delete(Sid);
|
|
free(Param);
|
|
break;
|
|
}
|
|
case RENEW: {
|
|
struct Upnp_Event_Subscribe Evt;
|
|
UpnpString *Sid = UpnpString_new();
|
|
UpnpString_set_String(Sid, Param->SubsId);
|
|
Evt.ErrCode = genaRenewSubscription(
|
|
Param->Handle,
|
|
Sid,
|
|
&Param->TimeOut);
|
|
Evt.TimeOut = Param->TimeOut;
|
|
strcpy((char *)Evt.Sid, UpnpString_get_String(Sid));
|
|
Param->Fun(UPNP_EVENT_RENEWAL_COMPLETE, &Evt, Param->Cookie);
|
|
UpnpString_delete(Sid);
|
|
free(Param);
|
|
break;
|
|
}
|
|
#endif /* EXCLUDE_GENA == 0 */
|
|
#if EXCLUDE_SOAP == 0
|
|
case ACTION: {
|
|
struct Upnp_Action_Complete Evt;
|
|
Evt.ActionResult = NULL;
|
|
Evt.ErrCode = SoapSendAction(
|
|
Param->Url,
|
|
Param->ServiceType,
|
|
Param->Act, &Evt.ActionResult);
|
|
Evt.ActionRequest = Param->Act;
|
|
strcpy(Evt.CtrlUrl, Param->Url);
|
|
Param->Fun(UPNP_CONTROL_ACTION_COMPLETE, &Evt, Param->Cookie);
|
|
ixmlDocument_free(Evt.ActionRequest);
|
|
ixmlDocument_free(Evt.ActionResult);
|
|
free(Param);
|
|
break;
|
|
}
|
|
case STATUS: {
|
|
struct Upnp_State_Var_Complete Evt;
|
|
Evt.ErrCode = SoapGetServiceVarStatus(
|
|
Param->Url,
|
|
Param->VarName,
|
|
&Evt.CurrentVal);
|
|
strcpy(Evt.StateVarName, Param->VarName);
|
|
strcpy(Evt.CtrlUrl, Param->Url);
|
|
Param->Fun(UPNP_CONTROL_GET_VAR_COMPLETE, &Evt, Param->Cookie);
|
|
free(Evt.CurrentVal);
|
|
free(Param);
|
|
break;
|
|
}
|
|
#endif /* EXCLUDE_SOAP == 0 */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Exiting UpnpThreadDistribution\n");
|
|
}
|
|
#endif /* INCLUDE_CLIENT_APIS */
|
|
|
|
|
|
/*!
|
|
* \brief Get callback function ptr from a handle.
|
|
*
|
|
* \return Upnp_FunPtr
|
|
*/
|
|
Upnp_FunPtr GetCallBackFn(UpnpClient_Handle Hnd)
|
|
{
|
|
return ((struct Handle_Info *)HandleTable[Hnd])->Callback;
|
|
}
|
|
|
|
|
|
/* Assumes at most one client */
|
|
Upnp_Handle_Type GetClientHandleInfo(
|
|
UpnpClient_Handle *client_handle_out,
|
|
struct Handle_Info **HndInfo)
|
|
{
|
|
Upnp_Handle_Type ret = HND_CLIENT;
|
|
UpnpClient_Handle client;
|
|
|
|
if (GetHandleInfo(1, HndInfo) == HND_CLIENT) {
|
|
client = 1;
|
|
} else if (GetHandleInfo(2, HndInfo) == HND_CLIENT) {
|
|
client = 2;
|
|
} else {
|
|
client = -1;
|
|
ret = HND_INVALID;
|
|
}
|
|
|
|
*client_handle_out = client;
|
|
return ret;
|
|
}
|
|
|
|
|
|
Upnp_Handle_Type GetDeviceHandleInfo(
|
|
const int AddressFamily,
|
|
UpnpDevice_Handle *device_handle_out,
|
|
struct Handle_Info **HndInfo)
|
|
{
|
|
/* Check if we've got a registered device of the address family specified. */
|
|
if ((AddressFamily == AF_INET && UpnpSdkDeviceRegisteredV4 == 0) ||
|
|
(AddressFamily == AF_INET6 && UpnpSdkDeviceregisteredV6 == 0)) {
|
|
*device_handle_out = -1;
|
|
return HND_INVALID;
|
|
}
|
|
|
|
/* Find it. */
|
|
for (*device_handle_out=1; *device_handle_out < NUM_HANDLE; (*device_handle_out)++) {
|
|
if (GetHandleInfo(*device_handle_out, HndInfo) == HND_DEVICE) {
|
|
if ((*HndInfo)->DeviceAf == AddressFamily) {
|
|
return HND_DEVICE;
|
|
}
|
|
}
|
|
}
|
|
|
|
*device_handle_out = -1;
|
|
return HND_INVALID;
|
|
}
|
|
|
|
|
|
Upnp_Handle_Type GetHandleInfo(
|
|
UpnpClient_Handle Hnd,
|
|
struct Handle_Info **HndInfo)
|
|
{
|
|
Upnp_Handle_Type ret = UPNP_E_INVALID_HANDLE;
|
|
|
|
UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
|
|
"GetHandleInfo: entering, Handle is %d\n", Hnd);
|
|
|
|
if (Hnd < 1 || Hnd >= NUM_HANDLE) {
|
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
|
"GetHandleInfo: Handle out of range\n");
|
|
} else if (HandleTable[Hnd] == NULL) {
|
|
UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
|
|
"GetHandleInfo: HandleTable[%d] is NULL\n",
|
|
Hnd);
|
|
} else if (HandleTable[Hnd] != NULL) {
|
|
*HndInfo = (struct Handle_Info *)HandleTable[Hnd];
|
|
ret = ((struct Handle_Info *)*HndInfo)->HType;
|
|
}
|
|
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, "GetHandleInfo: exiting\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int PrintHandleInfo(UpnpClient_Handle Hnd)
|
|
{
|
|
struct Handle_Info * HndInfo;
|
|
if (HandleTable[Hnd] != NULL) {
|
|
HndInfo = HandleTable[Hnd];
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Printing information for Handle_%d\n", Hnd);
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"HType_%d\n", HndInfo->HType);
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
if(HndInfo->HType != HND_CLIENT)
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"DescURL_%s\n", HndInfo->DescURL);
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
} else {
|
|
return UPNP_E_INVALID_HANDLE;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
int getlocalhostname(char *out, size_t out_len)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
char tempstr[16];
|
|
const char *p = NULL;
|
|
|
|
#ifdef WIN32
|
|
struct hostent *h = NULL;
|
|
struct sockaddr_in LocalAddr;
|
|
|
|
gethostname(out, out_len);
|
|
h = gethostbyname(out);
|
|
if (h != NULL) {
|
|
memcpy(&LocalAddr.sin_addr, h->h_addr_list[0], 4);
|
|
p = inet_ntop(AF_INET, &LocalAddr.sin_addr, tempstr, sizeof(tempstr));
|
|
if (p) {
|
|
strncpy(out, p, out_len);
|
|
} else {
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"getlocalhostname: inet_ntop returned error\n" );
|
|
ret = UPNP_E_INIT;
|
|
}
|
|
} else {
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"getlocalhostname: gethostbyname returned error\n" );
|
|
ret = UPNP_E_INIT;
|
|
}
|
|
|
|
#elif (defined(BSD) && BSD >= 199306) || defined(__FreeBSD_kernel__)
|
|
struct ifaddrs *ifap, *ifa;
|
|
|
|
if (getifaddrs(&ifap) != 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"DiscoverInterfaces: getifaddrs() returned error\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
|
|
/* cycle through available interfaces */
|
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
|
|
/* Skip loopback, point-to-point and down interfaces,
|
|
* except don't skip down interfaces
|
|
* if we're trying to get a list of configurable interfaces. */
|
|
if ((ifa->ifa_flags & IFF_LOOPBACK) ||
|
|
(!( ifa->ifa_flags & IFF_UP))) {
|
|
continue;
|
|
}
|
|
if (ifa->ifa_addr->sa_family == AF_INET) {
|
|
/* We don't want the loopback interface. */
|
|
if (((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr.s_addr ==
|
|
htonl(INADDR_LOOPBACK)) {
|
|
continue;
|
|
}
|
|
p = inet_ntop(AF_INET,
|
|
&((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr,
|
|
tempstr, sizeof(tempstr));
|
|
if (p) {
|
|
strncpy(out, p, out_len);
|
|
} else {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"getlocalhostname: inet_ntop returned error\n");
|
|
ret = UPNP_E_INIT;
|
|
}
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside getlocalhostname: after strncpy %s\n", out);
|
|
break;
|
|
}
|
|
}
|
|
freeifaddrs(ifap);
|
|
|
|
ret = ifa ? UPNP_E_SUCCESS : UPNP_E_INIT;
|
|
#else
|
|
char szBuffer[MAX_INTERFACES * sizeof (struct ifreq)];
|
|
struct ifconf ifConf;
|
|
struct ifreq ifReq;
|
|
int nResult;
|
|
long unsigned int i;
|
|
int LocalSock;
|
|
struct sockaddr_in LocalAddr;
|
|
int j = 0;
|
|
|
|
/* purify */
|
|
memset(&ifConf, 0, sizeof(ifConf));
|
|
memset(&ifReq, 0, sizeof(ifReq));
|
|
memset(szBuffer, 0, sizeof(szBuffer));
|
|
|
|
/* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */
|
|
LocalSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (LocalSock < 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Can't create addrlist socket\n");
|
|
return UPNP_E_INIT;
|
|
}
|
|
/* Get the interface configuration information... */
|
|
ifConf.ifc_len = sizeof szBuffer;
|
|
ifConf.ifc_ifcu.ifcu_buf = (caddr_t) szBuffer;
|
|
nResult = ioctl(LocalSock, SIOCGIFCONF, &ifConf);
|
|
if (nResult < 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"DiscoverInterfaces: SIOCGIFCONF returned error\n");
|
|
|
|
return UPNP_E_INIT;
|
|
}
|
|
|
|
/* Cycle through the list of interfaces looking for IP addresses. */
|
|
for (i = 0; i < (long unsigned int)ifConf.ifc_len && j < DEFAULT_INTERFACE; ) {
|
|
struct ifreq *pifReq =
|
|
(struct ifreq *)((caddr_t)ifConf.ifc_req + i);
|
|
i += sizeof *pifReq;
|
|
/* See if this is the sort of interface we want to deal with. */
|
|
strcpy(ifReq.ifr_name, pifReq->ifr_name);
|
|
if (ioctl(LocalSock, SIOCGIFFLAGS, &ifReq) < 0) {
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Can't get interface flags for %s:\n",
|
|
ifReq.ifr_name);
|
|
}
|
|
/* Skip loopback, point-to-point and down interfaces,
|
|
* except don't skip down interfaces
|
|
* if we're trying to get a list of configurable interfaces. */
|
|
if ((ifReq.ifr_flags & IFF_LOOPBACK) ||
|
|
(!(ifReq.ifr_flags & IFF_UP))) {
|
|
continue;
|
|
}
|
|
if (pifReq->ifr_addr.sa_family == AF_INET) {
|
|
/* Get a pointer to the address...*/
|
|
memcpy(&LocalAddr, &pifReq->ifr_addr,
|
|
sizeof pifReq->ifr_addr);
|
|
/* We don't want the loopback interface. */
|
|
if (LocalAddr.sin_addr.s_addr ==
|
|
htonl(INADDR_LOOPBACK)) {
|
|
continue;
|
|
}
|
|
}
|
|
/* increment j if we found an address which is not loopback
|
|
* and is up */
|
|
j++;
|
|
}
|
|
close(LocalSock);
|
|
|
|
p = inet_ntop(AF_INET, &LocalAddr.sin_addr, tempstr, sizeof(tempstr));
|
|
if (p) {
|
|
strncpy(out, p, out_len);
|
|
} else {
|
|
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
|
"getlocalhostname: inet_ntop returned error\n" );
|
|
ret = UPNP_E_INIT;
|
|
}
|
|
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
|
|
"Inside getlocalhostname: after strncpy %s\n", out);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef INCLUDE_DEVICE_APIS
|
|
#if EXCLUDE_SSDP == 0
|
|
void AutoAdvertise(void *input)
|
|
{
|
|
upnp_timeout *event = (upnp_timeout *)input;
|
|
|
|
UpnpSendAdvertisement(event->handle, *((int *)event->Event));
|
|
free_upnp_timeout(event);
|
|
}
|
|
#endif /* EXCLUDE_SSDP == 0 */
|
|
#endif /* INCLUDE_DEVICE_APIS */
|
|
|
|
|
|
#ifdef INTERNAL_WEB_SERVER
|
|
int UpnpSetWebServerRootDir(const char *rootDir)
|
|
{
|
|
if( UpnpSdkInit == 0 )
|
|
return UPNP_E_FINISH;
|
|
if( ( rootDir == NULL ) || ( strlen( rootDir ) == 0 ) ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
membuffer_destroy( &gDocumentRootDir );
|
|
|
|
return web_server_set_root_dir(rootDir);
|
|
}
|
|
#endif /* INTERNAL_WEB_SERVER */
|
|
|
|
|
|
int UpnpAddVirtualDir(const char *newDirName)
|
|
{
|
|
virtualDirList *pNewVirtualDir;
|
|
virtualDirList *pLast;
|
|
virtualDirList *pCurVirtualDir;
|
|
char dirName[NAME_SIZE];
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
/* SDK is not initialized */
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
if( ( newDirName == NULL ) || ( strlen( newDirName ) == 0 ) ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
if( *newDirName != '/' ) {
|
|
dirName[0] = '/';
|
|
strcpy( dirName + 1, newDirName );
|
|
} else {
|
|
strcpy( dirName, newDirName );
|
|
}
|
|
|
|
pCurVirtualDir = pVirtualDirList;
|
|
while( pCurVirtualDir != NULL ) {
|
|
/* already has this entry */
|
|
if( strcmp( pCurVirtualDir->dirName, dirName ) == 0 ) {
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
pCurVirtualDir = pCurVirtualDir->next;
|
|
}
|
|
|
|
pNewVirtualDir =
|
|
( virtualDirList * ) malloc( sizeof( virtualDirList ) );
|
|
if( pNewVirtualDir == NULL ) {
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
pNewVirtualDir->next = NULL;
|
|
strcpy( pNewVirtualDir->dirName, dirName );
|
|
*( pNewVirtualDir->dirName + strlen( dirName ) ) = 0;
|
|
|
|
if( pVirtualDirList == NULL ) { /* first virtual dir */
|
|
pVirtualDirList = pNewVirtualDir;
|
|
} else {
|
|
pLast = pVirtualDirList;
|
|
while( pLast->next != NULL ) {
|
|
pLast = pLast->next;
|
|
}
|
|
pLast->next = pNewVirtualDir;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
int UpnpRemoveVirtualDir(const char *dirName)
|
|
{
|
|
virtualDirList *pPrev;
|
|
virtualDirList *pCur;
|
|
int found = 0;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
if( dirName == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
if( pVirtualDirList == NULL ) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
/* Handle the special case where the directory that we are */
|
|
/* removing is the first and only one in the list. */
|
|
if( ( pVirtualDirList->next == NULL ) &&
|
|
( strcmp( pVirtualDirList->dirName, dirName ) == 0 ) ) {
|
|
free( pVirtualDirList );
|
|
pVirtualDirList = NULL;
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
pCur = pVirtualDirList;
|
|
pPrev = pCur;
|
|
|
|
while( pCur != NULL ) {
|
|
if( strcmp( pCur->dirName, dirName ) == 0 ) {
|
|
pPrev->next = pCur->next;
|
|
free( pCur );
|
|
found = 1;
|
|
break;
|
|
} else {
|
|
pPrev = pCur;
|
|
pCur = pCur->next;
|
|
}
|
|
}
|
|
|
|
if( found == 1 )
|
|
return UPNP_E_SUCCESS;
|
|
else
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
|
|
void UpnpRemoveAllVirtualDirs(void)
|
|
{
|
|
virtualDirList *pCur;
|
|
virtualDirList *pNext;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return;
|
|
}
|
|
|
|
pCur = pVirtualDirList;
|
|
|
|
while( pCur != NULL ) {
|
|
pNext = pCur->next;
|
|
free( pCur );
|
|
|
|
pCur = pNext;
|
|
}
|
|
|
|
pVirtualDirList = NULL;
|
|
}
|
|
|
|
|
|
int UpnpEnableWebserver(int enable)
|
|
{
|
|
int retVal;
|
|
|
|
if( UpnpSdkInit != 1 ) {
|
|
return UPNP_E_FINISH;
|
|
}
|
|
|
|
switch ( enable ) {
|
|
#ifdef INTERNAL_WEB_SERVER
|
|
case TRUE:
|
|
if( ( retVal = web_server_init() ) != UPNP_E_SUCCESS ) {
|
|
return retVal;
|
|
}
|
|
bWebServerState = WEB_SERVER_ENABLED;
|
|
SetHTTPGetCallback( web_server_callback );
|
|
break;
|
|
|
|
case FALSE:
|
|
web_server_destroy();
|
|
bWebServerState = WEB_SERVER_DISABLED;
|
|
SetHTTPGetCallback( NULL );
|
|
break;
|
|
#endif /* INTERNAL_WEB_SERVER */
|
|
default:
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Checks if the webserver is enabled or disabled.
|
|
*
|
|
* \return 1, if webserver is enabled or 0, if webserver is disabled.
|
|
*/
|
|
int UpnpIsWebserverEnabled(void)
|
|
{
|
|
if (UpnpSdkInit != 1) {
|
|
return 0;
|
|
}
|
|
|
|
return bWebServerState == WEB_SERVER_ENABLED;
|
|
}
|
|
|
|
|
|
int UpnpVirtualDir_set_GetInfoCallback(VDCallback_GetInfo callback)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
if (!callback) {
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
} else {
|
|
virtualDirCallback.get_info = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int UpnpVirtualDir_set_OpenCallback(VDCallback_Open callback)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
if (!callback) {
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
} else {
|
|
virtualDirCallback.open = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int UpnpVirtualDir_set_ReadCallback(VDCallback_Read callback)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
if (!callback) {
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
} else {
|
|
virtualDirCallback.read = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int UpnpVirtualDir_set_WriteCallback(VDCallback_Write callback)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
if (!callback) {
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
} else {
|
|
virtualDirCallback.write = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int UpnpVirtualDir_set_SeekCallback(VDCallback_Seek callback)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
if (!callback) {
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
} else {
|
|
virtualDirCallback.seek = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int UpnpVirtualDir_set_CloseCallback(VDCallback_Close callback)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
if (!callback) {
|
|
ret = UPNP_E_INVALID_PARAM;
|
|
} else {
|
|
virtualDirCallback.close = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int UpnpSetContentLength(UpnpClient_Handle Hnd, size_t contentLength)
|
|
{
|
|
int errCode = UPNP_E_SUCCESS;
|
|
struct Handle_Info *HInfo = NULL;
|
|
|
|
do {
|
|
if (UpnpSdkInit != 1) {
|
|
errCode = UPNP_E_FINISH;
|
|
break;
|
|
}
|
|
|
|
HandleLock();
|
|
|
|
errCode = GetHandleInfo(Hnd, &HInfo);
|
|
if (errCode != HND_DEVICE) {
|
|
errCode = UPNP_E_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
if (contentLength > MAX_SOAP_CONTENT_LENGTH) {
|
|
errCode = UPNP_E_OUTOF_BOUNDS;
|
|
break;
|
|
}
|
|
g_maxContentLength = contentLength;
|
|
} while (0);
|
|
|
|
HandleUnlock();
|
|
return errCode;
|
|
}
|
|
|
|
int UpnpSetMaxContentLength(size_t contentLength)
|
|
{
|
|
int errCode = UPNP_E_SUCCESS;
|
|
|
|
do {
|
|
if (UpnpSdkInit != 1) {
|
|
errCode = UPNP_E_FINISH;
|
|
break;
|
|
}
|
|
g_maxContentLength = contentLength;
|
|
} while(0);
|
|
|
|
return errCode;
|
|
}
|
|
|