Compare commits
16 Commits
release-1.
...
branch-1.6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bc335f5df7 | ||
![]() |
cf31814e55 | ||
![]() |
3b0fd070fd | ||
![]() |
9f69fb1b80 | ||
![]() |
c124ae6507 | ||
![]() |
51a01cdba1 | ||
![]() |
6905714a7e | ||
![]() |
72460df747 | ||
![]() |
a596abfbe3 | ||
![]() |
fbbb24f406 | ||
![]() |
0508fb0d6e | ||
![]() |
848d66e69d | ||
![]() |
04fb684323 | ||
![]() |
faaef39a3c | ||
![]() |
a1193f385e | ||
![]() |
c20f2bd3a1 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -73,6 +73,14 @@ GRTAGS
|
|||||||
GSYMS
|
GSYMS
|
||||||
GTAGS
|
GTAGS
|
||||||
|
|
||||||
|
# QT-Creator files
|
||||||
|
Makefile.am.user
|
||||||
|
pupnp.config
|
||||||
|
pupnp.creator
|
||||||
|
pupnp.creator.user
|
||||||
|
pupnp.files
|
||||||
|
pupnp.includes
|
||||||
|
|
||||||
*.orig
|
*.orig
|
||||||
*~
|
*~
|
||||||
\#*#
|
\#*#
|
||||||
@@ -109,4 +117,4 @@ docs/doxygen
|
|||||||
/build/vc10/out.vc9.Win32/Debug
|
/build/vc10/out.vc9.Win32/Debug
|
||||||
/build/vc10/out.vc10.Win32
|
/build/vc10/out.vc10.Win32
|
||||||
/build/vc10/out.vc10.x64
|
/build/vc10/out.vc10.x64
|
||||||
/pthreads
|
/pthreads
|
||||||
|
58
ChangeLog
58
ChangeLog
@@ -1,3 +1,61 @@
|
|||||||
|
*******************************************************************************
|
||||||
|
Version 1.6.20
|
||||||
|
*******************************************************************************
|
||||||
|
|
||||||
|
2015-02-04 Shaun Marko <semarko@users.sf.net>
|
||||||
|
|
||||||
|
Bug tracker #124 Build fails with --enable-debug
|
||||||
|
|
||||||
|
Build environment
|
||||||
|
Fedora 21
|
||||||
|
X86-64
|
||||||
|
* gcc 4.9.2
|
||||||
|
|
||||||
|
How to repeat
|
||||||
|
$ ./configure --enable debug
|
||||||
|
$ make
|
||||||
|
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I.. -I../upnp/inc -I./inc -I../threadutil/inc
|
||||||
|
-I../ixml/inc -I./src/inc -pthread -g -O2 -Wall -MT src/api/libupnp_la-UpnpString.lo
|
||||||
|
-MD -MP -MF src/api/.deps/libupnp_la-UpnpString.Tpo -c src/api/UpnpString.c
|
||||||
|
-fPIC -DPIC -o src/api .libs/libupnp_la-UpnpString.o src/api/UpnpString.c:47:16:
|
||||||
|
error: expected identifier or '(' before 'extension'
|
||||||
|
extern char *strndup(const char *string, size_t __n);
|
||||||
|
^
|
||||||
|
Makefile:1016: recipe for target 'src/api/libupnp_la-UpnpString.lo' failed
|
||||||
|
|
||||||
|
Reason for failure
|
||||||
|
Build enables -O2 optimization flags which causes the inclusion of a
|
||||||
|
macro implementation of strndup from include/bits/string2.h.
|
||||||
|
|
||||||
|
Workarounds
|
||||||
|
Disable optimization when configuring or making:
|
||||||
|
$ configure CFLAGS='-g -pthread -O0' --enable-debug
|
||||||
|
$ make
|
||||||
|
or
|
||||||
|
$ configure --enable-debug
|
||||||
|
$ make CFLAGS='-g -pthread -O0' Define NO_STRING_INLINES
|
||||||
|
$ export CFLAGS="-DNO_STRING_INLINES -O2"
|
||||||
|
$ ./configure --enagble-debug
|
||||||
|
$ make
|
||||||
|
|
||||||
|
Fix
|
||||||
|
* Don't declare strndup in src/api/UpnpString.c if it exists
|
||||||
|
|
||||||
|
2015-02-01 Jean-Francois Dockes <medoc@users.sf.net>
|
||||||
|
|
||||||
|
Out-of-tree builds seem to be currently broken, because ixml and
|
||||||
|
threadutil files need an include path to include UpnpGlobal.h, and
|
||||||
|
configure tries to copy files into a directory which it does not create.
|
||||||
|
The patch fixes both issues.
|
||||||
|
|
||||||
|
2014-01-03 Peng <howtofly(at)gmail.com>
|
||||||
|
|
||||||
|
rewrite soap_device.c
|
||||||
|
|
||||||
|
1) separate HTTP handling from SOAP handling
|
||||||
|
2) remove repeated validity check, each check is performed exactly once
|
||||||
|
3) fix HTTP status code per UPnP spec, SOAP spec and RFC 2774
|
||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
Version 1.6.19
|
Version 1.6.19
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
|
2
Doxyfile
2
Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME = libUPnP
|
|||||||
# This could be handy for archiving the generated documentation or
|
# This could be handy for archiving the generated documentation or
|
||||||
# if some version control system is used.
|
# if some version control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 1.6.19
|
PROJECT_NUMBER = 1.6.20
|
||||||
|
|
||||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||||
# base path where the generated documentation will be put.
|
# base path where the generated documentation will be put.
|
||||||
|
4
THANKS
4
THANKS
@@ -23,6 +23,7 @@ exempt of errors.
|
|||||||
- Craig Nelson
|
- Craig Nelson
|
||||||
- David Blanchet
|
- David Blanchet
|
||||||
- David Maass
|
- David Maass
|
||||||
|
- Dirk (dirk_vdb)
|
||||||
- Emil Ljungdahl
|
- Emil Ljungdahl
|
||||||
- Erik Johansson
|
- Erik Johansson
|
||||||
- Eric Tanguy
|
- Eric Tanguy
|
||||||
@@ -37,6 +38,7 @@ exempt of errors.
|
|||||||
- Ingo Hofmann
|
- Ingo Hofmann
|
||||||
- Ivan Romanov (ivanromanov)
|
- Ivan Romanov (ivanromanov)
|
||||||
- Jiri Zouhar
|
- Jiri Zouhar
|
||||||
|
- Jean-Francois Dockes (medoc)
|
||||||
- John Dennis
|
- John Dennis
|
||||||
- Jonathan Casiot (no_dice)
|
- Jonathan Casiot (no_dice)
|
||||||
- Josh Carroll
|
- Josh Carroll
|
||||||
@@ -56,12 +58,14 @@ exempt of errors.
|
|||||||
- Paul Vixie
|
- Paul Vixie
|
||||||
- Peng
|
- Peng
|
||||||
- Peter Hartley
|
- Peter Hartley
|
||||||
|
- Philipp Matthias Hahn
|
||||||
- Pino Toscano (pinotree)
|
- Pino Toscano (pinotree)
|
||||||
- Rene Hexel
|
- Rene Hexel
|
||||||
- Robert Buckley (rbuckley)
|
- Robert Buckley (rbuckley)
|
||||||
- Robert Gingher (robsbox)
|
- Robert Gingher (robsbox)
|
||||||
- Ronan Menard
|
- Ronan Menard
|
||||||
- Sebastian Brandt
|
- Sebastian Brandt
|
||||||
|
- Shaun Marko (semarko)
|
||||||
- Siva Chandran
|
- Siva Chandran
|
||||||
- Stefan Sommerfeld (zerocom)
|
- Stefan Sommerfeld (zerocom)
|
||||||
- Stéphane Corthésy
|
- Stéphane Corthésy
|
||||||
|
@@ -105,7 +105,7 @@
|
|||||||
#define PACKAGE_NAME "libupnp"
|
#define PACKAGE_NAME "libupnp"
|
||||||
|
|
||||||
/* Define to the full name and version of this package. */
|
/* Define to the full name and version of this package. */
|
||||||
#define PACKAGE_STRING "libupnp 1.6.19"
|
#define PACKAGE_STRING "libupnp 1.6.20"
|
||||||
|
|
||||||
/* Define to the one symbol short name of this package. */
|
/* Define to the one symbol short name of this package. */
|
||||||
#define PACKAGE_TARNAME "libupnp"
|
#define PACKAGE_TARNAME "libupnp"
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
#define PACKAGE_URL ""
|
#define PACKAGE_URL ""
|
||||||
|
|
||||||
/* Define to the version of this package. */
|
/* Define to the version of this package. */
|
||||||
#define PACKAGE_VERSION "1.6.19"
|
#define PACKAGE_VERSION "1.6.20"
|
||||||
|
|
||||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||||
your system. */
|
your system. */
|
||||||
@@ -172,13 +172,13 @@
|
|||||||
#define UPNP_VERSION_MINOR 6
|
#define UPNP_VERSION_MINOR 6
|
||||||
|
|
||||||
/* see upnpconfig.h */
|
/* see upnpconfig.h */
|
||||||
#define UPNP_VERSION_PATCH 19
|
#define UPNP_VERSION_PATCH 20
|
||||||
|
|
||||||
/* see upnpconfig.h */
|
/* see upnpconfig.h */
|
||||||
#define UPNP_VERSION_STRING "1.6.19"
|
#define UPNP_VERSION_STRING "1.6.20"
|
||||||
|
|
||||||
/* Version number of package */
|
/* Version number of package */
|
||||||
#define VERSION "1.6.19"
|
#define VERSION "1.6.20"
|
||||||
|
|
||||||
/* File Offset size */
|
/* File Offset size */
|
||||||
#define _FILE_OFFSET_BITS 64
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
/** The library version (string) e.g. "1.3.0" */
|
/** The library version (string) e.g. "1.3.0" */
|
||||||
#define UPNP_VERSION_STRING "1.6.19"
|
#define UPNP_VERSION_STRING "1.6.20"
|
||||||
|
|
||||||
/** Major version of the library */
|
/** Major version of the library */
|
||||||
#define UPNP_VERSION_MAJOR 1
|
#define UPNP_VERSION_MAJOR 1
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
#define UPNP_VERSION_MINOR 6
|
#define UPNP_VERSION_MINOR 6
|
||||||
|
|
||||||
/** Patch version of the library */
|
/** Patch version of the library */
|
||||||
#define UPNP_VERSION_PATCH 19
|
#define UPNP_VERSION_PATCH 20
|
||||||
|
|
||||||
/** The library version (numeric) e.g. 10300 means version 1.3.0 */
|
/** The library version (numeric) e.g. 10300 means version 1.3.0 */
|
||||||
#define UPNP_VERSION \
|
#define UPNP_VERSION \
|
||||||
|
13
configure.ac
13
configure.ac
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
AC_PREREQ(2.60)
|
AC_PREREQ(2.60)
|
||||||
|
|
||||||
AC_INIT([libupnp], [1.6.19], [mroberto@users.sourceforge.net])
|
AC_INIT([libupnp], [1.6.20], [mroberto@users.sourceforge.net])
|
||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
dnl # *Independently* of the above libupnp package version, the libtool version
|
dnl # *Independently* of the above libupnp package version, the libtool version
|
||||||
dnl # of the 3 libraries need to be updated whenever there is a change released:
|
dnl # of the 3 libraries need to be updated whenever there is a change released:
|
||||||
@@ -341,6 +341,16 @@ dnl #AC_SUBST([LT_VERSION_THREADUTIL], [6:4:0])
|
|||||||
dnl #AC_SUBST([LT_VERSION_UPNP], [9:3:3])
|
dnl #AC_SUBST([LT_VERSION_UPNP], [9:3:3])
|
||||||
dnl #
|
dnl #
|
||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
|
dnl # Release 1.6.20:
|
||||||
|
dnl # "current:revision:age"
|
||||||
|
dnl #
|
||||||
|
dnl # -
|
||||||
|
dnl #
|
||||||
|
dnl #AC_SUBST([LT_VERSION_IXML], [::])
|
||||||
|
dnl #AC_SUBST([LT_VERSION_THREADUTIL], [::])
|
||||||
|
dnl #AC_SUBST([LT_VERSION_UPNP], [::])
|
||||||
|
dnl #
|
||||||
|
dnl ############################################################################
|
||||||
AC_SUBST([LT_VERSION_IXML], [2:8:0])
|
AC_SUBST([LT_VERSION_IXML], [2:8:0])
|
||||||
AC_SUBST([LT_VERSION_THREADUTIL], [6:4:0])
|
AC_SUBST([LT_VERSION_THREADUTIL], [6:4:0])
|
||||||
AC_SUBST([LT_VERSION_UPNP], [9:3:3])
|
AC_SUBST([LT_VERSION_UPNP], [9:3:3])
|
||||||
@@ -715,6 +725,7 @@ AC_OUTPUT
|
|||||||
# Files copied for windows compilation.
|
# Files copied for windows compilation.
|
||||||
#
|
#
|
||||||
echo "configure: copying \"autoconfig.h\" to \"build/inc/autoconfig.h\""
|
echo "configure: copying \"autoconfig.h\" to \"build/inc/autoconfig.h\""
|
||||||
|
test -d build/inc || mkdir -p build/inc
|
||||||
cp autoconfig.h build/inc/autoconfig.h
|
cp autoconfig.h build/inc/autoconfig.h
|
||||||
echo "configure: copying \"upnp/inc/upnpconfig.h\" to \"build/inc/upnpconfig.h\""
|
echo "configure: copying \"upnp/inc/upnpconfig.h\" to \"build/inc/upnpconfig.h\""
|
||||||
cp upnp/inc/upnpconfig.h build/inc/upnpconfig.h
|
cp upnp/inc/upnpconfig.h build/inc/upnpconfig.h
|
||||||
|
@@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
SUBDIRS = doc
|
SUBDIRS = doc
|
||||||
|
|
||||||
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc
|
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc \
|
||||||
|
-I$(top_srcdir)/upnp/inc
|
||||||
AM_CFLAGS =
|
AM_CFLAGS =
|
||||||
|
|
||||||
LDADD = libixml.la
|
LDADD = libixml.la
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
Version: 1.6.19
|
Version: 1.6.20
|
||||||
Summary: Universal Plug and Play (UPnP) SDK
|
Summary: Universal Plug and Play (UPnP) SDK
|
||||||
Name: libupnp
|
Name: libupnp
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
|
@@ -4,7 +4,8 @@
|
|||||||
# (C) Copyright 2005 Remi Turboult <r3mi@users.sourceforge.net>
|
# (C) Copyright 2005 Remi Turboult <r3mi@users.sourceforge.net>
|
||||||
#
|
#
|
||||||
|
|
||||||
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc
|
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc \
|
||||||
|
-I$(top_srcdir)/upnp/inc
|
||||||
|
|
||||||
if ENABLE_DEBUG
|
if ENABLE_DEBUG
|
||||||
AM_CPPFLAGS += -DDEBUG -DSTATS
|
AM_CPPFLAGS += -DDEBUG -DSTATS
|
||||||
|
@@ -154,9 +154,10 @@ libupnp_la_SOURCES += \
|
|||||||
|
|
||||||
|
|
||||||
# check / distcheck tests
|
# check / distcheck tests
|
||||||
check_PROGRAMS = test_init
|
check_PROGRAMS = test_init test_url
|
||||||
TESTS = test_init
|
TESTS = test_init test_url
|
||||||
test_init_SOURCES = test/test_init.c
|
test_init_SOURCES = test/test_init.c
|
||||||
|
test_url_SOURCES = test/test_url.c
|
||||||
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@@ -43,9 +43,7 @@
|
|||||||
#endif /* WIN32 */
|
#endif /* WIN32 */
|
||||||
|
|
||||||
/* strndup() is a GNU extension. */
|
/* strndup() is a GNU extension. */
|
||||||
#if HAVE_STRNDUP && !defined(WIN32)
|
#if !HAVE_STRNDUP || defined(WIN32)
|
||||||
extern char *strndup(__const char *__string, size_t __n);
|
|
||||||
#else /* HAVE_STRNDUP && !defined(WIN32) */
|
|
||||||
static char *strndup(const char *__string, size_t __n)
|
static char *strndup(const char *__string, size_t __n)
|
||||||
{
|
{
|
||||||
size_t strsize = strnlen(__string, __n);
|
size_t strsize = strnlen(__string, __n);
|
||||||
|
@@ -480,25 +480,20 @@ int genaInitNotify(
|
|||||||
}
|
}
|
||||||
*reference_count = 0;
|
*reference_count = 0;
|
||||||
|
|
||||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
UDN_copy = strdup(UDN);
|
||||||
if (UDN_copy == NULL) {
|
if (UDN_copy == NULL) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
servId_copy = strdup(servId);
|
||||||
if (servId_copy == NULL) {
|
if (servId_copy == NULL) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(UDN_copy, 0, strlen(UDN) + 1);
|
|
||||||
strncpy(UDN_copy, UDN, strlen(UDN));
|
|
||||||
memset(servId_copy, 0, strlen(servId) + 1);
|
|
||||||
strncpy(servId_copy, servId, strlen(servId));
|
|
||||||
|
|
||||||
HandleLock();
|
HandleLock();
|
||||||
|
|
||||||
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
|
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
|
||||||
@@ -639,25 +634,20 @@ int genaInitNotifyExt(
|
|||||||
}
|
}
|
||||||
*reference_count = 0;
|
*reference_count = 0;
|
||||||
|
|
||||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
UDN_copy = strdup(UDN);
|
||||||
if (UDN_copy == NULL) {
|
if (UDN_copy == NULL) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
servId_copy = strdup(servId);
|
||||||
if( servId_copy == NULL ) {
|
if( servId_copy == NULL ) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(UDN_copy, 0, strlen(UDN) + 1);
|
|
||||||
strncpy(UDN_copy, UDN, strlen(UDN));
|
|
||||||
memset(servId_copy, 0, strlen(servId) + 1);
|
|
||||||
strncpy(servId_copy, servId, strlen(servId));
|
|
||||||
|
|
||||||
HandleLock();
|
HandleLock();
|
||||||
|
|
||||||
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
|
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
|
||||||
@@ -798,25 +788,20 @@ int genaNotifyAllExt(
|
|||||||
}
|
}
|
||||||
*reference_count = 0;
|
*reference_count = 0;
|
||||||
|
|
||||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
UDN_copy = strdup(UDN);
|
||||||
if (UDN_copy == NULL) {
|
if (UDN_copy == NULL) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
servId_copy = strdup(servId);
|
||||||
if( servId_copy == NULL ) {
|
if( servId_copy == NULL ) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(UDN_copy, 0, strlen(UDN) + 1);
|
|
||||||
strncpy(UDN_copy, UDN, strlen(UDN));
|
|
||||||
memset(servId_copy, 0, strlen(servId) + 1);
|
|
||||||
strncpy(servId_copy, servId, strlen(servId));
|
|
||||||
|
|
||||||
propertySet = ixmlPrintNode((IXML_Node *)PropSet);
|
propertySet = ixmlPrintNode((IXML_Node *)PropSet);
|
||||||
if (propertySet == NULL) {
|
if (propertySet == NULL) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
@@ -944,25 +929,20 @@ int genaNotifyAll(
|
|||||||
}
|
}
|
||||||
*reference_count = 0;
|
*reference_count = 0;
|
||||||
|
|
||||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
UDN_copy = strdup(UDN);
|
||||||
if (UDN_copy == NULL) {
|
if (UDN_copy == NULL) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
servId_copy = strdup(servId);
|
||||||
if( servId_copy == NULL ) {
|
if( servId_copy == NULL ) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
ret = UPNP_E_OUTOF_MEMORY;
|
ret = UPNP_E_OUTOF_MEMORY;
|
||||||
goto ExitFunction;
|
goto ExitFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(UDN_copy, 0, strlen(UDN) + 1);
|
|
||||||
strncpy(UDN_copy, UDN, strlen(UDN));
|
|
||||||
memset(servId_copy, 0, strlen(servId) + 1);
|
|
||||||
strncpy(servId_copy, servId, strlen(servId));
|
|
||||||
|
|
||||||
ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
|
ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
|
||||||
if (ret != XML_SUCCESS) {
|
if (ret != XML_SUCCESS) {
|
||||||
line = __LINE__;
|
line = __LINE__;
|
||||||
|
@@ -481,13 +481,12 @@ int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
|
|||||||
memset(Chunk_Header, 0,
|
memset(Chunk_Header, 0,
|
||||||
sizeof(Chunk_Header));
|
sizeof(Chunk_Header));
|
||||||
rc = snprintf(Chunk_Header,
|
rc = snprintf(Chunk_Header,
|
||||||
sizeof(Chunk_Header) - strlen ("\r\n"),
|
sizeof(Chunk_Header),
|
||||||
"%" PRIzx, num_read);
|
"%" PRIzx "\r\n", num_read);
|
||||||
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header) - strlen ("\r\n")) {
|
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header)) {
|
||||||
RetVal = UPNP_E_INTERNAL_ERROR;
|
RetVal = UPNP_E_INTERNAL_ERROR;
|
||||||
goto Cleanup_File;
|
goto Cleanup_File;
|
||||||
}
|
}
|
||||||
strncat(Chunk_Header, "\r\n", strlen ("\r\n"));
|
|
||||||
/* Copy the chunk size header */
|
/* Copy the chunk size header */
|
||||||
memcpy(file_buf - strlen(Chunk_Header),
|
memcpy(file_buf - strlen(Chunk_Header),
|
||||||
Chunk_Header,
|
Chunk_Header,
|
||||||
|
@@ -88,14 +88,19 @@ static const char *Http4xxStr =
|
|||||||
"Unsupported Media Type\0"
|
"Unsupported Media Type\0"
|
||||||
"Requested Range Not Satisfiable\0" "Expectation Failed\0";
|
"Requested Range Not Satisfiable\0" "Expectation Failed\0";
|
||||||
|
|
||||||
#define NUM_5XX_CODES 6
|
#define NUM_5XX_CODES 11
|
||||||
static const char *Http5xxCodes[NUM_5XX_CODES];
|
static const char *Http5xxCodes[NUM_5XX_CODES];
|
||||||
static const char *Http5xxStr =
|
static const char *Http5xxStr =
|
||||||
"Internal Server Error\0"
|
"Internal Server Error\0"
|
||||||
"Not Implemented\0"
|
"Not Implemented\0"
|
||||||
"Bad Gateway\0"
|
"Bad Gateway\0"
|
||||||
"Service Unavailable\0"
|
"Service Unavailable\0"
|
||||||
"Gateway Timeout\0" "HTTP Version Not Supported\0";
|
"Gateway Timeout\0"
|
||||||
|
"HTTP Version Not Supported\0"
|
||||||
|
"Variant Also Negotiates\0"
|
||||||
|
"Insufficient Storage\0"
|
||||||
|
"Loop Detected\0"
|
||||||
|
"\0" "Not Extended\0";
|
||||||
|
|
||||||
static int gInitialized = FALSE;
|
static int gInitialized = FALSE;
|
||||||
|
|
||||||
|
@@ -129,7 +129,7 @@ static const char *gMediaTypes[] = {
|
|||||||
#define TEXT_INDEX 5
|
#define TEXT_INDEX 5
|
||||||
|
|
||||||
/* general */
|
/* general */
|
||||||
#define NUM_MEDIA_TYPES 69
|
#define NUM_MEDIA_TYPES 70
|
||||||
#define NUM_HTTP_HEADER_NAMES 33
|
#define NUM_HTTP_HEADER_NAMES 33
|
||||||
|
|
||||||
#define ASCTIME_R_BUFFER_SIZE 26
|
#define ASCTIME_R_BUFFER_SIZE 26
|
||||||
@@ -156,6 +156,7 @@ static const char *gEncodedMediaTypes =
|
|||||||
"au\0" AUDIO_STR "basic\0"
|
"au\0" AUDIO_STR "basic\0"
|
||||||
"avi\0" VIDEO_STR "msvideo\0"
|
"avi\0" VIDEO_STR "msvideo\0"
|
||||||
"bmp\0" IMAGE_STR "bmp\0"
|
"bmp\0" IMAGE_STR "bmp\0"
|
||||||
|
"css\0" TEXT_STR "css\0"
|
||||||
"dcr\0" APPLICATION_STR "x-director\0"
|
"dcr\0" APPLICATION_STR "x-director\0"
|
||||||
"dib\0" IMAGE_STR "bmp\0"
|
"dib\0" IMAGE_STR "bmp\0"
|
||||||
"dir\0" APPLICATION_STR "x-director\0"
|
"dir\0" APPLICATION_STR "x-director\0"
|
||||||
@@ -792,11 +793,9 @@ static int CreateHTTPRangeResponseHeader(
|
|||||||
Instr->ReadSendSize = FileLength;
|
Instr->ReadSendSize = FileLength;
|
||||||
if (!ByteRangeSpecifier)
|
if (!ByteRangeSpecifier)
|
||||||
return HTTP_BAD_REQUEST;
|
return HTTP_BAD_REQUEST;
|
||||||
RangeInput = malloc(strlen(ByteRangeSpecifier) + 1);
|
RangeInput = strdup(ByteRangeSpecifier);
|
||||||
if (!RangeInput)
|
if (!RangeInput)
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
memset(RangeInput, 0, strlen(ByteRangeSpecifier) + 1);
|
|
||||||
strncpy(RangeInput, ByteRangeSpecifier, strlen(ByteRangeSpecifier));
|
|
||||||
/* CONTENT-RANGE: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
|
/* CONTENT-RANGE: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
|
||||||
if (StrStr(RangeInput, "bytes") == NULL ||
|
if (StrStr(RangeInput, "bytes") == NULL ||
|
||||||
(Ptr = StrStr(RangeInput, "=")) == NULL) {
|
(Ptr = StrStr(RangeInput, "=")) == NULL) {
|
||||||
|
@@ -387,7 +387,7 @@ static int parse_hostport(
|
|||||||
|
|
||||||
ret = getaddrinfo(srvname, NULL, &hints, &res0);
|
ret = getaddrinfo(srvname, NULL, &hints, &res0);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
for (res = res0; res && !ret; res = res->ai_next) {
|
for (res = res0; res; res = res->ai_next) {
|
||||||
switch (res->ai_family) {
|
switch (res->ai_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
@@ -395,12 +395,10 @@ static int parse_hostport(
|
|||||||
memcpy(&out->IPaddress,
|
memcpy(&out->IPaddress,
|
||||||
res->ai_addr,
|
res->ai_addr,
|
||||||
res->ai_addrlen);
|
res->ai_addrlen);
|
||||||
ret=1;
|
goto found;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
found:
|
||||||
freeaddrinfo(res0);
|
freeaddrinfo(res0);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
/* Didn't find an AF_INET or AF_INET6 address. */
|
/* Didn't find an AF_INET or AF_INET6 address. */
|
||||||
@@ -512,68 +510,81 @@ int remove_escaped_chars(INOUT char *in, INOUT size_t *size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int remove_dots(char *in, size_t size)
|
static UPNP_INLINE int is_end_path(char c) {
|
||||||
|
switch (c) {
|
||||||
|
case '?':
|
||||||
|
case '#':
|
||||||
|
case '\0':
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This function directly implements the "Remove Dot Segments"
|
||||||
|
* algorithm described in RFC 3986 section 5.2.4. */
|
||||||
|
int remove_dots(char *buf, size_t size)
|
||||||
{
|
{
|
||||||
char *copyTo = in;
|
char *in = buf;
|
||||||
char *copyFrom = in;
|
char *out = buf;
|
||||||
char *max = in + size;
|
char *max = buf + size;
|
||||||
char **Segments = NULL;
|
|
||||||
int lastSegment = -1;
|
|
||||||
|
|
||||||
Segments = malloc( sizeof( char * ) * size );
|
while (!is_end_path(in[0])) {
|
||||||
|
assert (buf <= out);
|
||||||
|
assert (out <= in);
|
||||||
|
assert (in < max);
|
||||||
|
|
||||||
if( Segments == NULL )
|
/* case 2.A: */
|
||||||
return UPNP_E_OUTOF_MEMORY;
|
if (strncmp(in, "./", 2) == 0) {
|
||||||
|
in += 2;
|
||||||
Segments[0] = NULL;
|
} else if (strncmp(in, "../", 3) == 0) {
|
||||||
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
in += 3;
|
||||||
"REMOVE_DOTS: before: %s\n", in );
|
/* case 2.B: */
|
||||||
while( ( copyFrom < max ) && ( *copyFrom != '?' )
|
} else if (strncmp(in, "/./", 3) == 0) {
|
||||||
&& ( *copyFrom != '#' ) ) {
|
in += 2;
|
||||||
|
} else if (strncmp(in, "/.", 2) == 0 && is_end_path(in[2])) {
|
||||||
if( ( ( *copyFrom ) == '.' )
|
in += 1;
|
||||||
&& ( ( copyFrom == in ) || ( *( copyFrom - 1 ) == '/' ) ) ) {
|
in[0] = '/';
|
||||||
if( ( copyFrom + 1 == max )
|
/* case 2.C: */
|
||||||
|| ( *( copyFrom + 1 ) == '/' ) ) {
|
} else if (strncmp(in, "/../", 4) == 0 || (strncmp(in, "/..", 3) == 0 && is_end_path(in[3]))) {
|
||||||
|
/* Make the next character in the input buffer a '/': */
|
||||||
copyFrom += 2;
|
if (is_end_path(in[3])) { /* terminating "/.." case */
|
||||||
continue;
|
in += 2;
|
||||||
} else if( ( *( copyFrom + 1 ) == '.' )
|
in[0] = '/';
|
||||||
&& ( ( copyFrom + 2 == max )
|
} else { /* "/../" prefix case */
|
||||||
|| ( *( copyFrom + 2 ) == '/' ) ) ) {
|
in += 3;
|
||||||
copyFrom += 3;
|
|
||||||
|
|
||||||
if( lastSegment > 0 ) {
|
|
||||||
copyTo = Segments[--lastSegment];
|
|
||||||
} else {
|
|
||||||
free( Segments );
|
|
||||||
/*TRACE("ERROR RESOLVING URL, ../ at ROOT"); */
|
|
||||||
return UPNP_E_INVALID_URL;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
/* Trim the last component from the output buffer, or empty it. */
|
||||||
|
while (buf < out)
|
||||||
if( ( *copyFrom ) == '/' ) {
|
if (*--out == '/')
|
||||||
|
break;
|
||||||
lastSegment++;
|
#ifdef DEBUG
|
||||||
Segments[lastSegment] = copyTo + 1;
|
if (out < in)
|
||||||
}
|
out[0] = '\0';
|
||||||
( *copyTo ) = ( *copyFrom );
|
#endif
|
||||||
copyTo++;
|
/* case 2.D: */
|
||||||
copyFrom++;
|
} else if (strncmp(in, ".", 1) == 0 && is_end_path(in[1])) {
|
||||||
}
|
in += 1;
|
||||||
if( copyFrom < max ) {
|
} else if (strncmp(in, "..", 2) == 0 && is_end_path(in[2])) {
|
||||||
while( copyFrom < max ) {
|
in += 2;
|
||||||
( *copyTo ) = ( *copyFrom );
|
/* case 2.E */
|
||||||
copyTo++;
|
} else {
|
||||||
copyFrom++;
|
/* move initial '/' character (if any) */
|
||||||
|
if (in[0] == '/')
|
||||||
|
*out++ = *in++;
|
||||||
|
/* move first segment up to, but not including, the next '/' character */
|
||||||
|
while (in < max && in[0] != '/' && !is_end_path(in[0]))
|
||||||
|
*out++ = *in++;
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (out < in)
|
||||||
|
out[0] = '\0';
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
( *copyTo ) = 0;
|
while (in < max)
|
||||||
free( Segments );
|
*out++ = *in++;
|
||||||
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
if (out < max)
|
||||||
"REMOVE_DOTS: after: %s\n", in );
|
out[0] = '\0';
|
||||||
return UPNP_E_SUCCESS;
|
return UPNP_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,115 +593,113 @@ char *resolve_rel_url(char *base_url, char *rel_url)
|
|||||||
{
|
{
|
||||||
uri_type base;
|
uri_type base;
|
||||||
uri_type rel;
|
uri_type rel;
|
||||||
|
int rv;
|
||||||
|
|
||||||
size_t i = (size_t)0;
|
if (!base_url) {
|
||||||
char *finger = NULL;
|
if (!rel_url)
|
||||||
|
|
||||||
char *last_slash = NULL;
|
|
||||||
|
|
||||||
char *out = NULL;
|
|
||||||
|
|
||||||
if( base_url && rel_url ) {
|
|
||||||
out =
|
|
||||||
( char * )malloc( strlen( base_url ) + strlen( rel_url ) + (size_t)2 );
|
|
||||||
} else {
|
|
||||||
if( rel_url )
|
|
||||||
return strdup( rel_url );
|
|
||||||
else
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
return strdup(rel_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( out == NULL ) {
|
size_t len_rel = strlen(rel_url);
|
||||||
|
if (parse_uri(rel_url, len_rel, &rel) != HTTP_SUCCESS)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (rel.type == (enum uriType)ABSOLUTE)
|
||||||
|
return strdup(rel_url);
|
||||||
|
|
||||||
|
size_t len_base = strlen(base_url);
|
||||||
|
if ((parse_uri(base_url, len_base, &base) != HTTP_SUCCESS)
|
||||||
|
|| (base.type != (enum uriType)ABSOLUTE))
|
||||||
|
return NULL;
|
||||||
|
if (len_rel == (size_t)0)
|
||||||
|
return strdup(base_url);
|
||||||
|
|
||||||
|
size_t len = len_base + len_rel + (size_t)2;
|
||||||
|
char *out = (char *)malloc(len);
|
||||||
|
if (out == NULL)
|
||||||
|
return NULL;
|
||||||
|
memset(out, 0, len);
|
||||||
|
char *out_finger = out;
|
||||||
|
|
||||||
|
/* scheme */
|
||||||
|
rv = snprintf(out_finger, len, "%.*s:", (int)base.scheme.size, base.scheme.buff);
|
||||||
|
if (rv < 0 || rv >= len)
|
||||||
|
goto error;
|
||||||
|
out_finger += rv;
|
||||||
|
len -= rv;
|
||||||
|
|
||||||
|
/* authority */
|
||||||
|
if (rel.hostport.text.size > (size_t)0) {
|
||||||
|
rv = snprintf(out_finger, len, "%s", rel_url);
|
||||||
|
if (rv < 0 || rv >= len)
|
||||||
|
goto error;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
if (base.hostport.text.size > (size_t)0) {
|
||||||
|
rv = snprintf(out_finger, len, "//%.*s", (int)base.hostport.text.size, base.hostport.text.buff);
|
||||||
|
if (rv < 0 || rv >= len)
|
||||||
|
goto error;
|
||||||
|
out_finger += rv;
|
||||||
|
len -= rv;
|
||||||
}
|
}
|
||||||
memset( out, 0, strlen( base_url ) + strlen( rel_url ) + (size_t)2 );
|
|
||||||
|
|
||||||
if( ( parse_uri( rel_url, strlen( rel_url ), &rel ) ) == HTTP_SUCCESS ) {
|
/* path */
|
||||||
|
char *path = out_finger;
|
||||||
if( rel.type == ( enum uriType) ABSOLUTE ) {
|
if (rel.path_type == (enum pathType)ABS_PATH) {
|
||||||
|
rv = snprintf(out_finger, len, "%s", rel_url);
|
||||||
strncpy( out, rel_url, strlen ( rel_url ) );
|
} else if (base.pathquery.size == (size_t)0) {
|
||||||
} else {
|
rv = snprintf(out_finger, len, "/%s", rel_url);
|
||||||
|
|
||||||
if( ( parse_uri( base_url, strlen( base_url ), &base ) ==
|
|
||||||
HTTP_SUCCESS )
|
|
||||||
&& ( base.type == ( enum uriType ) ABSOLUTE ) ) {
|
|
||||||
|
|
||||||
if( strlen( rel_url ) == (size_t)0 ) {
|
|
||||||
strncpy( out, base_url, strlen ( base_url ) );
|
|
||||||
} else {
|
|
||||||
char *out_finger = out;
|
|
||||||
assert( base.scheme.size + (size_t)1 /* ':' */ <= strlen ( base_url ) );
|
|
||||||
memcpy( out, base.scheme.buff, base.scheme.size );
|
|
||||||
out_finger += base.scheme.size;
|
|
||||||
( *out_finger ) = ':';
|
|
||||||
out_finger++;
|
|
||||||
|
|
||||||
if( rel.hostport.text.size > (size_t)0 ) {
|
|
||||||
snprintf( out_finger, strlen( rel_url ) + (size_t)1,
|
|
||||||
"%s", rel_url );
|
|
||||||
} else {
|
|
||||||
if( base.hostport.text.size > (size_t)0 ) {
|
|
||||||
assert( base.scheme.size + (size_t)1
|
|
||||||
+ base.hostport.text.size + (size_t)2 /* "//" */ <= strlen ( base_url ) );
|
|
||||||
memcpy( out_finger, "//", (size_t)2 );
|
|
||||||
out_finger += 2;
|
|
||||||
memcpy( out_finger, base.hostport.text.buff,
|
|
||||||
base.hostport.text.size );
|
|
||||||
out_finger += base.hostport.text.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( rel.path_type == ( enum pathType ) ABS_PATH ) {
|
|
||||||
strncpy( out_finger, rel_url, strlen ( rel_url ) );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
char temp_path = '/';
|
|
||||||
|
|
||||||
if( base.pathquery.size == (size_t)0 ) {
|
|
||||||
base.pathquery.size = (size_t)1;
|
|
||||||
base.pathquery.buff = &temp_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert( base.scheme.size + (size_t)1 + base.hostport.text.size + (size_t)2
|
|
||||||
+ base.pathquery.size <= strlen ( base_url ) + (size_t)1 /* temp_path */);
|
|
||||||
finger = out_finger;
|
|
||||||
last_slash = finger;
|
|
||||||
i = (size_t)0;
|
|
||||||
while( ( i < base.pathquery.size ) &&
|
|
||||||
( base.pathquery.buff[i] != '?' ) ) {
|
|
||||||
( *finger ) = base.pathquery.buff[i];
|
|
||||||
if( base.pathquery.buff[i] == '/' )
|
|
||||||
last_slash = finger + 1;
|
|
||||||
i++;
|
|
||||||
finger++;
|
|
||||||
|
|
||||||
}
|
|
||||||
strncpy( last_slash, rel_url, strlen ( rel_url ) );
|
|
||||||
if( remove_dots( out_finger,
|
|
||||||
strlen( out_finger ) ) !=
|
|
||||||
UPNP_E_SUCCESS ) {
|
|
||||||
free(out);
|
|
||||||
/* free(rel_url); */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
free(out);
|
|
||||||
/* free(rel_url); */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
free(out);
|
if (rel.pathquery.size == (size_t)0) {
|
||||||
/* free(rel_url); */
|
rv = snprintf(out_finger, len, "%.*s", (int)base.pathquery.size, base.pathquery.buff);
|
||||||
return NULL;
|
} else {
|
||||||
}
|
if (len < base.pathquery.size)
|
||||||
|
goto error;
|
||||||
|
size_t i = (size_t)0, prefix = (size_t)1;
|
||||||
|
while (i < base.pathquery.size) {
|
||||||
|
out_finger[i] = base.pathquery.buff[i];
|
||||||
|
switch (base.pathquery.buff[i++]) {
|
||||||
|
case '/':
|
||||||
|
prefix = i;
|
||||||
|
/* fall-through */
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
case '?': /* query */
|
||||||
|
if (rel.pathquery.buff[0] == '?')
|
||||||
|
prefix = --i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out_finger += prefix;
|
||||||
|
len -= prefix;
|
||||||
|
rv = snprintf(out_finger, len, "%.*s", (int)rel.pathquery.size, rel.pathquery.buff);
|
||||||
|
}
|
||||||
|
if (rv < 0 || rv >= len)
|
||||||
|
goto error;
|
||||||
|
out_finger += rv;
|
||||||
|
len -= rv;
|
||||||
|
|
||||||
|
/* fragment */
|
||||||
|
if (rel.fragment.size > (size_t)0)
|
||||||
|
rv = snprintf(out_finger, len, "#%.*s", (int)rel.fragment.size, rel.fragment.buff);
|
||||||
|
else if (base.fragment.size > (size_t)0)
|
||||||
|
rv = snprintf(out_finger, len, "#%.*s", (int)base.fragment.size, base.fragment.buff);
|
||||||
|
else
|
||||||
|
rv = 0;
|
||||||
|
}
|
||||||
|
if (rv < 0 || rv >= len)
|
||||||
|
goto error;
|
||||||
|
out_finger += rv;
|
||||||
|
len -= rv;
|
||||||
|
|
||||||
|
if (remove_dots(path, out_finger - path) != UPNP_E_SUCCESS)
|
||||||
|
goto error;
|
||||||
|
|
||||||
/* free(rel_url); */
|
|
||||||
return out;
|
return out;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(out);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -79,6 +79,10 @@
|
|||||||
#define HTTP_SERVICE_UNAVAILABLE 503
|
#define HTTP_SERVICE_UNAVAILABLE 503
|
||||||
#define HTTP_GATEWAY_TIMEOUT 504
|
#define HTTP_GATEWAY_TIMEOUT 504
|
||||||
#define HTTP_HTTP_VERSION_NOT_SUPPORTED 505
|
#define HTTP_HTTP_VERSION_NOT_SUPPORTED 505
|
||||||
|
#define HTTP_VARIANT_ALSO_NEGOTIATES 506
|
||||||
|
#define HTTP_INSUFFICIENT_STORAGE 507
|
||||||
|
#define HTTP_LOOP_DETECTED 508
|
||||||
|
#define HTTP_NOT_EXTENDED 510
|
||||||
|
|
||||||
/* HTTP lib error codes */
|
/* HTTP lib error codes */
|
||||||
|
|
||||||
|
@@ -48,6 +48,9 @@
|
|||||||
#include "unixutil.h"
|
#include "unixutil.h"
|
||||||
#include "upnpapi.h"
|
#include "upnpapi.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#endif
|
#endif
|
||||||
@@ -57,12 +60,15 @@
|
|||||||
|
|
||||||
#define SREQ_HDR_NOT_FOUND -1
|
#define SREQ_HDR_NOT_FOUND -1
|
||||||
#define SREQ_BAD_HDR_FORMAT -2
|
#define SREQ_BAD_HDR_FORMAT -2
|
||||||
|
#define SREQ_NOT_EXTENDED -3
|
||||||
|
|
||||||
#define SOAP_INVALID_ACTION 401
|
#define SOAP_INVALID_ACTION 401
|
||||||
#define SOAP_INVALID_ARGS 402
|
#define SOAP_INVALID_ARGS 402
|
||||||
#define SOAP_OUT_OF_SYNC 403
|
#define SOAP_OUT_OF_SYNC 403
|
||||||
#define SOAP_INVALID_VAR 404
|
#define SOAP_INVALID_VAR 404
|
||||||
#define SOAP_ACTION_FAILED 501
|
#define SOAP_ACTION_FAILED 501
|
||||||
|
#define SOAP_MEMORY_OUT 603
|
||||||
|
|
||||||
|
|
||||||
static const char *SOAP_BODY = "Body";
|
static const char *SOAP_BODY = "Body";
|
||||||
static const char *SOAP_URN = "http:/""/schemas.xmlsoap.org/soap/envelope/";
|
static const char *SOAP_URN = "http:/""/schemas.xmlsoap.org/soap/envelope/";
|
||||||
@@ -72,81 +78,17 @@ static const char *Soap_Invalid_Action = "Invalid Action";
|
|||||||
/*static const char* Soap_Invalid_Args = "Invalid Args"; */
|
/*static const char* Soap_Invalid_Args = "Invalid Args"; */
|
||||||
static const char *Soap_Action_Failed = "Action Failed";
|
static const char *Soap_Action_Failed = "Action Failed";
|
||||||
static const char *Soap_Invalid_Var = "Invalid Var";
|
static const char *Soap_Invalid_Var = "Invalid Var";
|
||||||
|
static const char *Soap_Memory_out = "Out of Memory";
|
||||||
|
|
||||||
/*!
|
typedef struct soap_devserv_t {
|
||||||
* \brief This function retrives the name of the SOAP action.
|
char dev_udn[NAME_SIZE];
|
||||||
*
|
char service_type[NAME_SIZE];
|
||||||
* \return 0 if successful else returns appropriate error.
|
char service_id[NAME_SIZE];
|
||||||
*/
|
memptr action_name;
|
||||||
static UPNP_INLINE int get_request_type(
|
Upnp_FunPtr callback;
|
||||||
/*! [in] HTTP request. */
|
void *cookie;
|
||||||
http_message_t *request,
|
}soap_devserv_t;
|
||||||
/*! [out] SOAP action name. */
|
|
||||||
memptr *action_name)
|
|
||||||
{
|
|
||||||
memptr value;
|
|
||||||
memptr ns_value, dummy_quote;
|
|
||||||
http_header_t *hdr;
|
|
||||||
char save_char;
|
|
||||||
char *s;
|
|
||||||
membuffer soap_action_name;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
/* find soapaction header */
|
|
||||||
if (request->method == SOAPMETHOD_POST) {
|
|
||||||
if (!httpmsg_find_hdr(request, HDR_SOAPACTION, &value))
|
|
||||||
return SREQ_HDR_NOT_FOUND;
|
|
||||||
} else {
|
|
||||||
/* M-POST */
|
|
||||||
/* get NS value from MAN header */
|
|
||||||
hdr = httpmsg_find_hdr(request, HDR_MAN, &value);
|
|
||||||
if (hdr == NULL)
|
|
||||||
return SREQ_HDR_NOT_FOUND;
|
|
||||||
if (matchstr(value.buf, value.length, "%q%i ; ns = %s",
|
|
||||||
&dummy_quote, &ns_value) != 0)
|
|
||||||
return SREQ_BAD_HDR_FORMAT;
|
|
||||||
/* create soapaction name header */
|
|
||||||
membuffer_init(&soap_action_name);
|
|
||||||
if (membuffer_assign(&soap_action_name,
|
|
||||||
ns_value.buf, ns_value.length) == UPNP_E_OUTOF_MEMORY ||
|
|
||||||
membuffer_append_str(&soap_action_name,
|
|
||||||
"-SOAPACTION") == UPNP_E_OUTOF_MEMORY) {
|
|
||||||
membuffer_destroy(&soap_action_name);
|
|
||||||
return UPNP_E_OUTOF_MEMORY;
|
|
||||||
}
|
|
||||||
hdr = httpmsg_find_hdr_str(request, soap_action_name.buf);
|
|
||||||
membuffer_destroy(&soap_action_name);
|
|
||||||
if (!hdr)
|
|
||||||
return SREQ_HDR_NOT_FOUND;
|
|
||||||
value.buf = hdr->value.buf;
|
|
||||||
value.length = hdr->value.length;
|
|
||||||
}
|
|
||||||
/* determine type */
|
|
||||||
save_char = value.buf[value.length];
|
|
||||||
value.buf[value.length] = '\0';
|
|
||||||
s = strchr(value.buf, '#');
|
|
||||||
if (s == NULL) {
|
|
||||||
value.buf[value.length] = save_char;
|
|
||||||
return SREQ_BAD_HDR_FORMAT;
|
|
||||||
}
|
|
||||||
/* move to value */
|
|
||||||
s++;
|
|
||||||
n = value.length - (size_t)(s - value.buf);
|
|
||||||
if (matchstr(s, n, "%s", action_name) != PARSE_OK) {
|
|
||||||
value.buf[value.length] = save_char;
|
|
||||||
return SREQ_BAD_HDR_FORMAT;
|
|
||||||
}
|
|
||||||
/* action name or variable ? */
|
|
||||||
if (memptr_cmp(action_name, "QueryStateVariable") == 0) {
|
|
||||||
/* query variable */
|
|
||||||
action_name->buf = NULL;
|
|
||||||
action_name->length = 0;
|
|
||||||
}
|
|
||||||
/* restore */
|
|
||||||
value.buf[value.length] = save_char;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Sends SOAP error response.
|
* \brief Sends SOAP error response.
|
||||||
@@ -270,288 +212,6 @@ static UPNP_INLINE void send_var_query_response(
|
|||||||
membuffer_destroy(&response);
|
membuffer_destroy(&response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Separates the action node from the root DOM node.
|
|
||||||
*
|
|
||||||
* \return 0 if successful, or -1 if fails.
|
|
||||||
*/
|
|
||||||
static UPNP_INLINE int get_action_node(
|
|
||||||
/*! [in] The root DOM node. */
|
|
||||||
IXML_Document *TempDoc,
|
|
||||||
/*! [in] IXML_Node name to be searched. */
|
|
||||||
char *NodeName,
|
|
||||||
/*! [out] Response/Output node. */
|
|
||||||
IXML_Document **RespNode)
|
|
||||||
{
|
|
||||||
IXML_Node *EnvpNode = NULL;
|
|
||||||
IXML_Node *BodyNode = NULL;
|
|
||||||
IXML_Node *ActNode = NULL;
|
|
||||||
DOMString ActNodeName = NULL;
|
|
||||||
const DOMString nodeName;
|
|
||||||
int ret_code = -1; /* error, by default */
|
|
||||||
IXML_NodeList *nl = NULL;
|
|
||||||
|
|
||||||
UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__,
|
|
||||||
"get_action_node(): node name =%s\n ", NodeName);
|
|
||||||
*RespNode = NULL;
|
|
||||||
/* Got the Envelope node here */
|
|
||||||
EnvpNode = ixmlNode_getFirstChild((IXML_Node *) TempDoc);
|
|
||||||
if (!EnvpNode)
|
|
||||||
goto error_handler;
|
|
||||||
nl = ixmlElement_getElementsByTagNameNS((IXML_Element *)EnvpNode,
|
|
||||||
"*", "Body");
|
|
||||||
if (!nl)
|
|
||||||
goto error_handler;
|
|
||||||
BodyNode = ixmlNodeList_item(nl, 0);
|
|
||||||
if (!BodyNode)
|
|
||||||
goto error_handler;
|
|
||||||
/* Got action node here */
|
|
||||||
ActNode = ixmlNode_getFirstChild(BodyNode);
|
|
||||||
if (!ActNode)
|
|
||||||
goto error_handler;
|
|
||||||
/* Test whether this is the action node */
|
|
||||||
nodeName = ixmlNode_getNodeName(ActNode);
|
|
||||||
if (!nodeName)
|
|
||||||
goto error_handler;
|
|
||||||
if (!strstr(nodeName, NodeName))
|
|
||||||
goto error_handler;
|
|
||||||
else {
|
|
||||||
ActNodeName = ixmlPrintNode(ActNode);
|
|
||||||
if (!ActNodeName)
|
|
||||||
goto error_handler;
|
|
||||||
ret_code = ixmlParseBufferEx(ActNodeName, RespNode);
|
|
||||||
if (ret_code != IXML_SUCCESS) {
|
|
||||||
ret_code = -1;
|
|
||||||
goto error_handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* success */
|
|
||||||
ret_code = 0;
|
|
||||||
|
|
||||||
error_handler:
|
|
||||||
ixmlFreeDOMString(ActNodeName);
|
|
||||||
if (nl)
|
|
||||||
ixmlNodeList_free(nl);
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Checks the soap body xml came in the SOAP request.
|
|
||||||
*
|
|
||||||
* \return UPNP_E_SUCCESS if successful else returns appropriate error.
|
|
||||||
*/
|
|
||||||
static int check_soap_body(
|
|
||||||
/* [in] soap body xml document. */
|
|
||||||
IN IXML_Document *doc,
|
|
||||||
/* [in] URN. */
|
|
||||||
IN const char *urn,
|
|
||||||
/* [in] Name of the requested action. */
|
|
||||||
IN const char *actionName)
|
|
||||||
{
|
|
||||||
IXML_NodeList *nl = NULL;
|
|
||||||
IXML_Node *bodyNode = NULL;
|
|
||||||
IXML_Node *actionNode = NULL;
|
|
||||||
const DOMString ns = NULL;
|
|
||||||
const DOMString name = NULL;
|
|
||||||
int ret_code = UPNP_E_INVALID_ACTION;
|
|
||||||
|
|
||||||
nl = ixmlDocument_getElementsByTagNameNS(doc, SOAP_URN, SOAP_BODY);
|
|
||||||
if (nl) {
|
|
||||||
bodyNode = ixmlNodeList_item(nl, 0);
|
|
||||||
if (bodyNode) {
|
|
||||||
actionNode = ixmlNode_getFirstChild(bodyNode);
|
|
||||||
if (actionNode) {
|
|
||||||
ns = ixmlNode_getNamespaceURI(actionNode);
|
|
||||||
name = ixmlNode_getLocalName(actionNode);
|
|
||||||
/* Don't check version number, to accept a
|
|
||||||
* request comming on a v1 service when
|
|
||||||
* publishing a v2 service */
|
|
||||||
if (name &&
|
|
||||||
ns &&
|
|
||||||
!strcmp(actionName, name) &&
|
|
||||||
!strncmp(urn, ns, strlen (urn) - 2))
|
|
||||||
ret_code = UPNP_E_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ixmlNodeList_free(nl);
|
|
||||||
}
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Checks the HTTP header of the SOAP request coming from the
|
|
||||||
* control point.
|
|
||||||
*
|
|
||||||
* \return UPNP_E_SUCCESS if successful else returns appropriate error.
|
|
||||||
*/
|
|
||||||
static int check_soap_action_header(
|
|
||||||
/*! [in] HTTP request. */
|
|
||||||
http_message_t * request,
|
|
||||||
/*! [in] URN. */
|
|
||||||
const char *urn,
|
|
||||||
/*! [out] Name of the SOAP action. */
|
|
||||||
char **actionName)
|
|
||||||
{
|
|
||||||
memptr header_name;
|
|
||||||
http_header_t *soap_action_header = NULL;
|
|
||||||
char *ns_compare = NULL;
|
|
||||||
size_t tempSize = 0;
|
|
||||||
int ret_code = UPNP_E_SUCCESS;
|
|
||||||
char *temp_header_value = NULL;
|
|
||||||
char *temp = NULL;
|
|
||||||
char *temp2 = NULL;
|
|
||||||
|
|
||||||
/* check soap action header */
|
|
||||||
soap_action_header = httpmsg_find_hdr(request, HDR_SOAPACTION,
|
|
||||||
&header_name);
|
|
||||||
if (!soap_action_header) {
|
|
||||||
ret_code = UPNP_E_INVALID_ACTION;
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
if (soap_action_header->value.length <= 0) {
|
|
||||||
ret_code = UPNP_E_INVALID_ACTION;
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
temp_header_value = malloc(soap_action_header->value.length + 1);
|
|
||||||
if (!temp_header_value) {
|
|
||||||
ret_code = UPNP_E_OUTOF_MEMORY;
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
strncpy(temp_header_value, soap_action_header->value.buf,
|
|
||||||
soap_action_header->value.length);
|
|
||||||
temp_header_value[soap_action_header->value.length] = 0;
|
|
||||||
temp = strchr(temp_header_value, '#');
|
|
||||||
if (!temp) {
|
|
||||||
free(temp_header_value);
|
|
||||||
ret_code = UPNP_E_INVALID_ACTION;
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*temp) = 0; /* temp make string */
|
|
||||||
|
|
||||||
/* check to see if it is Query State Variable or
|
|
||||||
* Service Action */
|
|
||||||
tempSize = strlen(urn) + 2;
|
|
||||||
ns_compare = malloc(tempSize);
|
|
||||||
if (!ns_compare) {
|
|
||||||
ret_code = UPNP_E_OUTOF_MEMORY;
|
|
||||||
free(temp_header_value);
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
snprintf(ns_compare, tempSize, "\"%s", urn);
|
|
||||||
/* Don't check version number, to accept a request comming on a v1
|
|
||||||
* service when publishing a v2 service */
|
|
||||||
if (strncmp(temp_header_value, ns_compare, strlen(ns_compare) - 2))
|
|
||||||
ret_code = UPNP_E_INVALID_ACTION;
|
|
||||||
else {
|
|
||||||
ret_code = UPNP_E_SUCCESS;
|
|
||||||
temp++;
|
|
||||||
temp2 = strchr(temp, '\"');
|
|
||||||
/* remove ending " if present */
|
|
||||||
if (temp2)
|
|
||||||
(*temp2) = 0;
|
|
||||||
if (*temp)
|
|
||||||
(*actionName) = strdup(temp);
|
|
||||||
if (!*actionName)
|
|
||||||
ret_code = UPNP_E_OUTOF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(temp_header_value);
|
|
||||||
free(ns_compare);
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Retrives all the information needed to process the incoming SOAP
|
|
||||||
* request. It finds the device and service info and also the callback
|
|
||||||
* function to hand-over the request to the device application.
|
|
||||||
*
|
|
||||||
* \return UPNP_E_SUCCESS if successful else returns appropriate error.
|
|
||||||
*/
|
|
||||||
static int get_device_info(
|
|
||||||
/*! [in] HTTP request. */
|
|
||||||
http_message_t *request,
|
|
||||||
/*! [in] flag for a querry. */
|
|
||||||
int isQuery,
|
|
||||||
/*! [in] Action request document. */
|
|
||||||
IXML_Document *actionDoc,
|
|
||||||
/*! [in] . */
|
|
||||||
int AddressFamily,
|
|
||||||
/*! [out] Device UDN string. */
|
|
||||||
OUT char device_udn[LINE_SIZE],
|
|
||||||
/*! [out] Service ID string. */
|
|
||||||
char service_id[LINE_SIZE],
|
|
||||||
/*! [out] callback function of the device application. */
|
|
||||||
Upnp_FunPtr *callback,
|
|
||||||
/*! [out] cookie stored by device application. */
|
|
||||||
void **cookie)
|
|
||||||
{
|
|
||||||
struct Handle_Info *device_info;
|
|
||||||
int device_hnd;
|
|
||||||
service_info *serv_info;
|
|
||||||
char save_char;
|
|
||||||
/* error by default */
|
|
||||||
int ret_code = -1;
|
|
||||||
const char *control_url;
|
|
||||||
char *actionName = NULL;
|
|
||||||
|
|
||||||
/* null-terminate pathquery of url */
|
|
||||||
control_url = request->uri.pathquery.buff;
|
|
||||||
save_char = control_url[request->uri.pathquery.size];
|
|
||||||
((char *)control_url)[request->uri.pathquery.size] = '\0';
|
|
||||||
|
|
||||||
HandleLock();
|
|
||||||
|
|
||||||
if (GetDeviceHandleInfo(AddressFamily, &device_hnd,
|
|
||||||
&device_info) != HND_DEVICE)
|
|
||||||
goto error_handler;
|
|
||||||
serv_info = FindServiceControlURLPath(
|
|
||||||
&device_info->ServiceTable, control_url);
|
|
||||||
if (!serv_info)
|
|
||||||
goto error_handler;
|
|
||||||
if (isQuery) {
|
|
||||||
ret_code = check_soap_action_header(request,
|
|
||||||
QUERY_STATE_VAR_URN, &actionName);
|
|
||||||
if (ret_code != UPNP_E_SUCCESS &&
|
|
||||||
ret_code != UPNP_E_OUTOF_MEMORY) {
|
|
||||||
ret_code = UPNP_E_INVALID_ACTION;
|
|
||||||
goto error_handler;
|
|
||||||
}
|
|
||||||
/* check soap body */
|
|
||||||
ret_code = check_soap_body(actionDoc, QUERY_STATE_VAR_URN,
|
|
||||||
actionName);
|
|
||||||
free(actionName);
|
|
||||||
if (ret_code != UPNP_E_SUCCESS)
|
|
||||||
goto error_handler;
|
|
||||||
} else {
|
|
||||||
ret_code = check_soap_action_header(request,
|
|
||||||
serv_info->serviceType, &actionName);
|
|
||||||
if (ret_code != UPNP_E_SUCCESS &&
|
|
||||||
ret_code != UPNP_E_OUTOF_MEMORY) {
|
|
||||||
ret_code = UPNP_E_INVALID_SERVICE;
|
|
||||||
goto error_handler;
|
|
||||||
}
|
|
||||||
/* check soap body */
|
|
||||||
ret_code = check_soap_body(actionDoc, serv_info->serviceType,
|
|
||||||
actionName);
|
|
||||||
free(actionName);
|
|
||||||
if (ret_code != UPNP_E_SUCCESS) {
|
|
||||||
ret_code = UPNP_E_INVALID_SERVICE;
|
|
||||||
goto error_handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
namecopy(service_id, serv_info->serviceId);
|
|
||||||
namecopy(device_udn, serv_info->UDN);
|
|
||||||
*callback = device_info->Callback;
|
|
||||||
*cookie = device_info->Cookie;
|
|
||||||
ret_code = 0;
|
|
||||||
|
|
||||||
error_handler:
|
|
||||||
/* restore */
|
|
||||||
((char *)control_url)[request->uri.pathquery.size] = save_char;
|
|
||||||
HandleUnlock();
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Sends the SOAP action response.
|
* \brief Sends the SOAP action response.
|
||||||
@@ -622,102 +282,38 @@ error_handler:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Finds the name of the state variable asked in the SOAP request.
|
|
||||||
*
|
|
||||||
* \return 0 if successful else returns -1.
|
|
||||||
*/
|
|
||||||
static UPNP_INLINE int get_var_name(
|
|
||||||
/*! [in] Document containing variable request. */
|
|
||||||
IXML_Document *TempDoc,
|
|
||||||
/*! [out] Name of the state varible. */
|
|
||||||
char *VarName)
|
|
||||||
{
|
|
||||||
IXML_Node *EnvpNode = NULL;
|
|
||||||
IXML_Node *BodyNode = NULL;
|
|
||||||
IXML_Node *StNode = NULL;
|
|
||||||
IXML_Node *VarNameNode = NULL;
|
|
||||||
IXML_Node *VarNode = NULL;
|
|
||||||
const DOMString StNodeName = NULL;
|
|
||||||
const DOMString Temp = NULL;
|
|
||||||
int ret_val = -1;
|
|
||||||
|
|
||||||
/* Got the Envelop node here */
|
|
||||||
EnvpNode = ixmlNode_getFirstChild((IXML_Node *) TempDoc);
|
|
||||||
if (EnvpNode == NULL)
|
|
||||||
goto error_handler;
|
|
||||||
/* Got Body here */
|
|
||||||
BodyNode = ixmlNode_getFirstChild(EnvpNode);
|
|
||||||
if (BodyNode == NULL)
|
|
||||||
goto error_handler;
|
|
||||||
/* Got action node here */
|
|
||||||
StNode = ixmlNode_getFirstChild(BodyNode);
|
|
||||||
if (StNode == NULL)
|
|
||||||
goto error_handler;
|
|
||||||
/* Test whether this is the action node */
|
|
||||||
StNodeName = ixmlNode_getNodeName(StNode);
|
|
||||||
if (StNodeName == NULL ||
|
|
||||||
strstr(StNodeName, "QueryStateVariable") == NULL)
|
|
||||||
goto error_handler;
|
|
||||||
VarNameNode = ixmlNode_getFirstChild(StNode);
|
|
||||||
if (VarNameNode == NULL)
|
|
||||||
goto error_handler;
|
|
||||||
VarNode = ixmlNode_getFirstChild(VarNameNode);
|
|
||||||
Temp = ixmlNode_getNodeValue(VarNode);
|
|
||||||
linecopy(VarName, Temp);
|
|
||||||
UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__,
|
|
||||||
"Received query for variable name %s\n", VarName);
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
ret_val = 0;
|
|
||||||
|
|
||||||
error_handler:
|
|
||||||
return ret_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Handles the SOAP requests to querry the state variables.
|
* \brief Handles the SOAP requests to querry the state variables.
|
||||||
* This functionality has been deprecated in the UPnP V1.0 architecture.
|
* This functionality has been deprecated in the UPnP V1.0 architecture.
|
||||||
*/
|
*/
|
||||||
static UPNP_INLINE void handle_query_variable(
|
static UPNP_INLINE void handle_query_variable(
|
||||||
/*! [in] Socket info. */
|
/*! [in] Socket info. */
|
||||||
SOCKINFO *info,
|
SOCKINFO *info,
|
||||||
/*! [in] HTTP request. */
|
/*! [in] HTTP Request. */
|
||||||
http_message_t *request,
|
http_message_t *request,
|
||||||
/*! [in] Document containing the variable request SOAP message. */
|
/*! [in] SOAP device/service information. */
|
||||||
IXML_Document *xml_doc)
|
soap_devserv_t *soap_info,
|
||||||
|
/*! [in] Node containing variable name. */
|
||||||
|
IXML_Node *req_node)
|
||||||
{
|
{
|
||||||
Upnp_FunPtr soap_event_callback;
|
|
||||||
void *cookie;
|
|
||||||
char var_name[LINE_SIZE];
|
|
||||||
struct Upnp_State_Var_Request variable;
|
struct Upnp_State_Var_Request variable;
|
||||||
const char *err_str;
|
const char *err_str;
|
||||||
int err_code;
|
int err_code;
|
||||||
|
const DOMString var_name;
|
||||||
|
|
||||||
/* get var name */
|
|
||||||
if (get_var_name(xml_doc, var_name) != 0) {
|
|
||||||
send_error_response(info, SOAP_INVALID_VAR,
|
|
||||||
Soap_Invalid_Var, request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* get info for event */
|
|
||||||
err_code = get_device_info(request, 1, xml_doc,
|
|
||||||
info->foreign_sockaddr.ss_family,
|
|
||||||
variable.DevUDN,
|
|
||||||
variable.ServiceID,
|
|
||||||
&soap_event_callback, &cookie);
|
|
||||||
if (err_code != 0) {
|
|
||||||
send_error_response(info, SOAP_INVALID_VAR,
|
|
||||||
Soap_Invalid_Var, request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
linecopy(variable.ErrStr, "");
|
|
||||||
variable.ErrCode = UPNP_E_SUCCESS;
|
variable.ErrCode = UPNP_E_SUCCESS;
|
||||||
|
linecopy(variable.ErrStr, "");
|
||||||
|
namecopy(variable.DevUDN, soap_info->dev_udn);
|
||||||
|
namecopy(variable.ServiceID, soap_info->service_id);
|
||||||
|
var_name = ixmlNode_getNodeValue(req_node);
|
||||||
namecopy(variable.StateVarName, var_name);
|
namecopy(variable.StateVarName, var_name);
|
||||||
variable.CurrentVal = NULL;
|
|
||||||
variable.CtrlPtIPAddr = info->foreign_sockaddr;
|
variable.CtrlPtIPAddr = info->foreign_sockaddr;
|
||||||
|
variable.CurrentVal = NULL;
|
||||||
|
|
||||||
/* send event */
|
/* send event */
|
||||||
soap_event_callback(UPNP_CONTROL_GET_VAR_REQUEST, &variable, cookie);
|
soap_info->callback(UPNP_CONTROL_GET_VAR_REQUEST, &variable,
|
||||||
|
soap_info->cookie);
|
||||||
UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__,
|
UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__,
|
||||||
"Return from callback for var request\n");
|
"Return from callback for var request\n");
|
||||||
/* validate, and handle result */
|
/* validate, and handle result */
|
||||||
@@ -744,56 +340,59 @@ static UPNP_INLINE void handle_query_variable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Handles the SOAP action request. It checks the integrity of the SOAP
|
* \brief Handles the SOAP action request.
|
||||||
* action request and gives the call back to the device application.
|
|
||||||
*/
|
*/
|
||||||
static void handle_invoke_action(
|
static void handle_invoke_action(
|
||||||
/*! [in] Socket info. */
|
/*! [in] Socket info. */
|
||||||
IN SOCKINFO *info,
|
SOCKINFO *info,
|
||||||
/*! [in] HTTP Request. */
|
/*! [in] HTTP Request. */
|
||||||
IN http_message_t *request,
|
http_message_t *request,
|
||||||
/*! [in] Name of the SOAP Action. */
|
/*! [in] SOAP device/service information. */
|
||||||
IN memptr action_name,
|
soap_devserv_t *soap_info,
|
||||||
/*! [in] Document containing the SOAP action request. */
|
/*! [in] Node containing the SOAP action request. */
|
||||||
IN IXML_Document *xml_doc)
|
IXML_Node *req_node)
|
||||||
{
|
{
|
||||||
char save_char;
|
char save_char;
|
||||||
IXML_Document *resp_node = NULL;
|
IXML_Document *req_doc = NULL;
|
||||||
struct Upnp_Action_Request action;
|
struct Upnp_Action_Request action;
|
||||||
Upnp_FunPtr soap_event_callback;
|
|
||||||
void *cookie = NULL;
|
|
||||||
int err_code;
|
int err_code;
|
||||||
const char *err_str;
|
const char *err_str;
|
||||||
|
memptr action_name;
|
||||||
action.ActionResult = NULL;
|
action.ActionResult = NULL;
|
||||||
|
DOMString act_node = NULL;
|
||||||
|
|
||||||
/* null-terminate */
|
/* null-terminate */
|
||||||
|
action_name = soap_info->action_name;
|
||||||
save_char = action_name.buf[action_name.length];
|
save_char = action_name.buf[action_name.length];
|
||||||
action_name.buf[action_name.length] = '\0';
|
action_name.buf[action_name.length] = '\0';
|
||||||
/* set default error */
|
|
||||||
err_code = SOAP_INVALID_ACTION;
|
|
||||||
err_str = Soap_Invalid_Action;
|
|
||||||
/* get action node */
|
/* get action node */
|
||||||
if (get_action_node(xml_doc, action_name.buf, &resp_node) == -1)
|
act_node = ixmlPrintNode(req_node);
|
||||||
|
if (!act_node) {
|
||||||
|
err_code = SOAP_MEMORY_OUT;
|
||||||
|
err_str = Soap_Memory_out;
|
||||||
goto error_handler;
|
goto error_handler;
|
||||||
/* get device info for action event */
|
}
|
||||||
err_code = get_device_info(request,
|
err_code = ixmlParseBufferEx(act_node, &req_doc);
|
||||||
0,
|
if (err_code != IXML_SUCCESS) {
|
||||||
xml_doc,
|
if (IXML_INSUFFICIENT_MEMORY == err_code) {
|
||||||
info->foreign_sockaddr.ss_family,
|
err_code = SOAP_MEMORY_OUT;
|
||||||
action.DevUDN,
|
err_str = Soap_Memory_out;
|
||||||
action.ServiceID,
|
} else {
|
||||||
&soap_event_callback, &cookie);
|
err_code = SOAP_INVALID_ACTION;
|
||||||
|
err_str = Soap_Invalid_Action;
|
||||||
if (err_code != UPNP_E_SUCCESS)
|
}
|
||||||
goto error_handler;
|
goto error_handler;
|
||||||
namecopy(action.ActionName, action_name.buf);
|
}
|
||||||
linecopy(action.ErrStr, "");
|
|
||||||
action.ActionRequest = resp_node;
|
|
||||||
action.ActionResult = NULL;
|
|
||||||
action.ErrCode = UPNP_E_SUCCESS;
|
action.ErrCode = UPNP_E_SUCCESS;
|
||||||
|
linecopy(action.ErrStr, "");
|
||||||
|
namecopy(action.ActionName, action_name.buf);
|
||||||
|
namecopy(action.DevUDN, soap_info->dev_udn);
|
||||||
|
namecopy(action.ServiceID, soap_info->service_id);
|
||||||
|
action.ActionRequest = req_doc;
|
||||||
|
action.ActionResult = NULL;
|
||||||
action.CtrlPtIPAddr = info->foreign_sockaddr;
|
action.CtrlPtIPAddr = info->foreign_sockaddr;
|
||||||
UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__, "Calling Callback\n");
|
UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__, "Calling Callback\n");
|
||||||
soap_event_callback(UPNP_CONTROL_ACTION_REQUEST, &action, cookie);
|
soap_info->callback(UPNP_CONTROL_ACTION_REQUEST, &action, soap_info->cookie);
|
||||||
if (action.ErrCode != UPNP_E_SUCCESS) {
|
if (action.ErrCode != UPNP_E_SUCCESS) {
|
||||||
if (strlen(action.ErrStr) <= 0) {
|
if (strlen(action.ErrStr) <= 0) {
|
||||||
err_code = SOAP_ACTION_FAILED;
|
err_code = SOAP_ACTION_FAILED;
|
||||||
@@ -817,17 +416,279 @@ static void handle_invoke_action(
|
|||||||
/* error handling and cleanup */
|
/* error handling and cleanup */
|
||||||
error_handler:
|
error_handler:
|
||||||
ixmlDocument_free(action.ActionResult);
|
ixmlDocument_free(action.ActionResult);
|
||||||
ixmlDocument_free(resp_node);
|
ixmlDocument_free(req_doc);
|
||||||
|
ixmlFreeDOMString(act_node);
|
||||||
/* restore */
|
/* restore */
|
||||||
action_name.buf[action_name.length] = save_char;
|
action_name.buf[action_name.length] = save_char;
|
||||||
if (err_code != 0)
|
if (err_code != 0)
|
||||||
send_error_response(info, err_code, err_str, request);
|
send_error_response(info, err_code, err_str, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Retrieve SOAP device/service information associated
|
||||||
|
* with request-URI, which includes the callback function to hand-over
|
||||||
|
* the request to the device application.
|
||||||
|
*
|
||||||
|
* \return 0 if OK, -1 on error.
|
||||||
|
*/
|
||||||
|
static int get_dev_service(
|
||||||
|
/*! [in] HTTP request. */
|
||||||
|
http_message_t *request,
|
||||||
|
/*! [in] Address family: AF_INET or AF_INET6. */
|
||||||
|
int AddressFamily,
|
||||||
|
/*! [out] SOAP device/service information. */
|
||||||
|
soap_devserv_t *soap_info)
|
||||||
|
{
|
||||||
|
struct Handle_Info *device_info;
|
||||||
|
int device_hnd;
|
||||||
|
service_info *serv_info;
|
||||||
|
char save_char;
|
||||||
|
/* error by default */
|
||||||
|
int ret_code = -1;
|
||||||
|
const char *control_url;
|
||||||
|
|
||||||
|
/* null-terminate pathquery of url */
|
||||||
|
control_url = request->uri.pathquery.buff;
|
||||||
|
save_char = control_url[request->uri.pathquery.size];
|
||||||
|
((char *)control_url)[request->uri.pathquery.size] = '\0';
|
||||||
|
|
||||||
|
HandleReadLock();
|
||||||
|
|
||||||
|
if (GetDeviceHandleInfo(AddressFamily, &device_hnd,
|
||||||
|
&device_info) != HND_DEVICE)
|
||||||
|
goto error_handler;
|
||||||
|
serv_info = FindServiceControlURLPath(
|
||||||
|
&device_info->ServiceTable, control_url);
|
||||||
|
if (!serv_info)
|
||||||
|
goto error_handler;
|
||||||
|
|
||||||
|
namecopy(soap_info->dev_udn, serv_info->UDN);
|
||||||
|
namecopy(soap_info->service_type, serv_info->serviceType);
|
||||||
|
namecopy(soap_info->service_id, serv_info->serviceId);
|
||||||
|
soap_info->callback = device_info->Callback;
|
||||||
|
soap_info->cookie = device_info->Cookie;
|
||||||
|
ret_code = 0;
|
||||||
|
|
||||||
|
error_handler:
|
||||||
|
/* restore */
|
||||||
|
((char *)control_url)[request->uri.pathquery.size] = save_char;
|
||||||
|
HandleUnlock();
|
||||||
|
return ret_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Get the SOAPACTION header value for M-POST request.
|
||||||
|
*
|
||||||
|
* \return UPNP_E_SUCCESS if OK, error number on failure.
|
||||||
|
*/
|
||||||
|
static int get_mpost_acton_hdrval(
|
||||||
|
/*! [in] HTTP request. */
|
||||||
|
http_message_t *request,
|
||||||
|
/*! [out] Buffer to get the header value */
|
||||||
|
memptr *val)
|
||||||
|
{
|
||||||
|
http_header_t *hdr;
|
||||||
|
memptr ns_value, dummy_quote, value;
|
||||||
|
membuffer soap_action_name;
|
||||||
|
|
||||||
|
assert(HTTPMETHOD_MPOST == request->method);
|
||||||
|
hdr = httpmsg_find_hdr(request, HDR_MAN, &value);
|
||||||
|
if (NULL == hdr)
|
||||||
|
return SREQ_NOT_EXTENDED;
|
||||||
|
if (matchstr(value.buf, value.length, "%q%i ; ns = %s",
|
||||||
|
&dummy_quote, &ns_value) != PARSE_OK)
|
||||||
|
return SREQ_NOT_EXTENDED;
|
||||||
|
/* create soapaction name header */
|
||||||
|
membuffer_init(&soap_action_name);
|
||||||
|
if (membuffer_assign(&soap_action_name,
|
||||||
|
ns_value.buf, ns_value.length) == UPNP_E_OUTOF_MEMORY ||
|
||||||
|
membuffer_append_str(&soap_action_name,
|
||||||
|
"-SOAPACTION") == UPNP_E_OUTOF_MEMORY) {
|
||||||
|
membuffer_destroy(&soap_action_name);
|
||||||
|
return UPNP_E_OUTOF_MEMORY;
|
||||||
|
}
|
||||||
|
hdr = httpmsg_find_hdr_str(request, soap_action_name.buf);
|
||||||
|
membuffer_destroy(&soap_action_name);
|
||||||
|
if (NULL == hdr)
|
||||||
|
return SREQ_HDR_NOT_FOUND;
|
||||||
|
val->buf = hdr->value.buf;
|
||||||
|
val->length = hdr->value.length;
|
||||||
|
return UPNP_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Check the header validity, and get the action name
|
||||||
|
* and the version of the service that the CP wants to use.
|
||||||
|
*
|
||||||
|
* \return UPNP_E_SUCCESS if OK, error number on failure.
|
||||||
|
*/
|
||||||
|
static int check_soapaction_hdr(
|
||||||
|
/*! [in] HTTP request. */
|
||||||
|
http_message_t *request,
|
||||||
|
/*! [in, out] SOAP device/service information. */
|
||||||
|
soap_devserv_t *soap_info)
|
||||||
|
{
|
||||||
|
memptr value;
|
||||||
|
char save_char;
|
||||||
|
char *hash_pos = NULL;
|
||||||
|
char *col_pos1, *col_pos2, *serv_type;
|
||||||
|
int ret_code;
|
||||||
|
|
||||||
|
/* find SOAPACTION header */
|
||||||
|
if (SOAPMETHOD_POST == request->method) {
|
||||||
|
if (!httpmsg_find_hdr(request, HDR_SOAPACTION, &value))
|
||||||
|
return SREQ_HDR_NOT_FOUND;
|
||||||
|
} else {
|
||||||
|
ret_code = get_mpost_acton_hdrval(request, &value);
|
||||||
|
if (ret_code != UPNP_E_SUCCESS) {
|
||||||
|
return ret_code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* error by default */
|
||||||
|
ret_code = SREQ_BAD_HDR_FORMAT;
|
||||||
|
/* get action name*/
|
||||||
|
save_char = value.buf[value.length];
|
||||||
|
value.buf[value.length] = '\0';
|
||||||
|
hash_pos = strchr(value.buf, '#');
|
||||||
|
if (NULL == hash_pos) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
*hash_pos = '\0';
|
||||||
|
if (matchstr(hash_pos+1,
|
||||||
|
value.length - (size_t)(hash_pos+1 - value.buf),
|
||||||
|
"%s", &soap_info->action_name) != PARSE_OK) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check service type */
|
||||||
|
if (value.buf[0] != '\"') {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
serv_type = &value.buf[1];
|
||||||
|
col_pos1 = strrchr(serv_type, ':');
|
||||||
|
if (NULL == col_pos1) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
col_pos2 = strrchr(soap_info->service_type, ':');
|
||||||
|
/* XXX: this should be checked when service list is generated */
|
||||||
|
assert(col_pos2 != NULL);
|
||||||
|
if (col_pos2-soap_info->service_type == col_pos1-serv_type &&
|
||||||
|
strncmp(soap_info->service_type, serv_type, col_pos1-serv_type) == 0) {
|
||||||
|
/* for action invocation, update the version information */
|
||||||
|
namecopy(soap_info->service_type, serv_type);
|
||||||
|
} else if (strcmp(serv_type, QUERY_STATE_VAR_URN) == 0 &&
|
||||||
|
memptr_cmp(&soap_info->action_name, "QueryStateVariable") == 0) {
|
||||||
|
/* query variable */
|
||||||
|
soap_info->action_name.buf = NULL;
|
||||||
|
soap_info->action_name.length = 0;
|
||||||
|
} else {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
ret_code = UPNP_E_SUCCESS;
|
||||||
|
|
||||||
|
error_handler:
|
||||||
|
if (hash_pos != NULL) {
|
||||||
|
*hash_pos = '#';
|
||||||
|
}
|
||||||
|
value.buf[value.length] = save_char;
|
||||||
|
return ret_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Check validity of the SOAP request per UPnP specification.
|
||||||
|
*
|
||||||
|
* \return 0 if OK, -1 on failure.
|
||||||
|
*/
|
||||||
|
static int check_soap_request(
|
||||||
|
/*! [in] SOAP device/service information. */
|
||||||
|
soap_devserv_t *soap_info,
|
||||||
|
/*! [in] Document containing the SOAP action request. */
|
||||||
|
IN IXML_Document *xml_doc,
|
||||||
|
/*! [out] Node containing the SOAP action request/variable name. */
|
||||||
|
IXML_Node **req_node)
|
||||||
|
{
|
||||||
|
IXML_Node *envp_node = NULL;
|
||||||
|
IXML_Node *body_node = NULL;
|
||||||
|
IXML_Node *action_node = NULL;
|
||||||
|
const DOMString local_name = NULL;
|
||||||
|
const DOMString ns_uri = NULL;
|
||||||
|
int ret_val = -1;
|
||||||
|
|
||||||
|
/* Got the Envelop node here */
|
||||||
|
envp_node = ixmlNode_getFirstChild((IXML_Node *) xml_doc);
|
||||||
|
if (NULL == envp_node) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
ns_uri = ixmlNode_getNamespaceURI(envp_node);
|
||||||
|
if (NULL == ns_uri || strcmp(ns_uri, SOAP_URN) != 0) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
/* Got Body here */
|
||||||
|
body_node = ixmlNode_getFirstChild(envp_node);
|
||||||
|
if (NULL == body_node) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
local_name = ixmlNode_getLocalName(body_node);
|
||||||
|
if (NULL == local_name || strcmp(local_name, SOAP_BODY) != 0) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
/* Got action node here */
|
||||||
|
action_node = ixmlNode_getFirstChild(body_node);
|
||||||
|
if (NULL == action_node) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
/* check local name and namespace of action node */
|
||||||
|
ns_uri = ixmlNode_getNamespaceURI(action_node);
|
||||||
|
if (NULL == ns_uri) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
local_name = ixmlNode_getLocalName(action_node);
|
||||||
|
if (NULL == local_name) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
if (NULL == soap_info->action_name.buf) {
|
||||||
|
IXML_Node *varname_node = NULL;
|
||||||
|
IXML_Node *nametxt_node = NULL;
|
||||||
|
if (strcmp(ns_uri, QUERY_STATE_VAR_URN) != 0 ||
|
||||||
|
strcmp(local_name, "QueryStateVariable") != 0) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
varname_node = ixmlNode_getFirstChild(action_node);
|
||||||
|
if(NULL == varname_node) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
local_name = ixmlNode_getLocalName(varname_node);
|
||||||
|
if (strcmp(local_name, "varName") != 0) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
nametxt_node = ixmlNode_getFirstChild(varname_node);
|
||||||
|
if (NULL == nametxt_node ||
|
||||||
|
ixmlNode_getNodeType(nametxt_node) != eTEXT_NODE) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
*req_node = nametxt_node;
|
||||||
|
} else {
|
||||||
|
/* check service type against SOAPACTION header */
|
||||||
|
if (strcmp(soap_info->service_type, ns_uri) != 0 ||
|
||||||
|
memptr_cmp(&soap_info->action_name, local_name) != 0) {
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
*req_node = action_node;
|
||||||
|
}
|
||||||
|
/* success */
|
||||||
|
ret_val = 0;
|
||||||
|
|
||||||
|
error_handler:
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief This is a callback called by minisever after receiving the request
|
* \brief This is a callback called by minisever after receiving the request
|
||||||
* from the control point. This function will start processing the request.
|
* from the control point. After HTTP processing, it calls handle_soap_request
|
||||||
* It calls handle_invoke_action to handle the SOAP action.
|
* to start SOAP processing.
|
||||||
*/
|
*/
|
||||||
void soap_device_callback(
|
void soap_device_callback(
|
||||||
/*! [in] Parsed request received by the device. */
|
/*! [in] Parsed request received by the device. */
|
||||||
@@ -838,43 +699,74 @@ void soap_device_callback(
|
|||||||
SOCKINFO *info)
|
SOCKINFO *info)
|
||||||
{
|
{
|
||||||
int err_code;
|
int err_code;
|
||||||
const char *err_str;
|
|
||||||
memptr action_name;
|
|
||||||
IXML_Document *xml_doc = NULL;
|
IXML_Document *xml_doc = NULL;
|
||||||
|
soap_devserv_t *soap_info = NULL;
|
||||||
|
IXML_Node *req_node = NULL;
|
||||||
|
|
||||||
/* set default error */
|
/* get device/service identified by the request-URI */
|
||||||
err_code = SOAP_INVALID_ACTION;
|
soap_info = malloc(sizeof(soap_devserv_t));
|
||||||
err_str = Soap_Invalid_Action;
|
if (NULL == soap_info) {
|
||||||
|
err_code = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
if (get_dev_service(request,
|
||||||
|
info->foreign_sockaddr.ss_family, soap_info) < 0) {
|
||||||
|
err_code = HTTP_NOT_FOUND;
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
/* validate: content-type == text/xml */
|
/* validate: content-type == text/xml */
|
||||||
if (!has_xml_content_type(request))
|
if (!has_xml_content_type(request)) {
|
||||||
|
err_code = HTTP_UNSUPPORTED_MEDIA_TYPE;
|
||||||
goto error_handler;
|
goto error_handler;
|
||||||
/* type of request */
|
}
|
||||||
if (get_request_type(request, &action_name) != 0)
|
/* check SOAPACTION HTTP header */
|
||||||
|
err_code = check_soapaction_hdr(request, soap_info);
|
||||||
|
if (err_code != UPNP_E_SUCCESS) {
|
||||||
|
switch (err_code) {
|
||||||
|
case SREQ_NOT_EXTENDED:
|
||||||
|
err_code = HTTP_NOT_EXTENDED;
|
||||||
|
break;
|
||||||
|
case UPNP_E_OUTOF_MEMORY:
|
||||||
|
err_code = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err_code = HTTP_BAD_REQUEST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
goto error_handler;
|
goto error_handler;
|
||||||
|
}
|
||||||
/* parse XML */
|
/* parse XML */
|
||||||
err_code = ixmlParseBufferEx(request->entity.buf, &xml_doc);
|
err_code = ixmlParseBufferEx(request->entity.buf, &xml_doc);
|
||||||
if (err_code != IXML_SUCCESS) {
|
if (err_code != IXML_SUCCESS) {
|
||||||
if (err_code == IXML_INSUFFICIENT_MEMORY)
|
if (IXML_INSUFFICIENT_MEMORY == err_code)
|
||||||
err_code = UPNP_E_OUTOF_MEMORY;
|
err_code = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
else
|
else
|
||||||
err_code = SOAP_ACTION_FAILED;
|
err_code = HTTP_BAD_REQUEST;
|
||||||
err_str = "XML error";
|
|
||||||
goto error_handler;
|
goto error_handler;
|
||||||
}
|
}
|
||||||
if (action_name.length == 0)
|
/* check SOAP body */
|
||||||
|
if (check_soap_request(soap_info, xml_doc, &req_node) < 0)
|
||||||
|
{
|
||||||
|
err_code = HTTP_BAD_REQUEST;
|
||||||
|
goto error_handler;
|
||||||
|
}
|
||||||
|
/* process SOAP request */
|
||||||
|
if (NULL == soap_info->action_name.buf)
|
||||||
/* query var */
|
/* query var */
|
||||||
handle_query_variable(info, request, xml_doc);
|
handle_query_variable(info, request, soap_info, req_node);
|
||||||
else
|
else
|
||||||
/* invoke action */
|
/* invoke action */
|
||||||
handle_invoke_action(info, request, action_name, xml_doc);
|
handle_invoke_action(info, request, soap_info, req_node);
|
||||||
/* no error */
|
|
||||||
err_code = 0;
|
|
||||||
|
|
||||||
error_handler:
|
err_code = HTTP_OK;
|
||||||
|
|
||||||
|
error_handler:
|
||||||
ixmlDocument_free(xml_doc);
|
ixmlDocument_free(xml_doc);
|
||||||
if (err_code != 0)
|
free(soap_info);
|
||||||
send_error_response(info, err_code, err_str, request);
|
if (err_code != HTTP_OK) {
|
||||||
|
http_SendStatusResponse(info, err_code, request->major_version,
|
||||||
|
request->minor_version);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
parser = parser;
|
parser = parser;
|
||||||
}
|
}
|
||||||
|
@@ -143,15 +143,12 @@ static UPNP_INLINE int calc_alias(
|
|||||||
aliasPtr = alias + 1;
|
aliasPtr = alias + 1;
|
||||||
else
|
else
|
||||||
aliasPtr = alias;
|
aliasPtr = alias;
|
||||||
new_alias_len = root_len + strlen(temp_str) + strlen(aliasPtr);
|
new_alias_len = root_len + strlen(temp_str) + strlen(aliasPtr) + (size_t)1;
|
||||||
alias_temp = malloc(new_alias_len + (size_t)1);
|
alias_temp = malloc(new_alias_len);
|
||||||
if (alias_temp == NULL)
|
if (alias_temp == NULL)
|
||||||
return UPNP_E_OUTOF_MEMORY;
|
return UPNP_E_OUTOF_MEMORY;
|
||||||
memset(alias_temp, 0, new_alias_len + (size_t)1);
|
memset(alias_temp, 0, new_alias_len);
|
||||||
strncpy(alias_temp, rootPath, root_len);
|
snprintf(alias_temp, new_alias_len, "%s%s%s", rootPath, temp_str, aliasPtr);
|
||||||
alias_temp[root_len] = '\0';
|
|
||||||
strncat(alias_temp, temp_str, strlen(temp_str));
|
|
||||||
strncat(alias_temp, aliasPtr, strlen(aliasPtr));
|
|
||||||
|
|
||||||
*newAlias = alias_temp;
|
*newAlias = alias_temp;
|
||||||
return UPNP_E_SUCCESS;
|
return UPNP_E_SUCCESS;
|
||||||
@@ -186,14 +183,10 @@ static UPNP_INLINE int calc_descURL(
|
|||||||
assert(ipPortStr != NULL && strlen(ipPortStr) > 0);
|
assert(ipPortStr != NULL && strlen(ipPortStr) > 0);
|
||||||
assert(alias != NULL && strlen(alias) > 0);
|
assert(alias != NULL && strlen(alias) > 0);
|
||||||
|
|
||||||
len = strlen(http_scheme) + strlen(ipPortStr) + strlen(alias);
|
len = strlen(http_scheme) + strlen(ipPortStr) + strlen(alias) + (size_t)1;
|
||||||
if (len > ((size_t)LINE_SIZE - (size_t)1))
|
if (len > (size_t)LINE_SIZE)
|
||||||
return UPNP_E_URL_TOO_BIG;
|
return UPNP_E_URL_TOO_BIG;
|
||||||
strncpy(descURL, http_scheme, strlen(http_scheme));
|
snprintf(descURL, len, "%s%s%s", http_scheme, ipPortStr, alias);
|
||||||
descURL[strlen(http_scheme)] = '\0';
|
|
||||||
strncat(descURL, ipPortStr, strlen(ipPortStr));
|
|
||||||
strncat(descURL, alias, strlen(alias));
|
|
||||||
descURL[len] = '\0';
|
|
||||||
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
||||||
"desc url: %s\n", descURL);
|
"desc url: %s\n", descURL);
|
||||||
|
|
||||||
|
131
upnp/test/test_url.c
Normal file
131
upnp/test/test_url.c
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "upnp.h"
|
||||||
|
#include "upnptools.h"
|
||||||
|
|
||||||
|
struct test {
|
||||||
|
const char *base;
|
||||||
|
const char *rel;
|
||||||
|
const char *expect;
|
||||||
|
int line;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
#define TEST(BaseURL, RelURL, expect, ...) {BaseURL, RelURL, expect, __LINE__, ##__VA_ARGS__}
|
||||||
|
|
||||||
|
static int
|
||||||
|
result(const struct test *test)
|
||||||
|
{
|
||||||
|
char *absurl = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = UpnpResolveURL2(test->base, test->rel, &absurl);
|
||||||
|
if (ret == test->error && (test->expect == NULL || strcmp(test->expect, absurl) == 0)) {
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
printf("%s:%d: '%s' | '%s' -> '%s' != '%s' (%d)\n", __FILE__, test->line, test->base, test->rel, absurl, test->expect, ret);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
free(absurl);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The URLs must be resolvale! */
|
||||||
|
static const char ABS_URL1[] = "http://www.libupnp.org/path1/";
|
||||||
|
static const char ABS_URL2[] = "http://www.libupnp.org/path1/path1";
|
||||||
|
static const char ABS_URL3[] = "http://localhost/path1/";
|
||||||
|
static const char ABS_URL4[] = "http://127.0.0.1/path1/";
|
||||||
|
static const char ABS_URL5[] = "http://127.0.0.1:6544/path1/";
|
||||||
|
static const char ABS_URL6[] = "http://[::1]:6544/path1/";
|
||||||
|
|
||||||
|
static const char REL_URL1[] = "//localhost/path2";
|
||||||
|
static const char REL_URL2[] = "/path3";
|
||||||
|
static const char REL_URL3[] = "path4";
|
||||||
|
static const char REL_URL4[] = "../path5";
|
||||||
|
static const char REL_URL5[] = "?query1";
|
||||||
|
static const char REL_URL6[] = "#frag1";
|
||||||
|
|
||||||
|
static const char ABS_RFC[] = "http://localhost/b/c/d;p?q";
|
||||||
|
// s,\<a\>,localhost,
|
||||||
|
// s,//g\>,//127.0.0.1,
|
||||||
|
|
||||||
|
static const struct test RFC3986[] = {
|
||||||
|
// Errors
|
||||||
|
TEST(NULL, NULL, NULL, UPNP_E_INVALID_PARAM),
|
||||||
|
TEST(ABS_URL1, NULL, NULL, UPNP_E_INVALID_PARAM),
|
||||||
|
TEST("foo", "bar", NULL, UPNP_E_INVALID_URL),
|
||||||
|
// Custom
|
||||||
|
TEST(NULL, ABS_URL1, ABS_URL1),
|
||||||
|
TEST(ABS_URL1, ABS_URL2, ABS_URL2),
|
||||||
|
TEST(ABS_URL1, "", ABS_URL1),
|
||||||
|
TEST(ABS_URL1, REL_URL1, "http://localhost/path2"),
|
||||||
|
TEST(ABS_URL2, REL_URL1, "http://localhost/path2"),
|
||||||
|
TEST(ABS_URL1, REL_URL2, "http://www.libupnp.org/path3"),
|
||||||
|
TEST(ABS_URL2, REL_URL2, "http://www.libupnp.org/path3"),
|
||||||
|
TEST(ABS_URL1, REL_URL3, "http://www.libupnp.org/path1/path4"),
|
||||||
|
TEST(ABS_URL2, REL_URL3, "http://www.libupnp.org/path1/path4"),
|
||||||
|
TEST(ABS_URL1, REL_URL4, "http://www.libupnp.org/path5"),
|
||||||
|
TEST(ABS_URL2, REL_URL4, "http://www.libupnp.org/path5"),
|
||||||
|
TEST(ABS_URL1, REL_URL6, "http://www.libupnp.org/path1/#frag1"),
|
||||||
|
TEST(ABS_URL2, REL_URL6, "http://www.libupnp.org/path1/path1#frag1"),
|
||||||
|
TEST("http://127.0.0.1:6544/getDeviceDesc", "CDS_Event", "http://127.0.0.1:6544/CDS_Event"),
|
||||||
|
// <http://tools.ietf.org/html/rfc3986#section-5.4.1> Normal Examples
|
||||||
|
TEST(ABS_RFC, "g:h", "g:h"),
|
||||||
|
TEST(ABS_RFC, "g", "http://localhost/b/c/g"),
|
||||||
|
TEST(ABS_RFC, "./g", "http://localhost/b/c/g"),
|
||||||
|
TEST(ABS_RFC, "g/", "http://localhost/b/c/g/"),
|
||||||
|
TEST(ABS_RFC, "/g", "http://localhost/g"),
|
||||||
|
TEST(ABS_RFC, "//127.0.0.1", "http://127.0.0.1"),
|
||||||
|
TEST(ABS_RFC, "?y", "http://localhost/b/c/d;p?y"),
|
||||||
|
TEST(ABS_RFC, "g?y", "http://localhost/b/c/g?y"),
|
||||||
|
TEST(ABS_RFC, "#s", "http://localhost/b/c/d;p?q#s"),
|
||||||
|
TEST(ABS_RFC, "g#s", "http://localhost/b/c/g#s"),
|
||||||
|
TEST(ABS_RFC, "g?y#s", "http://localhost/b/c/g?y#s"),
|
||||||
|
TEST(ABS_RFC, ";x", "http://localhost/b/c/;x"),
|
||||||
|
TEST(ABS_RFC, "g;x", "http://localhost/b/c/g;x"),
|
||||||
|
TEST(ABS_RFC, "g;x?y#s", "http://localhost/b/c/g;x?y#s"),
|
||||||
|
TEST(ABS_RFC, "", "http://localhost/b/c/d;p?q"),
|
||||||
|
TEST(ABS_RFC, ".", "http://localhost/b/c/"),
|
||||||
|
TEST(ABS_RFC, "./", "http://localhost/b/c/"),
|
||||||
|
TEST(ABS_RFC, "..", "http://localhost/b/"),
|
||||||
|
TEST(ABS_RFC, "../", "http://localhost/b/"),
|
||||||
|
TEST(ABS_RFC, "../g", "http://localhost/b/g"),
|
||||||
|
TEST(ABS_RFC, "../..", "http://localhost/"),
|
||||||
|
TEST(ABS_RFC, "../../", "http://localhost/"),
|
||||||
|
TEST(ABS_RFC, "../../g", "http://localhost/g"),
|
||||||
|
// <http://tools.ietf.org/html/rfc3986#section-5.4.2> Abnormal Examples
|
||||||
|
TEST(ABS_RFC, "../../../g", "http://localhost/g"),
|
||||||
|
TEST(ABS_RFC, "../../../../g", "http://localhost/g"),
|
||||||
|
TEST(ABS_RFC, "/./g", "http://localhost/g"),
|
||||||
|
TEST(ABS_RFC, "/../g", "http://localhost/g"),
|
||||||
|
TEST(ABS_RFC, "g.", "http://localhost/b/c/g."),
|
||||||
|
TEST(ABS_RFC, ".g", "http://localhost/b/c/.g"),
|
||||||
|
TEST(ABS_RFC, "g..", "http://localhost/b/c/g.."),
|
||||||
|
TEST(ABS_RFC, "..g", "http://localhost/b/c/..g"),
|
||||||
|
TEST(ABS_RFC, "./../g", "http://localhost/b/g"),
|
||||||
|
TEST(ABS_RFC, "./g/.", "http://localhost/b/c/g/"),
|
||||||
|
TEST(ABS_RFC, "g/./h", "http://localhost/b/c/g/h"),
|
||||||
|
TEST(ABS_RFC, "g/../h", "http://localhost/b/c/h"),
|
||||||
|
TEST(ABS_RFC, "g;x=1/./y", "http://localhost/b/c/g;x=1/y"),
|
||||||
|
TEST(ABS_RFC, "g;x=1/../y", "http://localhost/b/c/y"),
|
||||||
|
TEST(ABS_RFC, "g?y/./x", "http://localhost/b/c/g?y/./x"),
|
||||||
|
TEST(ABS_RFC, "g?y/../x", "http://localhost/b/c/g?y/../x"),
|
||||||
|
TEST(ABS_RFC, "g#s/./x", "http://localhost/b/c/g#s/./x"),
|
||||||
|
TEST(ABS_RFC, "g#s/../x", "http://localhost/b/c/g#s/../x"),
|
||||||
|
TEST(ABS_RFC, "http:g", "http:g"),
|
||||||
|
};
|
||||||
|
#define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(RFC3986); i++)
|
||||||
|
ret += result(&RFC3986[i]);
|
||||||
|
|
||||||
|
exit (ret ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcc -o url-test -g url-test.c -I ixml/inc -I threadutil/inc -I upnp/inc upnp/.libs/libupnp.a -L ixml/.libs -lixml -L threadutil/.libs -lthreadutil -lpthread
|
Reference in New Issue
Block a user