Compare commits

..

16 Commits

Author SHA1 Message Date
Marcelo Roberto Jimenez
bc335f5df7 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-04 22:34:08 -02:00
Marcelo Roberto Jimenez
cf31814e55 Looks like I did something wrong two commits ago 2015-02-04 22:25:15 -02:00
Marcelo Roberto Jimenez
3b0fd070fd Update .gitignore 2015-02-04 22:18:23 -02:00
Marcelo Roberto Jimenez
9f69fb1b80 _LARGEFILE_SOURCE definition has changed in the last autotools 2015-02-01 22:31:30 -02:00
Jean-Francois Dockes
c124ae6507 Fix out-of-tree compilation: missing include directive and build
subdirectory

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.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2015-02-01 22:27:39 -02:00
Nick Leverton
51a01cdba1 Adjust NUM_MEDIA_TYPES to allow for the addition of css in commit 72460df7. 2014-10-02 20:34:58 +01:00
Marcelo Jimenez
6905714a7e Update the THANKS file
Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-26 13:59:38 -03:00
Dirk
72460df747 #121 Support css files in webserver
Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-26 13:49:43 -03:00
Philipp Matthias Hahn
a596abfbe3 Add unit test for parse_uri()
Tested through the public function UpnpResolveURL2().

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:59:04 -03:00
Philipp Matthias Hahn
fbbb24f406 Fix remove_dots()
Rewrite to handle all normal and abnormal examples mentioned in RFC 3986
section 5.4.
The previous implementation failed the following test cases:

'http://www.libupnp.org/path1/path1' | '#frag1' -> 'http://www.libupnp.org/path1/#frag1' != 'http://www.libupnp.org/path1/path1#frag1' (0)
'http://127.0.0.1:6544/getDeviceDesc' | 'CDS_Event' -> 'http://127.0.0.1:6544/CDS_EventDesc' != 'http://127.0.0.1:6544/CDS_Event' (0)
'http://localhost/b/c/d;p?q' | 'g' -> 'http://localhost/b/c/g;p' != 'http://localhost/b/c/g' (0)
'http://localhost/b/c/d;p?q' | 'g/' -> 'http://localhost/b/c/g/p' != 'http://localhost/b/c/g/' (0)
'http://localhost/b/c/d;p?q' | '?y' -> 'http://localhost/b/c/?yp' != 'http://localhost/b/c/d;p?y' (0)
'http://localhost/b/c/d;p?q' | '#s' -> 'http://localhost/b/c/#sp' != 'http://localhost/b/c/d;p?q#s' (0)
'http://localhost/b/c/d;p?q' | ';x' -> 'http://localhost/b/c/;xp' != 'http://localhost/b/c/;x' (0)
'http://localhost/b/c/d;p?q' | '.' -> 'http://localhost/b/c/.;p' != 'http://localhost/b/c/' (0)
'http://localhost/b/c/d;p?q' | './' -> 'http://localhost/b/c/p' != 'http://localhost/b/c/' (0)
'http://localhost/b/c/d;p?q' | '..' -> 'http://localhost/b/c/..p' != 'http://localhost/b/' (0)
'http://localhost/b/c/d;p?q' | '/./g' -> 'http://localhost/./g' != 'http://localhost/g' (0)
'http://localhost/b/c/d;p?q' | '/../g' -> 'http://localhost/../g' != 'http://localhost/g' (0)
'http://localhost/b/c/d;p?q' | 'g.' -> 'http://localhost/b/c/g.p' != 'http://localhost/b/c/g.' (0)
'http://localhost/b/c/d;p?q' | '.g' -> 'http://localhost/b/c/.gp' != 'http://localhost/b/c/.g' (0)

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:59:04 -03:00
Philipp Matthias Hahn
0508fb0d6e Fix resolve_rel_url()
This reworks commit 0edaf3361d, which
broke resolving relative url, where the relative URL is shorter than the
absolute URL:
    "http://127.0.0.1:6544/getDeviceDesc" + "CDS_Event"
    Wrong: "http://127.0.0.1:6544/CDS_EventDesc"
    Right: "http://127.0.0.1:6544/CDS_Event"

While reviewing that commit, improve code by:
1. Move the simple cases to the beginning of the function.
2. Keep track of the remaining target buffer size.
3. Fix URI concatenation with queries.
4. Fix URI concatenation with fragments.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Philipp Matthias Hahn
848d66e69d Fix broken strncat(..., strlen())
commit 0edaf3361d replaced several
malloc()+strcat() sequences with strncat() using strlen() on the
*source* string.
This is still vulnerable to overwrite the *target* buffer.

While reviewing this commit change the code to directly use snprintf()
for concatenating strings and check the length of the target buffer.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Philipp Matthias Hahn
04fb684323 Directly use strdup()
commit 0edaf3361d replaced several
malloc()+strcpy() sequences with memset()+strncpy() using strlen().
This doesn't improve security and introduced a bug URI handling.

While reviewing this commit change the code to directly use strdup()
instead of re-implementing it multiple times, as shortens the code and
thus improves readability.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Fabrice Fontaine
faaef39a3c Fix getaddrinfo() loop
Commit b116d10f did the following change:
    Use switch, int and sa_family_t with AF_INET in uri.c.

This breaks when getaddrinfo() only returns a single record, as in that
case the "break" only exits the switch statement and the loop-step
"res=res->ai_next" is still executed. After that "res == NULL" is
wrongly interpreted as not having found an AF_INET or AF_INET6 address.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Peng
a1193f385e 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

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-01-17 10:00:00 -02:00
Marcelo Roberto Jimenez
c20f2bd3a1 Homekeeping for the next release. 2013-11-15 14:16:13 -02:00
21 changed files with 817 additions and 723 deletions

10
.gitignore vendored
View File

@@ -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

View File

@@ -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
*******************************************************************************

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
Version: 1.6.19
Version: 1.6.20
Summary: Universal Plug and Play (UPnP) SDK
Name: libupnp
Release: 1%{?dist}

View File

@@ -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

View File

@@ -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 = \

View File

@@ -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);

View File

@@ -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__;

View File

@@ -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,

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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
View 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