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
|
||||
GTAGS
|
||||
|
||||
# QT-Creator files
|
||||
Makefile.am.user
|
||||
pupnp.config
|
||||
pupnp.creator
|
||||
pupnp.creator.user
|
||||
pupnp.files
|
||||
pupnp.includes
|
||||
|
||||
*.orig
|
||||
*~
|
||||
\#*#
|
||||
@@ -109,4 +117,4 @@ docs/doxygen
|
||||
/build/vc10/out.vc9.Win32/Debug
|
||||
/build/vc10/out.vc10.Win32
|
||||
/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
|
||||
*******************************************************************************
|
||||
|
2
Doxyfile
2
Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME = libUPnP
|
||||
# This could be handy for archiving the generated documentation or
|
||||
# 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)
|
||||
# base path where the generated documentation will be put.
|
||||
|
4
THANKS
4
THANKS
@@ -23,6 +23,7 @@ exempt of errors.
|
||||
- Craig Nelson
|
||||
- David Blanchet
|
||||
- David Maass
|
||||
- Dirk (dirk_vdb)
|
||||
- Emil Ljungdahl
|
||||
- Erik Johansson
|
||||
- Eric Tanguy
|
||||
@@ -37,6 +38,7 @@ exempt of errors.
|
||||
- Ingo Hofmann
|
||||
- Ivan Romanov (ivanromanov)
|
||||
- Jiri Zouhar
|
||||
- Jean-Francois Dockes (medoc)
|
||||
- John Dennis
|
||||
- Jonathan Casiot (no_dice)
|
||||
- Josh Carroll
|
||||
@@ -56,12 +58,14 @@ exempt of errors.
|
||||
- Paul Vixie
|
||||
- Peng
|
||||
- Peter Hartley
|
||||
- Philipp Matthias Hahn
|
||||
- Pino Toscano (pinotree)
|
||||
- Rene Hexel
|
||||
- Robert Buckley (rbuckley)
|
||||
- Robert Gingher (robsbox)
|
||||
- Ronan Menard
|
||||
- Sebastian Brandt
|
||||
- Shaun Marko (semarko)
|
||||
- Siva Chandran
|
||||
- Stefan Sommerfeld (zerocom)
|
||||
- Stéphane Corthésy
|
||||
|
@@ -105,7 +105,7 @@
|
||||
#define PACKAGE_NAME "libupnp"
|
||||
|
||||
/* 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 PACKAGE_TARNAME "libupnp"
|
||||
@@ -114,7 +114,7 @@
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* 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
|
||||
your system. */
|
||||
@@ -172,13 +172,13 @@
|
||||
#define UPNP_VERSION_MINOR 6
|
||||
|
||||
/* see upnpconfig.h */
|
||||
#define UPNP_VERSION_PATCH 19
|
||||
#define UPNP_VERSION_PATCH 20
|
||||
|
||||
/* see upnpconfig.h */
|
||||
#define UPNP_VERSION_STRING "1.6.19"
|
||||
#define UPNP_VERSION_STRING "1.6.20"
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "1.6.19"
|
||||
#define VERSION "1.6.20"
|
||||
|
||||
/* File Offset size */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
@@ -40,7 +40,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
/** 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 */
|
||||
#define UPNP_VERSION_MAJOR 1
|
||||
@@ -49,7 +49,7 @@
|
||||
#define UPNP_VERSION_MINOR 6
|
||||
|
||||
/** 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 */
|
||||
#define UPNP_VERSION \
|
||||
|
13
configure.ac
13
configure.ac
@@ -9,7 +9,7 @@
|
||||
|
||||
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 # *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:
|
||||
@@ -341,6 +341,16 @@ dnl #AC_SUBST([LT_VERSION_THREADUTIL], [6:4:0])
|
||||
dnl #AC_SUBST([LT_VERSION_UPNP], [9:3:3])
|
||||
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_THREADUTIL], [6:4:0])
|
||||
AC_SUBST([LT_VERSION_UPNP], [9:3:3])
|
||||
@@ -715,6 +725,7 @@ AC_OUTPUT
|
||||
# Files copied for windows compilation.
|
||||
#
|
||||
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
|
||||
echo "configure: copying \"upnp/inc/upnpconfig.h\" to \"build/inc/upnpconfig.h\""
|
||||
cp upnp/inc/upnpconfig.h build/inc/upnpconfig.h
|
||||
|
@@ -7,7 +7,8 @@
|
||||
|
||||
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 =
|
||||
|
||||
LDADD = libixml.la
|
||||
|
@@ -1,4 +1,4 @@
|
||||
Version: 1.6.19
|
||||
Version: 1.6.20
|
||||
Summary: Universal Plug and Play (UPnP) SDK
|
||||
Name: libupnp
|
||||
Release: 1%{?dist}
|
||||
|
@@ -4,7 +4,8 @@
|
||||
# (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
|
||||
AM_CPPFLAGS += -DDEBUG -DSTATS
|
||||
|
@@ -154,9 +154,10 @@ libupnp_la_SOURCES += \
|
||||
|
||||
|
||||
# check / distcheck tests
|
||||
check_PROGRAMS = test_init
|
||||
TESTS = test_init
|
||||
check_PROGRAMS = test_init test_url
|
||||
TESTS = test_init test_url
|
||||
test_init_SOURCES = test/test_init.c
|
||||
test_url_SOURCES = test/test_url.c
|
||||
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@@ -43,9 +43,7 @@
|
||||
#endif /* WIN32 */
|
||||
|
||||
/* strndup() is a GNU extension. */
|
||||
#if HAVE_STRNDUP && !defined(WIN32)
|
||||
extern char *strndup(__const char *__string, size_t __n);
|
||||
#else /* HAVE_STRNDUP && !defined(WIN32) */
|
||||
#if !HAVE_STRNDUP || defined(WIN32)
|
||||
static char *strndup(const char *__string, size_t __n)
|
||||
{
|
||||
size_t strsize = strnlen(__string, __n);
|
||||
|
@@ -480,25 +480,20 @@ int genaInitNotify(
|
||||
}
|
||||
*reference_count = 0;
|
||||
|
||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
||||
UDN_copy = strdup(UDN);
|
||||
if (UDN_copy == NULL) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
goto ExitFunction;
|
||||
}
|
||||
|
||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
||||
servId_copy = strdup(servId);
|
||||
if (servId_copy == NULL) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
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();
|
||||
|
||||
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
|
||||
@@ -639,25 +634,20 @@ int genaInitNotifyExt(
|
||||
}
|
||||
*reference_count = 0;
|
||||
|
||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
||||
UDN_copy = strdup(UDN);
|
||||
if (UDN_copy == NULL) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
goto ExitFunction;
|
||||
}
|
||||
|
||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
||||
servId_copy = strdup(servId);
|
||||
if( servId_copy == NULL ) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
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();
|
||||
|
||||
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
|
||||
@@ -798,25 +788,20 @@ int genaNotifyAllExt(
|
||||
}
|
||||
*reference_count = 0;
|
||||
|
||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
||||
UDN_copy = strdup(UDN);
|
||||
if (UDN_copy == NULL) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
goto ExitFunction;
|
||||
}
|
||||
|
||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
||||
servId_copy = strdup(servId);
|
||||
if( servId_copy == NULL ) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
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);
|
||||
if (propertySet == NULL) {
|
||||
line = __LINE__;
|
||||
@@ -944,25 +929,20 @@ int genaNotifyAll(
|
||||
}
|
||||
*reference_count = 0;
|
||||
|
||||
UDN_copy = (char *)malloc(strlen(UDN) + 1);
|
||||
UDN_copy = strdup(UDN);
|
||||
if (UDN_copy == NULL) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
goto ExitFunction;
|
||||
}
|
||||
|
||||
servId_copy = (char *)malloc(strlen(servId) + 1);
|
||||
servId_copy = strdup(servId);
|
||||
if( servId_copy == NULL ) {
|
||||
line = __LINE__;
|
||||
ret = UPNP_E_OUTOF_MEMORY;
|
||||
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);
|
||||
if (ret != XML_SUCCESS) {
|
||||
line = __LINE__;
|
||||
|
@@ -481,13 +481,12 @@ int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
|
||||
memset(Chunk_Header, 0,
|
||||
sizeof(Chunk_Header));
|
||||
rc = snprintf(Chunk_Header,
|
||||
sizeof(Chunk_Header) - strlen ("\r\n"),
|
||||
"%" PRIzx, num_read);
|
||||
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header) - strlen ("\r\n")) {
|
||||
sizeof(Chunk_Header),
|
||||
"%" PRIzx "\r\n", num_read);
|
||||
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header)) {
|
||||
RetVal = UPNP_E_INTERNAL_ERROR;
|
||||
goto Cleanup_File;
|
||||
}
|
||||
strncat(Chunk_Header, "\r\n", strlen ("\r\n"));
|
||||
/* Copy the chunk size header */
|
||||
memcpy(file_buf - strlen(Chunk_Header),
|
||||
Chunk_Header,
|
||||
|
@@ -88,14 +88,19 @@ static const char *Http4xxStr =
|
||||
"Unsupported Media Type\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 *Http5xxStr =
|
||||
"Internal Server Error\0"
|
||||
"Not Implemented\0"
|
||||
"Bad Gateway\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;
|
||||
|
||||
|
@@ -129,7 +129,7 @@ static const char *gMediaTypes[] = {
|
||||
#define TEXT_INDEX 5
|
||||
|
||||
/* general */
|
||||
#define NUM_MEDIA_TYPES 69
|
||||
#define NUM_MEDIA_TYPES 70
|
||||
#define NUM_HTTP_HEADER_NAMES 33
|
||||
|
||||
#define ASCTIME_R_BUFFER_SIZE 26
|
||||
@@ -156,6 +156,7 @@ static const char *gEncodedMediaTypes =
|
||||
"au\0" AUDIO_STR "basic\0"
|
||||
"avi\0" VIDEO_STR "msvideo\0"
|
||||
"bmp\0" IMAGE_STR "bmp\0"
|
||||
"css\0" TEXT_STR "css\0"
|
||||
"dcr\0" APPLICATION_STR "x-director\0"
|
||||
"dib\0" IMAGE_STR "bmp\0"
|
||||
"dir\0" APPLICATION_STR "x-director\0"
|
||||
@@ -792,11 +793,9 @@ static int CreateHTTPRangeResponseHeader(
|
||||
Instr->ReadSendSize = FileLength;
|
||||
if (!ByteRangeSpecifier)
|
||||
return HTTP_BAD_REQUEST;
|
||||
RangeInput = malloc(strlen(ByteRangeSpecifier) + 1);
|
||||
RangeInput = strdup(ByteRangeSpecifier);
|
||||
if (!RangeInput)
|
||||
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 */
|
||||
if (StrStr(RangeInput, "bytes") == NULL ||
|
||||
(Ptr = StrStr(RangeInput, "=")) == NULL) {
|
||||
|
@@ -387,7 +387,7 @@ static int parse_hostport(
|
||||
|
||||
ret = getaddrinfo(srvname, NULL, &hints, &res0);
|
||||
if (ret == 0) {
|
||||
for (res = res0; res && !ret; res = res->ai_next) {
|
||||
for (res = res0; res; res = res->ai_next) {
|
||||
switch (res->ai_family) {
|
||||
case AF_INET:
|
||||
case AF_INET6:
|
||||
@@ -395,12 +395,10 @@ static int parse_hostport(
|
||||
memcpy(&out->IPaddress,
|
||||
res->ai_addr,
|
||||
res->ai_addrlen);
|
||||
ret=1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
found:
|
||||
freeaddrinfo(res0);
|
||||
if (res == NULL)
|
||||
/* 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 *copyFrom = in;
|
||||
char *max = in + size;
|
||||
char **Segments = NULL;
|
||||
int lastSegment = -1;
|
||||
char *in = buf;
|
||||
char *out = buf;
|
||||
char *max = buf + size;
|
||||
|
||||
Segments = malloc( sizeof( char * ) * size );
|
||||
while (!is_end_path(in[0])) {
|
||||
assert (buf <= out);
|
||||
assert (out <= in);
|
||||
assert (in < max);
|
||||
|
||||
if( Segments == NULL )
|
||||
return UPNP_E_OUTOF_MEMORY;
|
||||
|
||||
Segments[0] = NULL;
|
||||
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
||||
"REMOVE_DOTS: before: %s\n", in );
|
||||
while( ( copyFrom < max ) && ( *copyFrom != '?' )
|
||||
&& ( *copyFrom != '#' ) ) {
|
||||
|
||||
if( ( ( *copyFrom ) == '.' )
|
||||
&& ( ( copyFrom == in ) || ( *( copyFrom - 1 ) == '/' ) ) ) {
|
||||
if( ( copyFrom + 1 == max )
|
||||
|| ( *( copyFrom + 1 ) == '/' ) ) {
|
||||
|
||||
copyFrom += 2;
|
||||
continue;
|
||||
} else if( ( *( copyFrom + 1 ) == '.' )
|
||||
&& ( ( copyFrom + 2 == max )
|
||||
|| ( *( copyFrom + 2 ) == '/' ) ) ) {
|
||||
copyFrom += 3;
|
||||
|
||||
if( lastSegment > 0 ) {
|
||||
copyTo = Segments[--lastSegment];
|
||||
} else {
|
||||
free( Segments );
|
||||
/*TRACE("ERROR RESOLVING URL, ../ at ROOT"); */
|
||||
return UPNP_E_INVALID_URL;
|
||||
}
|
||||
continue;
|
||||
/* case 2.A: */
|
||||
if (strncmp(in, "./", 2) == 0) {
|
||||
in += 2;
|
||||
} else if (strncmp(in, "../", 3) == 0) {
|
||||
in += 3;
|
||||
/* case 2.B: */
|
||||
} else if (strncmp(in, "/./", 3) == 0) {
|
||||
in += 2;
|
||||
} else if (strncmp(in, "/.", 2) == 0 && is_end_path(in[2])) {
|
||||
in += 1;
|
||||
in[0] = '/';
|
||||
/* case 2.C: */
|
||||
} else if (strncmp(in, "/../", 4) == 0 || (strncmp(in, "/..", 3) == 0 && is_end_path(in[3]))) {
|
||||
/* Make the next character in the input buffer a '/': */
|
||||
if (is_end_path(in[3])) { /* terminating "/.." case */
|
||||
in += 2;
|
||||
in[0] = '/';
|
||||
} else { /* "/../" prefix case */
|
||||
in += 3;
|
||||
}
|
||||
}
|
||||
|
||||
if( ( *copyFrom ) == '/' ) {
|
||||
|
||||
lastSegment++;
|
||||
Segments[lastSegment] = copyTo + 1;
|
||||
}
|
||||
( *copyTo ) = ( *copyFrom );
|
||||
copyTo++;
|
||||
copyFrom++;
|
||||
}
|
||||
if( copyFrom < max ) {
|
||||
while( copyFrom < max ) {
|
||||
( *copyTo ) = ( *copyFrom );
|
||||
copyTo++;
|
||||
copyFrom++;
|
||||
/* Trim the last component from the output buffer, or empty it. */
|
||||
while (buf < out)
|
||||
if (*--out == '/')
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
if (out < in)
|
||||
out[0] = '\0';
|
||||
#endif
|
||||
/* case 2.D: */
|
||||
} else if (strncmp(in, ".", 1) == 0 && is_end_path(in[1])) {
|
||||
in += 1;
|
||||
} else if (strncmp(in, "..", 2) == 0 && is_end_path(in[2])) {
|
||||
in += 2;
|
||||
/* case 2.E */
|
||||
} else {
|
||||
/* 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;
|
||||
free( Segments );
|
||||
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
|
||||
"REMOVE_DOTS: after: %s\n", in );
|
||||
while (in < max)
|
||||
*out++ = *in++;
|
||||
if (out < max)
|
||||
out[0] = '\0';
|
||||
return UPNP_E_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -582,115 +593,113 @@ char *resolve_rel_url(char *base_url, char *rel_url)
|
||||
{
|
||||
uri_type base;
|
||||
uri_type rel;
|
||||
int rv;
|
||||
|
||||
size_t i = (size_t)0;
|
||||
char *finger = NULL;
|
||||
|
||||
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
|
||||
if (!base_url) {
|
||||
if (!rel_url)
|
||||
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;
|
||||
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 ) {
|
||||
|
||||
if( rel.type == ( enum uriType) ABSOLUTE ) {
|
||||
|
||||
strncpy( out, rel_url, strlen ( rel_url ) );
|
||||
} else {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* path */
|
||||
char *path = out_finger;
|
||||
if (rel.path_type == (enum pathType)ABS_PATH) {
|
||||
rv = snprintf(out_finger, len, "%s", rel_url);
|
||||
} else if (base.pathquery.size == (size_t)0) {
|
||||
rv = snprintf(out_finger, len, "/%s", rel_url);
|
||||
} else {
|
||||
free(out);
|
||||
/* free(rel_url); */
|
||||
return NULL;
|
||||
}
|
||||
if (rel.pathquery.size == (size_t)0) {
|
||||
rv = snprintf(out_finger, len, "%.*s", (int)base.pathquery.size, base.pathquery.buff);
|
||||
} 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;
|
||||
|
||||
error:
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -79,6 +79,10 @@
|
||||
#define HTTP_SERVICE_UNAVAILABLE 503
|
||||
#define HTTP_GATEWAY_TIMEOUT 504
|
||||
#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 */
|
||||
|
||||
|
@@ -48,6 +48,9 @@
|
||||
#include "unixutil.h"
|
||||
#include "upnpapi.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
@@ -57,12 +60,15 @@
|
||||
|
||||
#define SREQ_HDR_NOT_FOUND -1
|
||||
#define SREQ_BAD_HDR_FORMAT -2
|
||||
#define SREQ_NOT_EXTENDED -3
|
||||
|
||||
#define SOAP_INVALID_ACTION 401
|
||||
#define SOAP_INVALID_ARGS 402
|
||||
#define SOAP_OUT_OF_SYNC 403
|
||||
#define SOAP_INVALID_VAR 404
|
||||
#define SOAP_ACTION_FAILED 501
|
||||
#define SOAP_MEMORY_OUT 603
|
||||
|
||||
|
||||
static const char *SOAP_BODY = "Body";
|
||||
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_Action_Failed = "Action Failed";
|
||||
static const char *Soap_Invalid_Var = "Invalid Var";
|
||||
static const char *Soap_Memory_out = "Out of Memory";
|
||||
|
||||
/*!
|
||||
* \brief This function retrives the name of the SOAP action.
|
||||
*
|
||||
* \return 0 if successful else returns appropriate error.
|
||||
*/
|
||||
static UPNP_INLINE int get_request_type(
|
||||
/*! [in] HTTP request. */
|
||||
http_message_t *request,
|
||||
/*! [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;
|
||||
typedef struct soap_devserv_t {
|
||||
char dev_udn[NAME_SIZE];
|
||||
char service_type[NAME_SIZE];
|
||||
char service_id[NAME_SIZE];
|
||||
memptr action_name;
|
||||
Upnp_FunPtr callback;
|
||||
void *cookie;
|
||||
}soap_devserv_t;
|
||||
|
||||
/* 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.
|
||||
@@ -270,288 +212,6 @@ static UPNP_INLINE void send_var_query_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.
|
||||
@@ -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.
|
||||
* This functionality has been deprecated in the UPnP V1.0 architecture.
|
||||
*/
|
||||
static UPNP_INLINE void handle_query_variable(
|
||||
/*! [in] Socket info. */
|
||||
SOCKINFO *info,
|
||||
/*! [in] HTTP request. */
|
||||
http_message_t *request,
|
||||
/*! [in] Document containing the variable request SOAP message. */
|
||||
IXML_Document *xml_doc)
|
||||
/*! [in] Socket info. */
|
||||
SOCKINFO *info,
|
||||
/*! [in] HTTP Request. */
|
||||
http_message_t *request,
|
||||
/*! [in] SOAP device/service information. */
|
||||
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;
|
||||
const char *err_str;
|
||||
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;
|
||||
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);
|
||||
variable.CurrentVal = NULL;
|
||||
variable.CtrlPtIPAddr = info->foreign_sockaddr;
|
||||
variable.CurrentVal = NULL;
|
||||
|
||||
/* 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__,
|
||||
"Return from callback for var request\n");
|
||||
/* 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
|
||||
* action request and gives the call back to the device application.
|
||||
* \brief Handles the SOAP action request.
|
||||
*/
|
||||
static void handle_invoke_action(
|
||||
/*! [in] Socket info. */
|
||||
IN SOCKINFO *info,
|
||||
/*! [in] HTTP Request. */
|
||||
IN http_message_t *request,
|
||||
/*! [in] Name of the SOAP Action. */
|
||||
IN memptr action_name,
|
||||
/*! [in] Document containing the SOAP action request. */
|
||||
IN IXML_Document *xml_doc)
|
||||
/*! [in] Socket info. */
|
||||
SOCKINFO *info,
|
||||
/*! [in] HTTP Request. */
|
||||
http_message_t *request,
|
||||
/*! [in] SOAP device/service information. */
|
||||
soap_devserv_t *soap_info,
|
||||
/*! [in] Node containing the SOAP action request. */
|
||||
IXML_Node *req_node)
|
||||
{
|
||||
char save_char;
|
||||
IXML_Document *resp_node = NULL;
|
||||
IXML_Document *req_doc = NULL;
|
||||
struct Upnp_Action_Request action;
|
||||
Upnp_FunPtr soap_event_callback;
|
||||
void *cookie = NULL;
|
||||
int err_code;
|
||||
const char *err_str;
|
||||
|
||||
memptr action_name;
|
||||
action.ActionResult = NULL;
|
||||
DOMString act_node = NULL;
|
||||
|
||||
/* null-terminate */
|
||||
action_name = soap_info->action_name;
|
||||
save_char = action_name.buf[action_name.length];
|
||||
action_name.buf[action_name.length] = '\0';
|
||||
/* set default error */
|
||||
err_code = SOAP_INVALID_ACTION;
|
||||
err_str = Soap_Invalid_Action;
|
||||
/* 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;
|
||||
/* get device info for action event */
|
||||
err_code = get_device_info(request,
|
||||
0,
|
||||
xml_doc,
|
||||
info->foreign_sockaddr.ss_family,
|
||||
action.DevUDN,
|
||||
action.ServiceID,
|
||||
&soap_event_callback, &cookie);
|
||||
|
||||
if (err_code != UPNP_E_SUCCESS)
|
||||
}
|
||||
err_code = ixmlParseBufferEx(act_node, &req_doc);
|
||||
if (err_code != IXML_SUCCESS) {
|
||||
if (IXML_INSUFFICIENT_MEMORY == err_code) {
|
||||
err_code = SOAP_MEMORY_OUT;
|
||||
err_str = Soap_Memory_out;
|
||||
} else {
|
||||
err_code = SOAP_INVALID_ACTION;
|
||||
err_str = Soap_Invalid_Action;
|
||||
}
|
||||
goto error_handler;
|
||||
namecopy(action.ActionName, action_name.buf);
|
||||
linecopy(action.ErrStr, "");
|
||||
action.ActionRequest = resp_node;
|
||||
action.ActionResult = NULL;
|
||||
}
|
||||
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;
|
||||
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 (strlen(action.ErrStr) <= 0) {
|
||||
err_code = SOAP_ACTION_FAILED;
|
||||
@@ -817,17 +416,279 @@ static void handle_invoke_action(
|
||||
/* error handling and cleanup */
|
||||
error_handler:
|
||||
ixmlDocument_free(action.ActionResult);
|
||||
ixmlDocument_free(resp_node);
|
||||
ixmlDocument_free(req_doc);
|
||||
ixmlFreeDOMString(act_node);
|
||||
/* restore */
|
||||
action_name.buf[action_name.length] = save_char;
|
||||
if (err_code != 0)
|
||||
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
|
||||
* from the control point. This function will start processing the request.
|
||||
* It calls handle_invoke_action to handle the SOAP action.
|
||||
* from the control point. After HTTP processing, it calls handle_soap_request
|
||||
* to start SOAP processing.
|
||||
*/
|
||||
void soap_device_callback(
|
||||
/*! [in] Parsed request received by the device. */
|
||||
@@ -838,43 +699,74 @@ void soap_device_callback(
|
||||
SOCKINFO *info)
|
||||
{
|
||||
int err_code;
|
||||
const char *err_str;
|
||||
memptr action_name;
|
||||
IXML_Document *xml_doc = NULL;
|
||||
soap_devserv_t *soap_info = NULL;
|
||||
IXML_Node *req_node = NULL;
|
||||
|
||||
/* set default error */
|
||||
err_code = SOAP_INVALID_ACTION;
|
||||
err_str = Soap_Invalid_Action;
|
||||
|
||||
/* get device/service identified by the request-URI */
|
||||
soap_info = malloc(sizeof(soap_devserv_t));
|
||||
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 */
|
||||
if (!has_xml_content_type(request))
|
||||
if (!has_xml_content_type(request)) {
|
||||
err_code = HTTP_UNSUPPORTED_MEDIA_TYPE;
|
||||
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;
|
||||
}
|
||||
/* parse XML */
|
||||
err_code = ixmlParseBufferEx(request->entity.buf, &xml_doc);
|
||||
if (err_code != IXML_SUCCESS) {
|
||||
if (err_code == IXML_INSUFFICIENT_MEMORY)
|
||||
err_code = UPNP_E_OUTOF_MEMORY;
|
||||
if (IXML_INSUFFICIENT_MEMORY == err_code)
|
||||
err_code = HTTP_INTERNAL_SERVER_ERROR;
|
||||
else
|
||||
err_code = SOAP_ACTION_FAILED;
|
||||
err_str = "XML error";
|
||||
err_code = HTTP_BAD_REQUEST;
|
||||
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 */
|
||||
handle_query_variable(info, request, xml_doc);
|
||||
handle_query_variable(info, request, soap_info, req_node);
|
||||
else
|
||||
/* invoke action */
|
||||
handle_invoke_action(info, request, action_name, xml_doc);
|
||||
/* no error */
|
||||
err_code = 0;
|
||||
handle_invoke_action(info, request, soap_info, req_node);
|
||||
|
||||
error_handler:
|
||||
err_code = HTTP_OK;
|
||||
|
||||
error_handler:
|
||||
ixmlDocument_free(xml_doc);
|
||||
if (err_code != 0)
|
||||
send_error_response(info, err_code, err_str, request);
|
||||
free(soap_info);
|
||||
if (err_code != HTTP_OK) {
|
||||
http_SendStatusResponse(info, err_code, request->major_version,
|
||||
request->minor_version);
|
||||
}
|
||||
return;
|
||||
parser = parser;
|
||||
}
|
||||
|
@@ -143,15 +143,12 @@ static UPNP_INLINE int calc_alias(
|
||||
aliasPtr = alias + 1;
|
||||
else
|
||||
aliasPtr = alias;
|
||||
new_alias_len = root_len + strlen(temp_str) + strlen(aliasPtr);
|
||||
alias_temp = malloc(new_alias_len + (size_t)1);
|
||||
new_alias_len = root_len + strlen(temp_str) + strlen(aliasPtr) + (size_t)1;
|
||||
alias_temp = malloc(new_alias_len);
|
||||
if (alias_temp == NULL)
|
||||
return UPNP_E_OUTOF_MEMORY;
|
||||
memset(alias_temp, 0, new_alias_len + (size_t)1);
|
||||
strncpy(alias_temp, rootPath, root_len);
|
||||
alias_temp[root_len] = '\0';
|
||||
strncat(alias_temp, temp_str, strlen(temp_str));
|
||||
strncat(alias_temp, aliasPtr, strlen(aliasPtr));
|
||||
memset(alias_temp, 0, new_alias_len);
|
||||
snprintf(alias_temp, new_alias_len, "%s%s%s", rootPath, temp_str, aliasPtr);
|
||||
|
||||
*newAlias = alias_temp;
|
||||
return UPNP_E_SUCCESS;
|
||||
@@ -186,14 +183,10 @@ static UPNP_INLINE int calc_descURL(
|
||||
assert(ipPortStr != NULL && strlen(ipPortStr) > 0);
|
||||
assert(alias != NULL && strlen(alias) > 0);
|
||||
|
||||
len = strlen(http_scheme) + strlen(ipPortStr) + strlen(alias);
|
||||
if (len > ((size_t)LINE_SIZE - (size_t)1))
|
||||
len = strlen(http_scheme) + strlen(ipPortStr) + strlen(alias) + (size_t)1;
|
||||
if (len > (size_t)LINE_SIZE)
|
||||
return UPNP_E_URL_TOO_BIG;
|
||||
strncpy(descURL, http_scheme, strlen(http_scheme));
|
||||
descURL[strlen(http_scheme)] = '\0';
|
||||
strncat(descURL, ipPortStr, strlen(ipPortStr));
|
||||
strncat(descURL, alias, strlen(alias));
|
||||
descURL[len] = '\0';
|
||||
snprintf(descURL, len, "%s%s%s", http_scheme, ipPortStr, alias);
|
||||
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
|
||||
"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