diff --git a/libupnp/AUTHORS b/libupnp/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/libupnp/COPYING b/libupnp/COPYING new file mode 100644 index 0000000..e69de29 diff --git a/libupnp/ChangeLog b/libupnp/ChangeLog new file mode 100644 index 0000000..edff3c7 --- /dev/null +++ b/libupnp/ChangeLog @@ -0,0 +1,278 @@ +************************************************************************* +Release of version 1.4.0 +************************************************************************* + +2006-05-26 Oxy + + * defines in iasnprintf.h changed to work with GCC-version < 3 + +2006-05-22 Oxy + + * BSD-patch added (not tested yet on an BSD system) + +2006-05-19 Oxy + + * Patch added for bug: ixml parser colapsed on empty args (arg="") + +2006-05-18 Oxy + + * DSM-320 patch added (fetched from project MediaTomb) + * httpGet additons atch added, Added proxy support by introducing + UpnpOpenHttpGetProxy. UpnpOpenHttpGet now just calls + UpnpOpenHttpGetProxy with the proxy url set to NULL. + * Bugfix for typo ("\0" / "0") in ixmlparser.c + * Bugfix for M-Search packet + +************************************************************************* +FORK FROM DEAD libupnp +************************************************************************* + +2006-04-29 Rémi Turboult + + * THANKS: new file with list of contributors + + * upnp/src/gena/gena_device.c (respond_ok): add 'Content-Length: 0' + in subscription response. Patch by Chaos (Bug # 1455367). + +2006-04-08 Rémi Turboult + + * upnp/doc/UPnP_Programming_Guide.pdf: replace this document with + the one in libupnp-doc-1.2.1 because current CVS version + was corrupted. + +2006-04-06 + + * changes applied to several files to work under Sparc Solaris, temporarily + requiring a define SPARC_SOLARIS + +2006-04-03 Rémi Turboult + + * upnp/Makefile.am: install upnp samples in $(docdir)/examples + +2006-03-28 Rémi Turboult + + * configure.ac: add --with-docdir option to choose where documentation + is installed (or -without-docdir to not install the documentation) + +2006-03-27 Rémi Turboult + + * ixml/test: add simple test suite for xml parser + +2006-03-26 Rémi Turboult + + * ixml/src/ixmlparser.c (Parser_processCDSect): fix bug which prevents + CDATA sections which contain a 0 (zero) to be parsed (instead the + parsing of the whole document is aborted). Patch by Arno Willig + (Patch # 1432124). + + * configure.ac, upnp/Makefile.am: add "--disable-samples" configure + option, and move samples compilation from check_PROGRAMS to + noinst_PROGRAMS + +2006-03-25 Rémi Turboult + + * upnp/src/genlib/miniserver/miniserver.c (get_miniserver_sockets): + fix bug if new socket created has fd 0 (can only occur when stdin + has been closed). Patch by Oskar Liljeblad 2004-07-02 : + http://sourceforge.net/mailarchive/message.php?msg_id=8870528 + +2006-03-21 Rémi Turboult + + * upnp/test/test_init.c: add some version checks and exit if failure + +2006-03-05 Rémi Turboult + + * libupnp version 1.3.1 + + * upnp/inc/upnpconfig.h.in: add new define UPNP_VERSION_PATCH + + * upnp/test/test_init.c: add simple test to run during checks + + * upnp/inc/upnp.h: include "upnpdebug.h" only if debug enabled + in the library (else header file is not installed) + + * upnp/Makefile.am (libupnp_la_LDFLAGS): add inter-library libtool + dependencies between upnp and ixml / threadutil, so that programs + linking against upnp only still work. + +2006-03-04 Rémi Turboult + + * libupnp version 1.3.0 + +2006-03-03 Rémi Turboult + + * upnp/src/genlib/net/http/httpreadwrite.c (get_sdk_info): use + package version string from configure to set sdk info + + * upnp/Makefile.am: add sample/tvdevice/web/ files in EXTRA_DIST + + do not distribute generated upnpconfig.h file. + +2006-02-28 Rémi Turboult + + * upnp/src/inc/config.h, configure.ac: use only new defines + UPNP_HAVE_xx instead of INCLUDE_yyy_APIS and INTERNAL_WEB_SERVER + + * upnp/Makefile.am, ixml/Makefile.am: add -export-symbols-regex to + the librarie LDFLAGS in order to export only the symbols defined + in the API + +2006-02-27 Rémi Turboult + + * configure.ac: add libtool versions for the 3 libraries + + * ixml/src/ixml.c (copy_with_escape): add missing 'static' to function + + * threadutil/src/ThreadPool.c (SetSeed): add missing 'static' + +2006-02-26 Rémi Turboult + + * threadutil/inc/iasnprintf.h: add gcc __printf__ format attribute + to "iasnprintf" + + * upnp/src/api/upnpapi.c: fix invalid UpnpPrintf formats + + * upnp/src/gena/gena_device.c: fix invalid UpnpPrintf formats + + * upnp/src/inc/config.h: move upnp/inc/config.h to internal + sources (this file is no longer installed with the libraries) + + * upnp/inc/upnpdebug.h: new file created from debug definitions + previously in upnp/inc/config.h + + * upnp/src/api/config.c: rename to upnp/src/api/upnpdebug.c + + * upnp/inc/upnpconfig.h.in: new file to contain information on + the configuration of the installed libraries (generates installed + file ) + +2006-02-22 Rémi Turboult + + * upnp/ : add missing include of config.h in some .c files + +2006-02-21 Rémi Turboult + + * upnp/inc/upnp.h: move some definitions which should not be + exported into "upnp/src/inc/util.h" + + * import all modifications below from libupnp in djmount 0.51 + into official libupnp + +2006-01-17 Rémi Turboult + + * threadutil/Makefile.am (libthreadutil_la_SOURCES): remove extraneous + file + +2006-01-15 Rémi Turboult + + * configure.ac: add checks for large-file support + + * upnp/inc/config.h: rename to "upnpconfig.h". The new "config.h" file + is the one generated by autoconf. + + * m4/type_socklen_t.m4: added new check for socklen_t (fallback to + int if not defined) + + * upnp/src/genlib/miniserver/miniserver.c, + upnp/src/ssdp/ssdp_server.c: use socklen_t where appropriate + (instead of int) + + * upnp/src/genlib/net/http/httpreadwrite.c (get_sdk_info): remove + XINLINE declaration (unused and too late) + + * ixml/src/node.c (ixmlNode_getNodeType): fix compilation warning + on const return type + +2006-01-12 Rémi Turboult + + * upnp/src/inc/readwrite.h : suppress unused C++ header file + +2006-01-11 Rémi Turboult + + * upnp/inc/config.h, upnp/src/inc/upnpapi.h, + upnp/src/inc/httpreadwrite.h: remove internal configuration variable + MINIMUM_DELAY (no clear purpose) + +2005-12-05 Rémi Turboult + + * upnp/inc/upnp.h: re-declare obsolete method UpnpSetContentLength, + for binary compatibility with previous libupnp version. + + * upnp/src/api/upnpapi.c: correct type of g_maxContentLength to size_t + +2005-11-01 Rémi Turboult + + * autoconfiscate library : replace all makefiles by Makefile.am + for automake support, + preliminary autoconf support + (generated config.h not yet used in source files) + +2005-10-18 Rémi Turboult + + * upnp/src/makefile: fix location of DEBUG STATIC libupnp library + + * upnp/sample/tvctrlpt/linux/Makefile, + upnp/sample/tvdevice/linux/Makefile: fix STATIC library support + +2005-10-16 Rémi Turboult + + * threadutil/src/Makefile (clean): remove built library + +2005-08-28 Rémi Turboult + + * ixml/src/ixml.h, ixml/src/ixml.c (ixmlRelaxParser) : new function + + * ixml/src/ixmlparser.h, ixml/src/ixmlparser.c (Parser_setErrorChar) : + new function + +2005-08-02 Rémi Turboult + + * ixml/src/Makefile: correct bug for static library being incorrectly + stripped when building non-debug + +2005-06-09 Rémi Turboult + + * ixml/src/element.c (ixmlElement_removeAttributeNode): + remove some compilation warning + + * ixml/inc/ixml.h, ixml/src/document.c : + add some missing const's in public API + + * upnp/inc/upnptools.h, upnp/src/api/upnptools.c : + add missing const's in public API + +2005-05-28 Rémi Turboult + + * upnp/inc/config.h: suppress HTTP_READ_BYTES (unused) + and replace by DEFAULT_SOAP_CONTENT_LENGTH (previously in upnpapi.h) + + * upnp/inc/upnp.h, upnp/src/api/upnpapi.c : replace + UpnpSetContentLength (which was not using its Handle argument) + by global function UpnpSetMaxContentLength. + Remove "hard" limitation to 32K (not suitable for using in UPnP AV). + + * upnp/src/inc/upnpapi.h : removed DEFAULT_SOAP_CONTENT_LENGTH + (moved to config.h) and MAX_SOAP_CONTENT_LENGTH (now unused) + + * upnp/src/api/upnptools.c : add more error message strings + + * upnp/src/genlib/net/http/httpreadwrite.c : return OUTOF_BOUNDS + instead of BAD_HTTPMSG when allowed Content Length is exceeded. + + * upnp/src/genlib/net/http/httpreadwrite.c : corrected an incorrect + sprintf format + +2005-05-27 Rémi Turboult + + * upnp/makefile, upnp/src/makefile, + ixml/Makefile, ixml/src/Makefile, + threadutil/Makefile, threadutil/src/Makefile : + implement STATIC library support (from patch at + http://sourceforge.net/tracker/?group_id=7189&atid=307189 ) + +2005-05-26 Rémi Turboult + + * upnp/src/api/upnpapi.c, upnp/src/soap/soap_device.c, + upnp/src/soap/makefile : + corrections for compilation with CLIENT=1 only + + * importing "libupnp-1.2.1a" as baseline + diff --git a/libupnp/INSTALL b/libupnp/INSTALL new file mode 100644 index 0000000..e69de29 diff --git a/libupnp/LICENSE b/libupnp/LICENSE new file mode 100644 index 0000000..d0d55c3 --- /dev/null +++ b/libupnp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2000-2003 Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/libupnp/Makefile.am b/libupnp/Makefile.am new file mode 100644 index 0000000..5a85a4f --- /dev/null +++ b/libupnp/Makefile.am @@ -0,0 +1,37 @@ +# $Id: Makefile.am,v 1.5 2006/04/29 20:12:50 r3mi Exp $ +# +# Top-level "Makefile.am" for libupnp +# +# Copyright (C) 2005 Rémi Turboult +# + +ACLOCAL_AMFLAGS = -I m4 + +DISTCHECK_CONFIGURE_FLAGS = --enable-debug --enable-samples + +SUBDIRS = ixml threadutil upnp + + +EXTRA_DIST = libupnp.pc.in LICENSE THANKS + + +# This variable must have 'exec' in its name, in order to be installed +# by 'install-exec' target (instead of default 'install-data') +pkgconfigexecdir = $(libdir)/pkgconfig +pkgconfigexec_DATA = libupnp.pc + +$(pkgconfigexec_DATA): config.status + + +if WITH_DOCDIR + docdir = @DOCDIR@ + doc_DATA = LICENSE README NEWS TODO THANKS +endif + + +CLEANFILES = IUpnpErrFile.txt IUpnpInfoFile.txt + + + + + diff --git a/libupnp/NEWS b/libupnp/NEWS new file mode 100644 index 0000000..aedbfd3 --- /dev/null +++ b/libupnp/NEWS @@ -0,0 +1,84 @@ + +What is new in libupnp 1.3.1 + +* fix: "upnp.h" includes "upnpdebug.h" only if debug enabled in the library + (else header file is not installed) + +* fix: add inter-library dependencies between upnp and ixml / threadutil, + so that programs linking against upnp only still work. + +============================================================================ + +What is new in libupnp 1.3.0 + +* major change: autoconfiscate build system. automake + autoconf replace the + previous makefiles. This should allow for easier build and installation on + various distributions. + +* change: optional library features are selected with configure options + (such as "./configure --enable-debug --disable-device") : see README file + for the main options, and "./configure --help" to display a complete list. + +* new: install a pkgconfig file "libupnp.pc" + +* new: a new installed file provides macros to know + the installed library version, and the optional features which have + been configured in the library. + +* change: the old included file "config.h", which contained internal + definitions needed to compile the library, is no longer installed in + +* change: the debug definitions previously available in + are now available in (only if library configured with + debug enabled). + +* change: add libtool versionning for the 3 libraries. + Also hide all library symbols not part of the public API. + +* change: remove "hard" limit to 32K in UpnpSetContentLength + (not suitable for UPnP AV clients). + +* new: new "UpnpSetMaxContentLength" function to globally set the maximum + incoming content-length that the SDK will process (should be used instead + of UpnpSetContentLength, which does not uses its handle argument) + +* change: returns OUTOF_BOUNDS instead of BAD_HTTPMSG when exceed allowed + Content Length + +* new: ixml: new function ixmlRelaxParser to make the XML parser more tolerant + to malformed text, if required (default behaviour is unchanged : abort + on error) + +* fix: compilation error with gcc4 + +* fix: add some missing const's in public API + +* fix: add check for availability of socklen_t type + +* fix: miscellaneous bugs and warnings (see details in ChangeLog) + +============================================================================ + +Changes to the SDK for UPnP Devices version 1.2.1a: + +- Changes the NAME_SIZE constant used for URL buffers to 256 bytes to + accomodate longer URLs. + +============================================================================ + +Changes to the SDK for UPnP Devices version 1.2.1: + +- Integrates an entirely new XML parser that features DOM2 API support and + a much smaller code size. +- Integrates a new threading utility library that manages all threads in + the library. +- Elimination of C++ and other code optimizations have reduced the binary + size by over 60%. +- The web server now supports application-level callbacks to handle + dynamically generated data. +- The web server now correctly handles chunked encoding. +- A new client HTTP API has been added that allows downloading of items + of unlimited size. +- The SDK supports much better cross-compilation support. +- Numerous memory leaks and bugs have been fixed. + diff --git a/libupnp/README b/libupnp/README new file mode 100644 index 0000000..c3f104a --- /dev/null +++ b/libupnp/README @@ -0,0 +1,294 @@ +Linux* SDK for UPnP* Devices (libupnp) + +Copyright (c) 2000-2003 Intel Corporation - All Rights Reserved. +Copyright (c) 2005-2006 Rémi Turboult + +See LICENSE for details. + +This file contains information about the above product in the following +sections: + +1. Release Contents +2. Package Contents +3. System Requirements +4. Build Instructions +5. Install/Uninstall Instructions +6. Product Release Notes +7. New Features +8. Support and Contact Information + + +1) Release Contents +------------------------------------------- + +The Linux SDK for UPnP Devices is an SDK for development of UPnP device +and control point applications for Linux. It consists of the core UPnP +protocols along with a UPnP-specific eXtensible Markup Language (XML) parser +supporting the Document Object Model (DOM) Level 2 API and an optional, +integrated mini web server for serving UPnP related documents. + + +2) Package Contents +------------------------------------------- + +The Linux SDK for UPnP Devices contains the following: + +README This file. Contains the installation and build instructions. +LICENSE The licensing terms the SDK is distributed under. +NEWS Changes and new features. +ixml\doc The files for generating the XML parser documentation from + the source code. +ixml\inc The public include files required to use the XML parser. +ixml\src The source code to the XML parser library. +threadutil\inc The public include files required to the threading + utility library. +threadutil\src The source code to the threading utility library. +upnp\doc The files for generating the SDK documentation from the + source code. +upnp\inc The public include files required to use the SDK. +upnp\src The source files comprising the SDK, libupnp.so. +upnp\sample A sample device and control point application, illustrating the + usage of the SDK. + + +3) System Requirements +------------------------------------------- + +The SDK for UPnP Devices is designed to compile and run under the +Linux operating system. It does, however, have dependencies on some +packages that may not be installed by default. All packages that it +requires are listed below. The name of the package and where it can be +found is dependent on the distribution of Linux being used. + +libpthread The header and library are installed as part of the glibc-devel + package (or equivalent). + +Additionally, the documentation for the SDK can be auto-generated from +the UPNP.H header file using DOC++, a documentation system for C, C++, +IDL, and Java*. DOC++ generates the documentation in HTML or TeX format. +Using some additional tools, the TeX output can be converted into a +PDF file. To generate the documentation these tools are required: + +DOC++ The homepage for DOC++ is http://docpp.sourceforge.net/. + The current version as of this release of the SDK is + version 3.4.9. DOC++ is the only requirement for generating + the HTML documentation. +LaTeX/TeX To generate the PDF documentation, LaTeX and TeX tools are + necessary. The tetex and tetex-latex packages provide these + tools. +dvips dvips converts the DVI file produced by LaTeX into a PostScript* + file. The tetex-dvips package provides this tool. +ps2pdf The final step to making the PDF is converting the PostStript + into Portable Document Format. The ghostscript package provides + this tool. + +For the UPnP library to function correctly, Linux networking must be configured +properly for multicasting. To do this: + +route add -net 239.0.0.0 netmask 255.0.0.0 eth0 + +where 'eth0' is the network adapter that the UPnP library will use. Without +this addition, device advertisements and control point searches will not +function. + + +libupnp has been built and tested on the following configurations: + +* MandrakeLinux 10.1 (kernel 2.6.8.1-12mdk, gcc 3.4.1, glibc-2.3.3) + +* Ubuntu 5.10 "Breezy Badger" (Linux kernel 2.6.12-9-386, gcc 4.0.2 20050808 + prerelease, libc6 2.3.5-1ubuntu12) + + + +4) Build Instructions +------------------------------------------- + +CORE LIBRARIES + +The in the examples below, replace $(LIBUPNP) with "libupnp-x.y.z", +with x, y, and z corresponding to the version of the library that you have. + +All pieces of the SDK are configured and built from the $(LIBUPNP) directory. + +% cd $(LIBUPNP) +% ./configure +% make + +will build a version of the binaries without debug support, and with default +options enabled (see below for options available at configure time). + + +To build the documentation, assuming all the necessary tools are installed +(see section 3) : + +To generate the HTML documentation: + +% cd $(LIBUPNP) +% make html + +To generate the PDF file: + +% cd $(LIBUPNP) +% make pdf + + +A few options are available at configure time. Use "./configure --help" +to display a complete list of options. Note that these options +may be combined in any order. +After installation, the file will provide a summary +of the optional features that have been included in the library. + + +% cd $(LIBUPNP) +% ./configure --enable-debug +% make + +will build a debug version with symbols support. + +To build the library with the optional, integrated mini web server (note +that this is the default): + +% cd $(LIBUPNP) +% ./configure --enable-webserver +% make + +To build without: + +% cd $(LIBUPNP) +% ./configure --disable-webserver +% make + + +The SDK for Linux also contains some additional helper APIs, declared in +inc/tools/upnptools.h. If these additional tools are not required, they can +be compiled out: + +% cd $(LIBUPNP) +% ./configure --disable-tools +% make + +By default, the tools are included in the library. + +To further remove code that is not required, the library can be build with or +with out the control point (client) or device specific code. To remove this +code: + +% cd $(LIBUPNP) +% ./configure --disable-client +% make + +to remove client only code or: + +% cd $(LIBUPNP) +% ./configure --disable-device +% make + +to remove device only code. + +By default, both client and device code is included in the library. The +integrated web server is automatically removed when configuring with +--disable-device. + +To build the library without large-file support (enabled by default) : + +% cd $(LIBUPNP) +% ./configure --disable-largefile +% make + + +To remove all the targets, object files, and built documentation: + +% cd $(LIBUPNP) +% make clean + + +CROSS COMPILATION + +To cross compile the SDK, a special "configure" directive is all that is +required: + +% cd $(LIBUPNP) +% ./configure --host=arm-linux +% make + +This will invoke the "arm-linux-gcc" cross compiler to build the library. + + +SAMPLES + +The SDK contains two samples: a TV device application and a control point +that talks with the TV device. They are found in the $(LIBUPNP)/upnp/sample +directory. + +To build the samples (note: this is the default behaviour): + +% cd $(LIBUPNP) +% ./configure --enable-samples +% make + +will build the sample device "$(LIBUPNP)/upnp/upnp_tv_device" and +sample control point "$(LIBUPNP)/upnp/upnp_tv_ctrlpt". +Note : the sample device won't be built if --disable-device has been +configured, and the sample control point won't be build if --disable-client +has been configured. + +To run the sample device, you need the "$(LIBUPNP)/upnp/sample/tvdevice/web" +sub-directory. Example : + +% cd $(LIBUPNP)/upnp/sample/tvdevice +% ../../upnp_tv_device + + +5) Install/Uninstall Instructions +------------------------------------------- + +Install + +The top-level makefile for the UPnP SDK contains rules to install the +necessary components. To install the SDK, as root: + +make install + +Uninstall + +Likewise, the top-level makefile contains an uninstall rule, reversing +the steps in the install: + +make uninstall + + +6) Product Release Notes +------------------------------------------- + +The SDK for UPnP Devices v1.2.1a has these known issues: + +- The UPnP library may not work with older versions of gcc and libstdc++, + causing a segmentation fault when the library loads. It is recommended + that gcc version 2.9 or later be used in building library. +- The UPnP library does not work the glibc 2.1.92-14 that ships with + Red Hat 7.0. For the library to function, you must updated the glibc + and glibc-devel packages to 2.1.94-3 or later. There is some issue with + libpthreads that has been resolved in the 2.1.94 version. + + +7) New Features +------------------------------------------- + +See NEWS file. + + +8) Support and Contact Information +------------------------------------------- + +Intel is not providing support for the Linux SDK for UPnP Devices. Two +mailing lists for the SDK are available on http://upnp.sourceforge.net/. +Questions and problems should be addressed on the appropriate mailing list. + +If you find this SDK useful, please send an email to upnp@intel.com and let +us know. + + +* Other brands, names, and trademarks are the property of their respective +owners. + diff --git a/libupnp/THANKS b/libupnp/THANKS new file mode 100644 index 0000000..fe174bf --- /dev/null +++ b/libupnp/THANKS @@ -0,0 +1,13 @@ + +libupnp was originally written by Intel Corporation. + +Many people further contributed to libupnp by reporting problems, +suggesting various improvements or submitting actual code. +Here is a list of these people. Help us keep it complete and +exempt of errors. + +Patches: Arno Willig, Oskar Liljeblad, Chaos, + Nektarios K. Papadopoulos, John Dennis, Jiri Zouhar, + Marcelo Jimenez +Solaris Port: Oxy +Hints and useful bugreports: Siva Chandran diff --git a/libupnp/TODO b/libupnp/TODO new file mode 100644 index 0000000..00c2dee --- /dev/null +++ b/libupnp/TODO @@ -0,0 +1,29 @@ + +To Be Done +========== + +- add FreeBSD patches + ( http://sf.net/tracker/index.php?func=detail&aid=1332618&group_id=7189&atid=307189 ?) + +- non-regression testing + +- replace doc++ by Doxygen for documentation generation + +- incorporate public patches and fix reported bugs : +http://sourceforge.net/tracker/?group_id=7189&atid=107189 and +http://sourceforge.net/tracker/?group_id=7189&atid=307189 + +- RPM packaging (a preliminary one here : + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=176617 ) + +- make API clean for large files and 64 bits + + +To Be Decided +============= + +- IPV6 support ? + + + + diff --git a/libupnp/bootstrap b/libupnp/bootstrap new file mode 100644 index 0000000..55d1a1b --- /dev/null +++ b/libupnp/bootstrap @@ -0,0 +1,19 @@ +#!/bin/sh + +rm -fr autom4te.cache + +# Equivalent to +# aclocal +# autoheader +# automake --add-missing --copy +# autoconf + +autoreconf --force --install -Wall -Wno-obsolete $* || exit 1 + +echo "Now run ./configure and then make." +exit 0 + + + + + diff --git a/libupnp/configure.ac b/libupnp/configure.ac new file mode 100644 index 0000000..23f2223 --- /dev/null +++ b/libupnp/configure.ac @@ -0,0 +1,193 @@ +# -*- Autoconf -*- +# $Id: configure.ac,v 1.11 2006/04/09 13:59:44 r3mi Exp $ +# +# Top-level configure.ac file for libupnp +# +# Process this file with autoconf to produce a configure script. +# +# (C) Copyright 2005-2006 Rémi Turboult +# + +AC_PREREQ(2.59) + +AC_INIT([libupnp], [1.4.0], [virtual_worlds@gmx.de]) +# *Independently* of the above libupnp package version, the libtool version +# of the 3 libraries need to be updated whenever there is a change released : +# "current:revision:age" (this is NOT the same as the package version), where: +# - library code modified: revision++ +# - interfaces changed/added/removed: current++ and revision=0 +# - interfaces added: age++ +# - interfaces removed: age=0 +# *please update only once, before a formal release, not for each change* +AC_SUBST([LT_VERSION_IXML], [2:1:0]) +AC_SUBST([LT_VERSION_THREADUTIL], [2:1:0]) +AC_SUBST([LT_VERSION_UPNP], [2:1:0]) + + +AC_CONFIG_AUX_DIR(config.aux) +AC_CONFIG_MACRO_DIR(m4) +AC_CONFIG_SRCDIR(upnp/inc/upnp.h) + +AM_INIT_AUTOMAKE([1.8 -Wall foreign subdir-objects]) + +# +# There are 3 configuration files : +# 1) "./autoconfig.h" is auto-generated and used only internally during build +# (usually named "config.h" but conflicts with the file below) +# 2) "./upnp/src/inc/config.h" is static and contains some compile-time +# parameters. This file was previously in "./upnp/inc" but is no longer +# installed (contains internal definitions only). +# 3) "./upnp/inc/upnpconfig.h" is auto-generated and installed with the +# libraries : it contains information on the configuration of the +# installed libraries. +# +AC_CONFIG_HEADERS([autoconfig.h upnp/inc/upnpconfig.h]) + +AC_REVISION([$Revision: 1.11 $]) + +upnpmaj=`echo "$PACKAGE_VERSION" | sed 's/\..*//' ` +upnpmin=[`echo "$PACKAGE_VERSION" | sed 's/^[^.]\.// ; s/[^0-9].*$//' `] +upnppatch=[`echo "$PACKAGE_VERSION" | sed 's/^[^.]\.[^.]\.// ; s/[^0-9].*$//' `] +AC_DEFINE_UNQUOTED([UPNP_VERSION_STRING], "$PACKAGE_VERSION", + [see upnpconfig.h]) +AC_DEFINE_UNQUOTED([UPNP_VERSION_MAJOR], $upnpmaj, [see upnpconfig.h]) +AC_DEFINE_UNQUOTED([UPNP_VERSION_MINOR], $upnpmin, [see upnpconfig.h]) +AC_DEFINE_UNQUOTED([UPNP_VERSION_PATCH], $upnppatch, [see upnpconfig.h]) + + +# +# Check for DEBUG flag +# +RT_BOOL_ARG_ENABLE([debug], [no], [extra debugging code]) +if test "x$enable_debug" = xyes ; then + AC_DEFINE(UPNP_HAVE_DEBUG, 1, [see upnpconfig.h]) + AC_DEFINE(DEBUG, 1, [Define to 1 to compile debug code]) +else + AC_DEFINE(NO_DEBUG, 1, [Define to 1 to prevent some debug code]) + AC_DEFINE(NDEBUG, 1, [Define to 1 to prevent compilation of assert()]) +fi + + +# +# Check for libupnp subsets +# + +RT_BOOL_ARG_ENABLE([client], [yes], [control point code (client)]) +if test "x$enable_client" = xyes ; then + AC_DEFINE(UPNP_HAVE_CLIENT, 1, [see upnpconfig.h]) +fi + + +RT_BOOL_ARG_ENABLE([device], [yes], + [device specific code (implies --disable-webserver if disabled)]) +if test "x$enable_device" = xyes ; then + AC_DEFINE(UPNP_HAVE_DEVICE, 1, [see upnpconfig.h]) +fi + + +if test "x$enable_device" = xno ; then + enable_webserver=no +else + RT_BOOL_ARG_ENABLE([webserver], [yes], [integrated web server]) +fi +AM_CONDITIONAL(ENABLE_WEBSERVER, test x"$enable_webserver" = xyes) +if test "x$enable_webserver" = xyes ; then + AC_DEFINE(UPNP_HAVE_WEBSERVER, 1, [see upnpconfig.h]) +fi + + +RT_BOOL_ARG_ENABLE([tools], [yes], [helper APIs in upnptools.h]) +if test "x$enable_tools" = xyes ; then + AC_DEFINE(UPNP_HAVE_TOOLS, 1, [see upnpconfig.h]) +fi + + +RT_BOOL_ARG_ENABLE([samples], [yes], [compilation of upnp/sample/ code]) + + +# doc installation +AC_MSG_CHECKING([documentation installation]) +AC_ARG_WITH([docdir], +AC_HELP_STRING([--with-docdir=DIR], + [where documentation is installed + @<:@[DATADIR/doc/]AC_PACKAGE_NAME[-]AC_PACKAGE_VERSION@:>@]) +AC_HELP_STRING([--without-docdir], + [do not install the documentation]), + [DOCDIR="$with_docdir"], + [DOCDIR="${datadir}/doc/${PACKAGE_NAME}-${PACKAGE_VERSION}"]) + +AM_CONDITIONAL(WITH_DOCDIR, test x"$with_docdir" != xno) +AC_SUBST(DOCDIR) +AC_MSG_RESULT($DOCDIR) + + +# +# Checks for programs +# +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_LIBTOOL +AC_PROG_INSTALL +AC_PROG_MAKE_SET + + +# +# Default compilation flags +# +if test x"$enable_debug" = xyes; then + # AC_PROG_CC already sets CFLAGS to "-g -O2" by default + : +else + # add optimise for size + AX_CFLAGS_GCC_OPTION([-Os]) +fi +AX_CFLAGS_WARN_ALL + +# Arrange for large-file support (can be disabled with --disable-largefile). +# Define _FILE_OFFSET_BITS and _LARGE_FILES if necessary +AC_SYS_LARGEFILE + + +# +# Checks for header files +# +AC_HEADER_STDC +# libupnp code doesn't use autoconf variables yet, +# so just abort if a header file is not found. +AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h malloc.h netdb.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h sys/timeb.h syslog.h unistd.h],[],[AC_MSG_ERROR([required header file missing])]) + + +# +# Checks for typedefs, structures, and compiler characteristics +# +AC_C_CONST +AC_TYPE_SIZE_T +TYPE_SOCKLEN_T + + +# +# Checks for library functions +# +AC_FUNC_VPRINTF +AC_FUNC_FSEEKO + + +# +# Checks for POSIX Threads +# +ACX_PTHREAD([],[AC_MSG_ERROR([POSIX threads are required to build this program])]) + + + + +AC_CONFIG_FILES([Makefile + ixml/Makefile + ixml/doc/Makefile + threadutil/Makefile + upnp/Makefile + upnp/doc/Makefile + libupnp.pc + ]) + +AC_OUTPUT + diff --git a/libupnp/ixml/Makefile.am b/libupnp/ixml/Makefile.am new file mode 100644 index 0000000..acf1f85 --- /dev/null +++ b/libupnp/ixml/Makefile.am @@ -0,0 +1,50 @@ +# $Id: Makefile.am,v 1.4 2006/03/27 21:27:31 r3mi Exp $ +# +# "Makefile.am" for "libupnp/ixml" +# +# (C) Copyright 2005 Rémi Turboult +# + +SUBDIRS = doc + +AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc +AM_CFLAGS = + +LDADD = libixml.la + +if ENABLE_DEBUG + AM_CPPFLAGS += -DDEBUG +else + AM_CPPFLAGS += -DNDEBUG +endif + +lib_LTLIBRARIES = libixml.la + +libixml_la_LDFLAGS = -version-info $(LT_VERSION_IXML) \ + -export-symbols-regex '^ixml.*' + +libixml_la_SOURCES = \ + src/ixml.c src/node.c src/ixmlparser.c \ + src/ixmlmembuf.c src/nodeList.c \ + src/element.c src/attr.c src/document.c \ + src/namedNodeMap.c \ + src/inc/ixmlmembuf.h src/inc/ixmlparser.h + +upnpincludedir = $(includedir)/upnp +upnpinclude_HEADERS = inc/ixml.h + + +check_PROGRAMS = test_document +TESTS = test/test_document.sh + +test_document_SOURCES = test/test_document.c + +EXTRA_DIST = test/test_document.sh test/testdata + +dist-hook: + rm -rf `find $(distdir)/test/testdata/ -type f \! -name '*ml' -print` + +clean-local: + @if [ -d bin ] ; then rm -rf bin ; fi + + diff --git a/libupnp/ixml/doc/Makefile.am b/libupnp/ixml/doc/Makefile.am new file mode 100644 index 0000000..1f01c63 --- /dev/null +++ b/libupnp/ixml/doc/Makefile.am @@ -0,0 +1,53 @@ +# $Id: Makefile.am,v 1.1.1.1 2006/02/18 13:47:30 r3mi Exp $ +# +# "Makefile.am" for "libunp/ixml/doc" +# +# (C) Copyright 2005 Rémi Turboult +# +########################################################################## +# +# Copyright (c) 2000-2003 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +########################################################################## + +EXTRA_DIST= docxx.sty intro.dxx ixml.dxx license.dxx + +html-local: + @if [ -d html ]; then rm -rf html; fi + @doc++ -nd -S -w -j -d html $(srcdir)/ixml.dxx + +pdf-local: + @doc++ -nd -S -w -j -t --package a4wide -o ixml.tex $(srcdir)/ixml.dxx + @-pdflatex "\scrollmode\input ixml.tex" > pdflatex.log + @-pdflatex "\scrollmode\input ixml.tex" >> pdflatex.log + @-pdflatex "\scrollmode\input ixml.tex" >> pdflatex.log + +clean-local: + -rm -rf html + -rm -f ixml.tex pdflatex.log ixml.log ixml.aux + diff --git a/libupnp/ixml/doc/docxx.sty b/libupnp/ixml/doc/docxx.sty new file mode 100644 index 0000000..4da4d39 --- /dev/null +++ b/libupnp/ixml/doc/docxx.sty @@ -0,0 +1,1083 @@ +% docxx.sty +% +% Copyright (c) 1996 Roland Wunderling, Malte Zoeckler +% Copyright (c) 1999-2001 Dragos Acostachioaie +% +% This file is part of DOC++. +% +% DOC++ is free software; you can redistribute it and/or +% modify it under the terms of the GNU General Public +% License as published by the Free Software Foundation; either +% version 2 of the license, or (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +% General Public License for more details. +% +% You should have received a copy of the GNU General Public +% License along with this program; if not, write to the Free +% Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +% +% +% Switch off special characters except {}\ for the rest of the text. +% +\def\cxxtilde{{\tt\~\relax}} +\addtolength{\parskip}{6pt} + +\catcode`\,=\active% +\def,{\char`\,\penalty-8\ } +%\def,{++ } +\catcode`\,=12 + +\def\<{{\tt <}} +\def\>{{\tt >}} +\def\TEX{} +\def\cxxExceptionsStr{} +\def\cxxParameterStr{} +\def\cxxReturnStr{} +\def\cxxInvariantsStr{} +\def\cxxPreconditionsStr{} +\def\cxxPostconditionsStr{} +\def\cxxSeeStr{} +\def\cxxAuthorStr{} +\def\cxxVersionStr{} +\def\cxxDeprecatedStr{} +\def\cxxSinceStr{} +\def\cxxFileStr{} +\def\cxxExceptions#1{\def\cxxExceptionsStr{#1}} +\def\cxxParameter#1{\def\cxxParameterStr{#1}} +\def\cxxReturn#1{\def\cxxReturnStr{#1}} +\def\cxxInvariants#1{\def\cxxInvariantsStr{#1}} +\def\cxxPreconditions#1{\def\cxxPreconditionsStr{#1}} +\def\cxxPostconditions#1{\def\cxxPostconditionsStr{#1}} +\def\cxxSee#1{\def\cxxSeeStr{#1}} +\def\cxxAuthor#1{\def\cxxAuthorStr{#1}} +\def\cxxVersion#1{\def\cxxVersionStr{#1}} +\def\cxxDeprecated#1{\def\cxxDeprecatedStr{#1}} +\def\cxxSince#1{\def\cxxSinceStr{#1}} +\def\cxxFile#1{\def\cxxFileStr{#1}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Define ... to do verbatim listing +% +\catcode`\=\active +\catcode`\=\active +{\obeyspaces\gdef {\ }} + +\def\ccverbatim{\strut\begingroup + \catcode`\\=12 \catcode`\{=12 + \catcode`\}=12 \catcode`\$=12 + \catcode`\&=12 \catcode`\#=12 + \catcode`\%=12 \catcode`\~=12 + \catcode`\_=12 \catcode`\^=12 + \catcode`\|=12 \catcode`\/=12 + \obeyspaces\tt} +\def{\let\par=\endgraf \ccverbatim \parskip=0pt \ccfinish} +{\catcode`\=0 catcode`\=12 +gdefccfinish#1{#1endgroup}} + +% +% Definition of structuring comands. +% +\newcommand{\Section}[1]{\section{#1}} +\newcommand{\SubSection}[1]{\subsection{#1}} +\newcommand{\SubSubSection}[1]{\subsubsection{#1}} +\newcommand{\Paragraph}[1]{\paragraph{#1}} + +\newcommand{\Ref}[1]{{\bf #1} ($\rightarrow$ \ref{#1})} +\newcommand{\URL}[2][]{% + \def\name{#1}% + \def\empty{}% + \ifx\name\empty% + {\tt #2}% + \else% + #1 ({\tt #2})% + \fi% +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% printing line #1 of code #2 +% +\newdimen\cxxcodewidth +\cxxcodewidth=\textwidth +\advance\cxxcodewidth by -21pt + +\def\cxxCodeLine#1#2{% +{\hbox to 20pt{\tiny\hss#1}\parbox[t]{\cxxcodewidth}{\small#2}}% +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for listing manual entries +% +\newdimen\cxxIdWidth +\newdimen\cxxTypeWidth +\newdimen\cxxProtoWidth +\newdimen\cxxMemoWidth +\newdimen\cxxPageWidth + +\cxxIdWidth=0.1\textwidth +\cxxTypeWidth=0.15\textwidth +\cxxProtoWidth=0.25\textwidth +\cxxMemoWidth=0.43\textwidth + +\cxxPageWidth=\textwidth +\advance\cxxPageWidth by-\cxxIdWidth +\advance\cxxPageWidth by-\cxxTypeWidth +\advance\cxxPageWidth by-\cxxProtoWidth +\advance\cxxPageWidth by-\cxxMemoWidth + +\newdimen\cxxProtoMemoWidth +\cxxProtoMemoWidth=\cxxProtoWidth +\advance\cxxProtoMemoWidth by\cxxMemoWidth + +\def\cxxStrut{\vrule width0pt height0pt depth9pt} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 1: type +% 2: name +% 3: args +% 4: memo +% 5: id +\def\cxxitem#1#2#3#4#5{\noindent{% + \setbox5\hbox{#5 }% + \ifdim\wd5>\cxxIdWidth% + \setbox5\hbox{\hbox to \cxxIdWidth{\hss#5}}% + \else + \setbox5\hbox{\hbox to \cxxIdWidth{#5\hss}}% + \fi% + \setbox1\hbox{{% + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}#1% + }}% + \setbox2\hbox{{% + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}\textbf{#2} #3% + }}% + \setbox4\hbox{\parbox[t]{\cxxMemoWidth}{{% + \raggedright\sloppy% + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}{\em #4} + \cxxStrut% + \def\page{#5}% + \ifx\page\empty% + \hss% + \else% + \ \dotfill% + \hbox to 0pt{\hbox to \cxxPageWidth{\hss% + \pageref{cxx.#5}% + }\hss}% + \fi% + }}}% + % + \ifdim\wd1>\cxxTypeWidth% + \hbox to \hsize{\unhbox5\hbox to \cxxTypeWidth{\unhbox1\hss}\hss}\\\nopagebreak% + \setbox5\hbox{\hskip\cxxIdWidth}% + \setbox1\hbox{\hskip\cxxTypeWidth}% + \else% + \setbox1\hbox{\hbox to \cxxTypeWidth{\unhbox1\hss}}% + \fi% + \ifdim\wd2>\cxxProtoWidth% + \ifdim\wd2<\cxxProtoMemoWidth% + \def\tmp{#4}% + \ifx\tmp\empty% + \def\tmp{#5}% + \ifx\tmp\empty% + \hbox to \hsize{\unhbox5\unhbox1\unhbox2\hss\cxxStrut}\\% + \else% + \hbox to \hsize{\unhbox5\unhbox1\unhbox2 + \dotfill\hbox to \cxxPageWidth{\hss\pageref{cxx.#5}}% + \cxxStrut}\\% + \fi% + \else% + \hbox to \hsize{\unhbox5\unhbox1\unhbox2\hss}\\\nopagebreak% + \hbox to \hsize{% + \hskip\cxxIdWidth% + \hskip\cxxTypeWidth% + \hskip\cxxProtoWidth% + \unhbox4\hss% + }\\% + \fi% + \else% + \hbox to \hsize{% + \unhbox5% + \unhbox1% + \parbox[t]{\cxxProtoMemoWidth}{% + \setbox255\hbox{\textbf{#2} (}% + \hangindent=\wd255\hangafter=1% + \raggedright\sloppy% + {\catcode`\&=4\catcode`\_=8% + \def{\ccverbatim \ccfinish}\textbf{#2} #3\strut}% + }\hss% + }\\\nopagebreak% + \hbox to \hsize{% + \hskip\cxxIdWidth% + \hskip\cxxTypeWidth% + \hskip\cxxProtoWidth% + \unhbox4\hss% + }\\% + \fi% + \else% + \hbox to \hsize{% + \unhbox5% + \unhbox1% + \hbox to \cxxProtoWidth{\unhbox2\hss}% + \unhbox4\hss% + }\\% + \fi% +}} + + +\newdimen\cxxtypestart +\cxxtypestart=0.05\textwidth +%\advance\cxxtypestart by \labelsep + +\newdimen\cxxnamestart +\cxxnamestart=\cxxtypestart +\advance\cxxnamestart by 0.25\textwidth + +\newdimen\cxxargsstart +\cxxargsstart=\cxxnamestart +\advance\cxxargsstart by 0.2\textwidth +\newdimen\cxxargswidth +\cxxargswidth=\textwidth +\advance\cxxargswidth by -\cxxargsstart +\advance\cxxargswidth by -\spaceskip + +\newdimen\cxxmemostart +\cxxmemostart=\cxxargsstart +\advance\cxxmemostart by 0.12\textwidth + +\newenvironment{cxxlist}[1]{ +\begingroup +\catcode`\,=\active% +\paragraph{#1}\strut\smallskip\\ +}{ +\endgroup +} + +\newenvironment{cxxnames}{\begin{cxxlist}{Names}}{\end{cxxlist}} +\newenvironment{cxxpublic}{\begin{cxxlist}{Public Members}}{\end{cxxlist}} +\newenvironment{cxxprivate}{\begin{cxxlist}{Private Members}}{\end{cxxlist}} +\newenvironment{cxxprotected}{\begin{cxxlist}{Protected Members}}{\end{cxxlist}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Numbered Frame box +% +\newlength{\cxxBoxLen}% +\newlength{\cxxBoxHt}% +\newlength{\cxxBoxDp}% +\newlength{\cxxSideHt}% +\newlength{\cxxSideLen}% +\newlength{\cxxTitleLen}% +\def\empty{} +\def\idPos{1cm} + +% +% box with id and name [optional width] +% +\newcommand{\aBox}[3][1pt]{{% +\small% +\def\width{#1}% +\def\num{#2}% +\def\name{#3}% +\setbox0\hbox{\hskip\width{ \strut\name\strut} \hskip\width}% +% +\setlength{\cxxBoxLen}{\wd0}% +\addtolength{\cxxBoxLen}{\width}% +\addtolength{\cxxBoxLen}{\width}% +\setbox2\hbox{\normalsize\strut\rule{\cxxBoxLen}{\width}}% +% +\ifx\num\empty% + \setbox1\hbox{\strut}% +\else% + \setbox1\hbox{\rule{\idPos}{\width} { \sf\bf\strut #2 } }% +\fi% +\addtolength{\cxxBoxLen}{-\wd1}% +\setbox1\hbox{\unhbox1\rule{\cxxBoxLen}{\width}}% +% +\setlength{\cxxSideHt}{\dp0}% +\addtolength{\cxxSideHt}{\width}% +\setlength{\cxxSideLen}{\dp0}% +\addtolength{\cxxSideLen}{\ht0}% +\addtolength{\cxxSideLen}{\dp1}% +\addtolength{\cxxSideLen}{\width}% +\addtolength{\cxxSideLen}{\width}% +\setbox3\hbox{\hbox to 0pt{\hss\rule[-\cxxSideHt]{\width}{\cxxSideLen}}}% +\setbox4\hbox{\hbox to 0pt{\rule[-\cxxSideHt]{\width}{\cxxSideLen}\hss}}% +% +\setlength{\cxxBoxHt}{\dp1}% +\addtolength{\cxxBoxHt}{1pt}% +\setlength{\cxxBoxDp}{\ht2}% +\addtolength{\cxxBoxDp}{-\width}% +\addtolength{\cxxBoxDp}{1pt}% +% +\setlength{\cxxBoxLen}{\ht3}% +\addtolength{\cxxBoxLen}{\ht1}% +% +\vbox to \cxxBoxLen{% +\hbox{\unhbox1}% +\vskip-\cxxBoxHt% +\hbox{\hskip\width\unhbox3\unhbox0\unhbox4}% +%\vskip-\ht2% +\vskip-\cxxBoxDp% +\hbox{\unhbox2}% +\vss% +}% +}} + +% +% box with id and name [optional width] of size #4 +% +\newcommand{\sizeBox}[4][3pt]{{% + \setbox0\hbox{ }% + \setlength{\cxxSideLen}{#4}% + \addtolength{\cxxSideLen}{-\wd0}% + \addtolength{\cxxSideLen}{-\wd0}% + \addtolength{\cxxSideLen}{-#1}% + \addtolength{\cxxSideLen}{-#1}% + \addtolength{\cxxSideLen}{-#1}% + \addtolength{\cxxSideLen}{-#1}% + \aBox[#1]{#2}{\hbox to \cxxSideLen{#3}}% +}} + +% +% centered box with id and name [optional width] of size #4 +% +\newcommand{\cBox}[4][3pt]{{% +\setbox0\hbox{\aBox[#1]{#2}{#3}}% +\ifdim\wd0<#4% + \sizeBox[#1]{#2}{\hss#3\hss}{#4}% +\else% + \setlength{\cxxSideLen}{\wd0}% + \advance\cxxSideLen by -#4% + \hskip -0.5\cxxSideLen% + \unhbox0% +\fi% +}} + +% +% right expanding box with id and name [optional width] of size at least #4 +% +\newcommand{\rBox}[4][3pt]{{% +\setbox0\hbox{\aBox[#1]{#2}{#3}\hss}% +\ifdim\wd0>#4% + \unhbox0% +\else% + \sizeBox[#1]{#2}{\hss#3\hss}{#4}% +\fi% +}} + +% +% left expanding box with id and name [optional width] of size at least #4 +% +\newcommand{\lBox}[4][3pt]{{% +\setbox0\hbox{\aBox[#1]{#2}{#3}\hss}% +\ifdim\wd0>#4% + \setlength{\cxxSideLen}{\wd0}% + \advance\cxxSideLen by -#4% + \hskip -\cxxSideLen% + \unhbox0% +\else% + \sizeBox[#1]{#2}{\hss#3\hss}{#4}% +\fi% +}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Pagestyle for documentation. +% +\newsavebox{\cxxHeadName} +\newcommand{\ps@docxx}{% + \renewcommand{\@oddhead}{\headlinetext}% + \renewcommand{\@evenhead}{\headlinetext}% + \renewcommand{\@oddfoot}{\footlinetext}% + \renewcommand{\@evenfoot}{\footlinetext}% +} +\newcommand{\makeHeadLine}[2]{ + \global\sbox{\cxxHeadName}{\vbox to 0pt{\vss% + \hbox to \textwidth{% + \hbox to 0pt{\strut\hbox to 1cm{\hss}\quad#1\hss}% + \hfil#2\hfil% + }% + \vskip 1pt% + \hbox to \textwidth{\hrulefill}% + }}% +} +\newcommand{\headlinetext}{\usebox{\cxxHeadName}} +\providecommand{\cxxCopyright}{% + \vtop{% + \hbox to \textwidth{{ + \tiny\sf This page was generated with the help of DOC++ + \hss}}% + \vskip -20pt + \hbox to \textwidth{ + \hbox{{\tiny\sf http://docpp.sourceforge.net}} + \hss}% + }% +} +\def\footlinetext{\hbox to \textwidth{ + \vtop{% + \hbox to \textwidth{\hrulefill}% + \vskip -20pt% + \cxxCopyright% + }% + \hss\vtop{\vskip 10pt\hbox{\today\hspace*{3cm}\textrm{\thepage} }} +}} +\pagestyle{docxx} + +\def\cxxTitle#1#2#3#4#5{\noindent{% + \thispagestyle{empty} + \vfill + \begin{center} + \Huge\bf + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}#1% + \strut\\ + \def{\ccverbatim \ccfinish}#2% + \strut\\ + \def{\ccverbatim \ccfinish}#3% + \strut\\ + \end{center} + \if\cxxVersionStr\empty% + \else% + \begin{center} + \small\sf + --- Version \cxxVersionStr\ --- + \end{center} + \global\def\cxxVersionStr{} + \fi + \vfill + \large + \begin{center} + \Large\em + \def{\ccverbatim \ccfinish}#4% + \end{center} + \vfill + \if\cxxAuthorStr\empty% + \else% + \begin{center} + \sf\cxxAuthorStr + \end{center} + \global\def\cxxAuthorStr{} + \vfill + \fi + \pagebreak + \makeHeadLine{}{#2} +}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for contents +% +\newcounter{cxxContentsDepth} +\setcounter{cxxContentsDepth}{0} +\newdimen\cxxContentsLengthIncr +\cxxContentsLengthIncr=18pt +\newdimen\cxxContentsLength +\cxxContentsLength=\textwidth +\advance\cxxContentsLength by -\cxxContentsLengthIncr + +\makeHeadLine{}{Contents} + +\newenvironment{cxxContents}{ + \ifcase \value{cxxContentsDepth} + \vskip 40pt + \hbox to \hsize{\hskip 8pt\hskip\cxxContentsLengthIncr\Huge\bf Contents\hss} + \vskip 40pt + \bf + \else % >1 + \rm + \fi + \begingroup + \addtocounter{cxxContentsDepth}{1} + \advance\cxxContentsLengthIncr by 8pt + \advance\cxxContentsLength by -\cxxContentsLengthIncr +}{ + \addtocounter{cxxContentsDepth}{-1} + \advance\cxxContentsLength by \cxxContentsLengthIncr + \advance\cxxContentsLengthIncr by -8pt + \ifcase \value{cxxContentsDepth} + \vskip 12pt + \or + \vskip 9pt + \else + \vskip 3pt + \fi + \endgroup +} + +\newcommand{\cxxContentsEntry}[3]{{ + \def\emtpty{} + \def\memo{#3} + \ifx\memo\empty + \setbox0\hbox{\parbox[t]{\cxxContentsLength}{\strut#2 \dotfill }} + \else + \setbox0\hbox{\parbox[t]{\cxxContentsLength}{\strut#2 --- {\em #3} \dotfill }} + \fi + \setbox1\hbox{\vtop{\vskip\dp0\vskip-\ht0\vskip-1.5pt\hbox to 20pt{\hss\rm \pageref{cxx.#1}}}} + \hbox to \textwidth{% + \hss\hbox to \cxxContentsLengthIncr{#1\hss}% + \unhbox0\unhbox1% + } +}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for class graph +% +\newdimen\cxxClassGraphShift +\newdimen\cxxClassGraphLength +\newdimen\cxxClassGraphHeight +\newdimen\cxxClassGraphDepth +\newdimen\cxxClassGraphTotV +\setlength{\cxxClassGraphLength}{0.23\hsize} +\setlength{\cxxClassGraphShift}{30pt} +\setbox0\vbox{\aBox[5pt]{\strut}{\strut}} +\setlength{\cxxClassGraphHeight}{\ht0} +\setlength{\cxxClassGraphDepth}{\dp0} +\addtolength{\cxxClassGraphDepth}{10pt} +\setlength{\cxxClassGraphTotV}{\dp0} +\addtolength{\cxxClassGraphTotV}{\ht0} + +\newenvironment{cxxInheritance}{ + \par\medskip + \begingroup + \newcommand{\cxxCGSpace}[1]{% + \vtop to \cxxClassGraphTotV{\hbox to \cxxClassGraphShift{% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##1}}% + } + \newcommand{\cxxSlashHline}{% + \vrule width 0.1\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.1\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.1\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.1\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.1\cxxClassGraphShift height 3.5pt depth -3pt% + } + \newcommand{\cxxDotHline}{% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + } + \newcommand{\cxxHline}{% + \vrule width 0.5\cxxClassGraphShift height 3.5pt depth -3pt% + } + \newcommand{\cxxVup}{\hbox to 0pt{\hss% + \vrule width 0.5pt height \cxxClassGraphHeight depth -3pt% + \hss}} + \newcommand{\cxxVlow}{\hbox to 0pt{\hss% + \vtop to 0pt{\vskip-3pt% + \hbox{\vrule width 0.5pt height 13pt depth \cxxClassGraphDepth}% + \vss}% + \hss}} + \newcommand{\cxxLinkUp}{\hbox to 0pt{\hss\hskip 0.5\cxxClassGraphShift% + \raise0.7\cxxClassGraphHeight\hbox to 0pt{\hss\textbf{\symbol{94}}\hss}}}% + \newcommand{\cxxLinkDown}{\hbox to 0pt{\hss\hskip 0.5\cxxClassGraphShift% + \raise-0.6\cxxClassGraphHeight\vbox to 0pt{% + \hbox to 0pt{\hss\textbf{$\lor$}\hss}\vss}}}% + \newcommand{\cxxLinkLeft}{\hbox to 0pt{\hss\hskip 0.0\cxxClassGraphShift% + \raise0.0\cxxClassGraphHeight\hbox to 0pt{\textbf{\tt\<}\hss}}}% + \newcommand{\cxxLinkRight}{\hbox to 0pt{\hss\hskip 1.0\cxxClassGraphShift% + \raise0.0\cxxClassGraphHeight\hbox to 0pt{\hss\textbf{\tt\>}}}}% + \newcommand{\cxxInheritanceEntry}[5][]{ + \hbox to \hsize{\hss% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##2% + \def\tmp{##1}% + \ifx\tmp\empty% + \def\tmp{##5}% + \ifx\tmp\empty% + \rBox[1pt]{##3}{##4}{0.24\hsize}% + \else% + \lBox[1pt]{##3}{##4}{0.24\hsize}% + \fi% + \else% + \cBox[2pt]{##3}{##4}{0.24\hsize}% + \fi% + ##5\hfill% + }\vskip-1pt + } + \newcommand{\cxxNone}{\cxxCGSpace{\hss}} + \newcommand{\cxxLong}{\cxxCGSpace{\hss\cxxVup\cxxVlow\hss}} +% + \newcommand{\cxxPubLeft}{\cxxCGSpace{\cxxHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxProLeft}{\cxxCGSpace{\cxxSlashHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxPriLeft}{\cxxCGSpace{\cxxDotHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxPubleft}{\cxxCGSpace{\cxxHline\cxxVlow\hss}} + \newcommand{\cxxProleft}{\cxxCGSpace{\cxxSlashHline\cxxVlow\hss}} + \newcommand{\cxxPrileft}{\cxxCGSpace{\cxxDotHline\cxxVlow\hss}} + \newcommand{\cxxLastPubLeft}{\cxxCGSpace{\cxxHline% + \cxxVup\cxxLinkDown\cxxVlow\hss}} + \newcommand{\cxxLastProLeft}{\cxxCGSpace{\cxxSlashHline% + \cxxVup\cxxLinkDown\cxxVlow\hss}} + \newcommand{\cxxLastPriLeft}{\cxxCGSpace{\cxxDotHline% + \cxxVup\cxxLinkDown\cxxVlow\hss}} + \newcommand{\cxxLastPubleft}{\cxxCGSpace{\cxxHline% + \cxxVlow\cxxLinkDown\hss}} + \newcommand{\cxxLastProleft}{\cxxCGSpace{\cxxSlashHline% + \cxxVlow\cxxLinkDown\hss}} + \newcommand{\cxxLastPrileft}{\cxxCGSpace{\cxxDotHline% + \cxxVlow\cxxLinkDown\hss}} + \newcommand{\cxxLinkPubLeft}{\cxxCGSpace{\cxxLinkLeft\cxxHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxLinkProLeft}{\cxxCGSpace{\cxxLinkLeft\cxxSlashHline% + \cxxVup\cxxVlow\hss}} + \newcommand{\cxxLinkPriLeft}{\cxxCGSpace{\cxxLinkLeft\cxxDotHline% + \cxxVup\cxxVlow\hss}} + \newcommand{\cxxLinkPubleft}{\cxxCGSpace{\cxxLinkLeft\cxxHline\cxxVlow\hss}} + \newcommand{\cxxLinkProleft}{\cxxCGSpace{\cxxLinkLeft\cxxSlashHline\cxxVlow\hss}} + \newcommand{\cxxLinkPrileft}{\cxxCGSpace{\cxxLinkLeft\cxxDotHline\cxxVlow\hss}} +% + \newcommand{\cxxPubRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxHline}} + \newcommand{\cxxProRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxSlashHline}} + \newcommand{\cxxPriRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxDotHline}} + \newcommand{\cxxPubright}{\cxxCGSpace{\hss\cxxVup\cxxHline}} + \newcommand{\cxxProright}{\cxxCGSpace{\hss\cxxVup\cxxSlashHline}} + \newcommand{\cxxPriright}{\cxxCGSpace{\hss\cxxVup\cxxDotHline}} + + \newcommand{\cxxLinkPubRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxHline\cxxLinkRight}} + \newcommand{\cxxLinkProRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow% + \cxxSlashHline\cxxLinkRight}} + \newcommand{\cxxLinkPriRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow% + \cxxDotHline\cxxLinkRight}} + \newcommand{\cxxLinkPubright}{\cxxCGSpace{\hss\cxxVup\cxxHline\cxxLinkRight}} + \newcommand{\cxxLinkProright}{\cxxCGSpace{\hss\cxxVup\cxxSlashHline\cxxLinkRight}} + \newcommand{\cxxLinkPriright}{\cxxCGSpace{\hss\cxxVup\cxxDotHline\cxxLinkRight}} + + \newcommand{\cxxFirstPubRight}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxVlow\cxxHline}} + \newcommand{\cxxFirstProRight}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxVlow% + \cxxSlashHline}} + \newcommand{\cxxFirstPriRight}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxVlow\cxxDotHline}} + \newcommand{\cxxFirstPubright}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxHline}} + \newcommand{\cxxFirstProright}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxSlashHline}} + \newcommand{\cxxFirstPriright}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxDotHline}} +}{ + \endgroup +} + +\newenvironment{cxxClassGraph}{ + \begin{cxxInheritance} + \newcommand{\cxxClassGraphEntry}[4]{ + \hbox to \hsize{\hss% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##1% + \def\tmp{##4}% + \rBox[1pt]{##2}{##3}{0.24\hsize}% + ##4% + \hskip\cxxClassGraphShift\dotfill% + \hbox to \cxxClassGraphShift{\hss\pageref{cxx.##2}}% + }\vskip-1pt + } + \newcommand{\cxxClassGraphEntryUnknownPackage}[3]{ + \hbox to \hsize{\hss% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##1% + \def\tmp{##3}% + \rBox[1pt]{}{##2}{0.24\hsize}% + ##3% + \hskip\cxxClassGraphShift\hfill% + }\vskip-1pt + } + \clearpage + \pagebreak\strut + \makeHeadLine{}{Class Graph} + \vskip 20pt + \hbox to \hsize{\Huge\bf \quad Class Graph\hss} + \vskip 40pt +} +{ + \end{cxxInheritance} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for generic manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newdimen\cxxgenericstart +\cxxgenericstart=\labelwidth +\advance\cxxgenericstart by \labelsep +\newdimen\cxxgenericwidth +\cxxgenericwidth=\textwidth +\advance\cxxgenericwidth by -\cxxgenericstart + +\newcounter{cxxDepth} +\setcounter{cxxDepth}{0} + +\newlength{\cxxSize} +\newenvironment{cxxgeneric}[5]{ +% +% some local definitions +% + \def\empty{} + \def\type{#1} + \def\args{#3} + \def\memo{#4} + \def\id {#5} +% +% pagebreak ? +% +\ifcase \value{cxxDepth} + \clearpage % 0 + \pagebreak + \makeHeadLine{#5}{#2} + \setlength{\cxxSize}{2pt} +\or % 1 + \strut\bigskip\bigskip\goodbreak% + \setlength{\cxxSize}{1pt} +\else % >2 + \strut\bigskip\bigskip\goodbreak% + \setlength{\cxxSize}{0.5pt} +\fi +\addtocounter{cxxDepth}{1} +% +% write synopsis +% +\setbox0\hbox{ }% +\setbox1\hbox{\strut\large #1 {\bf#2} }% +\setbox3\hbox{\strut\large #1 {\bf#2} #3}% +% +% box with id and name [optional width] over entire page width +% +\setlength{\cxxSideLen}{\hsize}% +\addtolength{\cxxSideLen}{-4\cxxSize}% +\addtolength{\cxxSideLen}{-2\wd0}% +\setlength{\cxxTitleLen}{\cxxSideLen}% +\addtolength{\cxxTitleLen}{-8\wd0}% +\hbox{\aBox[\cxxSize]{\id}{\vbox{\vskip 1.5\parskip% + \hbox to \cxxSideLen{\strut% + \hbox to 4\wd0{}% + \ifdim\wd3<\cxxTitleLen% + \parbox[b]{\cxxTitleLen}{% + \begin{raggedright} + \noindent\large #1 {\bf#2} #3 + \end{raggedright} + } + \else% + \ifdim\wd1>0.7\cxxTitleLen% + \parbox[b]{\cxxTitleLen}{% + \begin{raggedright} + \noindent\large #1 {\bf#2} #3 + \end{raggedright} + } + \else% + \addtolength{\cxxTitleLen}{-\wd1}% + \unhbox1% + \parbox[t]{\cxxTitleLen}{% + \advance\lineskip 7pt% + \begin{raggedright} + \noindent\large\strut #3 + \end{raggedright} + } + \fi% + \fi% + \hss\strut% + }\vskip\parskip}% +}} +% \parbox[b]{\cxxSideLen}{\begingroup +% \catcode`\&=12% +% \catcode`\_=12% +% \begin{flushleft} +% \quad\large% +% \ifx\type\empty +% \ifx\args\empty +% \strut{\bf #2}\\ +% \else +% \strut{\bf #2}\ \args +% \fi +% \else +% \strut\type\ {\bf #2}\ \args +% \fi +% \end{flushleft} +% \endgroup}}}% +\ifx\memo\empty\else + \vskip 10pt + \begin{flushright} + \it\memo + \end{flushright} +\fi +\label{cxx.\id} +\begingroup +\def\cxxExceptionsStr{} +\def\cxxParameterStr{} +\def\cxxReturnStr{} +\def\cxxInvariantsStr{} +\def\cxxPreconditionsStr{} +\def\cxxPostconditionsStr{} +\def\cxxSeeStr{} +\def\cxxAuthorStr{} +\def\cxxVersionStr{} +\def\cxxDeprecatedStr{} +\def\cxxSinceStr{} +\def\cxxFileStr{} +\def\cxxExceptions##1{\def\cxxExceptionsStr{##1}} +\def\cxxParameter##1{\def\cxxParameterStr{##1}} +\def\cxxReturn##1{\def\cxxReturnStr{##1}} +\def\cxxInvariants##1{\def\cxxInvariantsStr{##1}} +\def\cxxPreconditions##1{\def\cxxPreconditionsStr{##1}} +\def\cxxPostconditions##1{\def\cxxPostconditionsStr{##1}} +\def\cxxSee##1{\def\cxxSeeStr{##1}} +\def\cxxAuthor##1{\def\cxxAuthorStr{##1}} +\def\cxxVersion##1{\def\cxxVersionStr{##1}} +\def\cxxDeprecated##1{\def\cxxDeprecatedStr{##1}} +\def\cxxSince##1{\def\cxxSinceStr{##1}} +\def\cxxFile##1{\def\cxxFileStr{##1}} +}{ +\endgroup +\addtocounter{cxxDepth}{-1} +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for function manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxfunction}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Arguments}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxentry}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for union manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxunion}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Members}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for typedef manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxtypedef}[5]{ +\begin{cxxgeneric}{#1}{#2}{}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Members}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for macro manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxmacro}[5]{ +\begin{cxxgeneric}{\#define}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for class manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxclass}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for namespace manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxnamespace}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for interface manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxinterface}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for variable manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxvariable}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Names}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for documentation +% +\newenvironment{cxxdocumentation}{ +% +% switch on special characters for documentation section +% +\begingroup +\catcode`\&=4 +\catcode`\_=8 +}{ +\endgroup +} + +\newenvironment{cxxdoc}{ +% +% switch on special characters for documentation section +% +\begin{cxxdocumentation} +\strut\\\noindent% +}{ +\smallskip +\def\empty{}% +\ifx\cxxReturnStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Return Value:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxReturnStr}\\ +\fi +\ifx\cxxParameterStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Parameters:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxParameterStr}\\ +\fi +\ifx\cxxExceptionsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Exceptions:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxExceptionsStr}\\ +\fi +\ifx\cxxInvariantsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Invariants:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxInvariantsStr}\\ +\fi +\ifx\cxxPreconditionsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Preconditions:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxPreconditionsStr}\\ +\fi +\ifx\cxxPostconditionsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Postconditions:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxPostconditionsStr}\\ +\fi +\ifx\cxxSeeStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf See Also:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxSeeStr}\\ +\fi +\ifx\cxxAuthorStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Author:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxAuthorStr}\\ +\fi +\ifx\cxxVersionStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Version:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxVersionStr}\\ +\fi +\ifx\cxxDeprecatedStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf\it Deprecated:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxDeprecatedStr}\\ +\fi +\ifx\cxxSinceStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Since:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxSinceStr}\\ +\fi +\ifx\cxxFileStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf File:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxFileStr}\\ +\fi +\end{cxxdocumentation} +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for commented listing +% arguments are: +% #1 Section title +% +\newenvironment{cxximplementation}[1]{ +\goodbreak +\begin{cxxdocumentation} +}{ +\end{cxxdocumentation} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\def\cxxCodeLine#1{ +\strut\hbox to 20pt{\tiny\hss #1}\small +%\advance\leftmargin by 20pt +%\advance\textwidth by -20pt +\ccverbatim \parskip=0pt \cxxCodeFinish} +{\catcode`\=0 catcode`\=12 +gdefcxxCodeFinish#1{hbox{#1}endgroup}} + diff --git a/libupnp/ixml/doc/intro.dxx b/libupnp/ixml/doc/intro.dxx new file mode 100644 index 0000000..2b88f89 --- /dev/null +++ b/libupnp/ixml/doc/intro.dxx @@ -0,0 +1,28 @@ +/**@name Introduction + * The Linux DOM2 XML Parser Version 1.2 (IXML) is a lightweight, portable XML + * parser supporting the standard Document Object Model (DOM) Level 2 + * interfaces. The parser uses a C-style interface, making it idea for small, + * embedded applications. This document describes the interfaces supported by + * IXML 1.2, referencing the W3C DOM2 recommendations when necessary, and the + * additional utility application programming interfaces (APIs) that it + * supports. + * + * Note that this document assumes that the reader has a copy of the DOM2-Core + * recommendation. Refer to the link below to obtain a copy. Only a brief + * description is included here and the reader is pointed to the DOM2-Core + * recommendation for more details. This document does, however, clarify +* IXML-specific behavior when the recommendation is unclear. + * + * {\bf About DOM} + * + * The Document Object Model (DOM) is a set of interfaces that give a + * programmatic interface to documents. It provides a platform-neutral and + * language-neutral interface for random access and updating elements inside + * XML documents. DOM Level 1 provided the basic interfaces to access + * document elements. DOM Level 2 extended the interfaces to provide proper + * support for XML namespaces. + * + * The latest DOM 2 recommendation is maintained by W3C and is available from + * {\tt http://www.w3.org/TR/DOM-Level-2-Core}. + */ + diff --git a/libupnp/ixml/doc/ixml.dxx b/libupnp/ixml/doc/ixml.dxx new file mode 100644 index 0000000..d4fd090 --- /dev/null +++ b/libupnp/ixml/doc/ixml.dxx @@ -0,0 +1,15 @@ +/**@name IXML v1.2 + * \begin{center} + * {\bf Linux DOM2 XML Parser Version 1.2} + * + * Copyright (C) 2000-2003 Intel Corporation ALL RIGHTS RESERVED + * + * Revision 1.2.1 (\Date) + * \end{center} + */ +//@{ + //@Include: intro.dxx + //@Include: license.dxx + //@Include: ../inc/ixml.h +//@} + diff --git a/libupnp/ixml/doc/license.dxx b/libupnp/ixml/doc/license.dxx new file mode 100644 index 0000000..88b21cf --- /dev/null +++ b/libupnp/ixml/doc/license.dxx @@ -0,0 +1,32 @@ +/**@name License + * + * \begin{center} + * Copyright (c) 2000-2003 Intel Corporation + * All rights reserved. + * \end{center} + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * \begin{itemize} + * \item Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * \item Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * \item Neither name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * \end{itemize} + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ diff --git a/libupnp/ixml/inc/ixml.h b/libupnp/ixml/inc/ixml.h new file mode 100644 index 0000000..21f3a51 --- /dev/null +++ b/libupnp/ixml/inc/ixml.h @@ -0,0 +1,1905 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _IXML_H_ +#define _IXML_H_ + +#include +#include +//#include +#include + +typedef int BOOL; + +#define DOMString char * + + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IN +#define IN +#endif + +#ifndef OUT +#define OUT +#endif + +#ifndef INOUT +#define INOUT +#endif + +/**@name DOM Interfaces + * The Document Object Model consists of a set of objects and interfaces + * for accessing and manipulating documents. IXML does not implement all + * the interfaces documented in the DOM2-Core recommendation but defines + * a subset of the most useful interfaces. A description of the supported + * interfaces and methods is presented in this section. + * + * For a complete discussion on the object model, the object hierarchy, + * etc., refer to section 1.1 of the DOM2-Core recommendation. + */ + +//@{ + +/*================================================================ +* +* DOM node type +* +* +*=================================================================*/ +typedef enum +{ + eINVALID_NODE = 0, + eELEMENT_NODE = 1, + eATTRIBUTE_NODE = 2, + eTEXT_NODE = 3, + eCDATA_SECTION_NODE = 4, + eENTITY_REFERENCE_NODE = 5, + eENTITY_NODE = 6, + ePROCESSING_INSTRUCTION_NODE = 7, + eCOMMENT_NODE = 8, + eDOCUMENT_NODE = 9, + eDOCUMENT_TYPE_NODE = 10, + eDOCUMENT_FRAGMENT_NODE = 11, + eNOTATION_NODE = 12, + +} IXML_NODE_TYPE; + +/*================================================================ +* +* error code +* +* +*=================================================================*/ +typedef enum +{ // see DOM spec + IXML_INDEX_SIZE_ERR = 1, + IXML_DOMSTRING_SIZE_ERR = 2, + IXML_HIERARCHY_REQUEST_ERR = 3, + IXML_WRONG_DOCUMENT_ERR = 4, + IXML_INVALID_CHARACTER_ERR = 5, + IXML_NO_DATA_ALLOWED_ERR = 6, + IXML_NO_MODIFICATION_ALLOWED_ERR = 7, + IXML_NOT_FOUND_ERR = 8, + IXML_NOT_SUPPORTED_ERR = 9, + IXML_INUSE_ATTRIBUTE_ERR = 10, + IXML_INVALID_STATE_ERR = 11, + IXML_SYNTAX_ERR = 12, + IXML_INVALID_MODIFICATION_ERR = 13, + IXML_NAMESPACE_ERR = 14, + IXML_INVALID_ACCESS_ERR = 15, + + IXML_SUCCESS = 0, + IXML_NO_SUCH_FILE = 101, + IXML_INSUFFICIENT_MEMORY = 102, + IXML_FILE_DONE = 104, + IXML_INVALID_PARAMETER = 105, + IXML_FAILED = 106, + IXML_INVALID_ITEM_NUMBER = 107, + +} IXML_ERRORCODE; + + +#define DOCUMENTNODENAME "#document" +#define TEXTNODENAME "#text" +#define CDATANODENAME "#cdata-section" + +/*================================================================ +* +* DOM data structures +* +* +*=================================================================*/ +typedef struct _IXML_Document *Docptr; + +typedef struct _IXML_Node *Nodeptr; +typedef struct _IXML_Node +{ + DOMString nodeName; + DOMString nodeValue; + IXML_NODE_TYPE nodeType; + DOMString namespaceURI; + DOMString prefix; + DOMString localName; + BOOL readOnly; + + Nodeptr parentNode; + Nodeptr firstChild; + Nodeptr prevSibling; + Nodeptr nextSibling; + Nodeptr firstAttr; + Docptr ownerDocument; + +} IXML_Node; + +typedef struct _IXML_Document +{ + IXML_Node n; +} IXML_Document; + +typedef struct _IXML_CDATASection +{ + IXML_Node n; +} IXML_CDATASection; + +typedef struct _IXML_Element +{ + IXML_Node n; + DOMString tagName; + +} IXML_Element; + +typedef struct _IXML_ATTR +{ + IXML_Node n; + BOOL specified; + IXML_Element *ownerElement; +} IXML_Attr; + +typedef struct _IXML_Text +{ + IXML_Node n; +} IXML_Text; + +typedef struct _IXML_NodeList +{ + IXML_Node *nodeItem; + struct _IXML_NodeList *next; +} IXML_NodeList; + + +typedef struct _IXML_NamedNodeMap +{ + IXML_Node *nodeItem; + struct _IXML_NamedNodeMap *next; +} IXML_NamedNodeMap; + +#ifdef __cplusplus +extern "C" { +#endif + +/*================================================================ +* +* NODE interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it Node} + * The {\bf Node} interface forms the primary datatype for all other DOM + * objects. Every other interface is derived from this interface, inheriting + * its functionality. For more information, refer to DOM2-Core page 34. + */ + +//@{ + + /** Returns the name of the {\bf Node}, depending on what type of + * {\bf Node} it is, in a read-only string. Refer to the table in the + * DOM2-Core for a description of the node names for various interfaces. + * + * @return [const DOMString] A constant {\bf DOMString} of the node name. + */ + +const DOMString +ixmlNode_getNodeName(IXML_Node *nodeptr + /** Pointer to the node to retrieve the name. */ + ); + + /** Returns the value of the {\bf Node} as a string. Note that this string + * is not a copy and modifying it will modify the value of the {\bf Node}. + * + * @return [DOMString] A {\bf DOMString} of the {\bf Node} value. + */ + +DOMString +ixmlNode_getNodeValue(IXML_Node *nodeptr + /** Pointer to the {\bf Node} to retrieve the value. */ + ); + + /** Assigns a new value to a {\bf Node}. The {\bf newNodeValue} string is + * duplicated and stored in the {\bf Node} so that the original does not + * have to persist past this call. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: The {\bf Node*} is not a valid + * pointer. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlNode_setNodeValue(IXML_Node *nodeptr, + /** The {\bf Node} to which to assign a new value. */ + char *newNodeValue + /** The new value of the {\bf Node}. */ + ); + + /** Retrieves the type of a {\bf Node}. The defined {\bf Node} constants + * are: + * \begin{itemize} + * \item {\tt eATTRIBUTE_NODE} + * \item {\tt eCDATA_SECTION_NODE} + * \item {\tt eCOMMENT_NODE} + * \item {\tt eDOCUMENT_FRAGMENT_NODE} + * \item {\tt eDOCUMENT_NODE} + * \item {\tt eDOCUMENT_TYPE_NODE} + * \item {\tt eELEMENT_NODE} + * \item {\tt eENTITY_NODE} + * \item {\tt eENTITY_REFERENCE_NODE} + * \item {\tt eNOTATION_NODE} + * \item {\tt ePROCESSING_INSTRUCTION_NODE} + * \item {\tt eTEXT_NODE} + * \end{itemize} + * + * @return [const unsigned short] An integer representing the type of the + * {\bf Node}. + */ + +unsigned short +ixmlNode_getNodeType(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the type. */ + ); + + /** Retrieves the parent {\bf Node} for a {\bf Node}. + * + * @return [Node*] A pointer to the parent {\bf Node} or {\tt NULL} if the + * {\bf Node} has no parent. + */ + +IXML_Node* +ixmlNode_getParentNode(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the + parent. */ + ); + + /** Retrieves the list of children of a {\bf Node} in a {\bf NodeList} + * structure. If a {\bf Node} has no children, {\bf ixmlNode_getChildNodes} + * returns a {\bf NodeList} structure that contains no {\bf Node}s. + * + * @return [NodeList*] A {\bf NodeList} of the children of the {\bf Node}. + */ + +IXML_NodeList* +ixmlNode_getChildNodes(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the + children. */ + ); + + /** Retrieves the first child {\bf Node} of a {\bf Node}. + * + * @return [Node*] A pointer to the first child {\bf Node} or {\tt NULL} + * if the {\bf Node} does not have any children. + */ + +IXML_Node* +ixmlNode_getFirstChild(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the first + child. */ +); + + /** Retrieves the last child {\bf Node} of a {\bf Node}. + * + * @return [Node*] A pointer to the last child {\bf Node} or {\tt NULL} if + * the {\bf Node} does not have any children. + */ + +IXML_Node* +ixmlNode_getLastChild(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the last + child. */ + ); + + /** Retrieves the sibling {\bf Node} immediately preceding this {\bf Node}. + * + * @return [Node*] A pointer to the previous sibling {\bf Node} or + * {\tt NULL} if no such {\bf Node} exists. + */ + +IXML_Node* +ixmlNode_getPreviousSibling(IXML_Node *nodeptr + /** The {\bf Node} for which to retrieve the + previous sibling. */ + ); + + /** Retrieves the sibling {\bf Node} immediately following this {\bf Node}. + * + * @return [Node*] A pointer to the next sibling {\bf Node} or {\tt NULL} + * if no such {\bf Node} exists. + */ + +IXML_Node* +ixmlNode_getNextSibling(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the next + sibling. */ + ); + + /** Retrieves the attributes of a {\bf Node}, if it is an {\bf Element} node, + * in a {\bf NamedNodeMap} structure. + * + * @return [NamedNodeMap*] A {\bf NamedNodeMap} of the attributes or + * {\tt NULL}. + */ + +IXML_NamedNodeMap* +ixmlNode_getAttributes(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the + attributes. */ + ); + + /** Retrieves the document object associated with this {\bf Node}. This + * owner document {\bf Node} allows other {\bf Node}s to be created in the + * context of this document. Note that {\bf Document} nodes do not have + * an owner document. + * + * @return [Document*] A pointer to the owning {\bf Document} or + * {\tt NULL}, if the {\bf Node} does not have an owner. + */ + +IXML_Document* +ixmlNode_getOwnerDocument(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the + owner document. */ + ); + + /** Retrieves the namespace URI for a {\bf Node} as a {\bf DOMString}. Only + * {\bf Node}s of type {\tt eELEMENT_NODE} or {\tt eATTRIBUTE_NODE} can + * have a namespace URI. {\bf Node}s created through the {\bf Document} + * interface will only contain a namespace if created using + * {\bf ixmlDocument_createElementNS}. + * + * @return [const DOMString] A {\bf DOMString} representing the URI of the + * namespace or {\tt NULL}. + */ + +const DOMString +ixmlNode_getNamespaceURI(IXML_Node *nodeptr + /** The {\bf Node} for which to retrieve the + namespace. */ + ); + + /** Retrieves the namespace prefix, if present. The prefix is the name + * used as an alias for the namespace URI for this element. Only + * {\bf Node}s of type {\tt eELEMENT_NODE} or {\tt eATTRIBUTE_NODE} can have + * a prefix. {\bf Node}s created through the {\bf Document} interface will + * only contain a prefix if created using {\bf ixmlDocument_createElementNS}. + * + * @return [DOMString] A {\bf DOMString} representing the namespace prefix + * or {\tt NULL}. + */ + +DOMString +ixmlNode_getPrefix(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the prefix. */ + ); + + /** Retrieves the local name of a {\bf Node}, if present. The local name is + * the tag name without the namespace prefix. Only {\bf Node}s of type + * {\tt eELEMENT_NODE} or {\tt eATTRIBUTE_NODE} can have a local name. + * {\Bf Node}s created through the {\bf Document} interface will only + * contain a local name if created using {\bf ixmlDocument_createElementNS}. + * + * @return [const DOMString] A {\bf DOMString} representing the local name + * of the {\bf Element} or {\tt NULL}. + */ + +const DOMString +ixmlNode_getLocalName(IXML_Node *nodeptr + /** The {\bf Node} from which to retrieve the local + name. */ + ); + + /** Inserts a new child {\bf Node} before the existing child {\bf Node}. + * {\bf refChild} can be {\tt NULL}, which inserts {\bf newChild} at the + * end of the list of children. Note that the {\bf Node} (or {\bf Node}s) + * in {\bf newChild} must already be owned by the owner document (or have no + * owner at all) of {\bf nodeptr} for insertion. If not, the {\bf Node} + * (or {\bf Node}s) must be imported into the document using + * {\bf ixmlDocument_importNode}. If {\bf newChild} is already in the tree, + * it is removed first. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf nodeptr} or + * {\bf newChild} is {\tt NULL}. + * \item {\tt IXML_HIERARCHY_REQUEST_ERR}: The type of the {\bf Node} + * does not allow children of the type of {\bf newChild}. + * \item {\tt IXML_WRONG_DOCUMENT_ERR}: {\bf newChild} has an owner + * document that does not match the owner of {\bf nodeptr}. + * \item {\tt IXML_NO_MODIFICATION_ALLOWED_ERR}: {\bf nodeptr} is + * read-only or the parent of the {\bf Node} being inserted is + * read-only. + * \item {\tt IXML_NOT_FOUND_ERR}: {\bf refChild} is not a child of + * {\bf nodeptr}. + * \end{itemize} + */ + +int +ixmlNode_insertBefore(IXML_Node *nodeptr, + /** The parent of the {\bf Node} before which to + insert the new child. */ + IXML_Node* newChild, + /** The {\bf Node} to insert into the tree. */ + IXML_Node* refChild + /** The reference child where the new {\bf Node} + should be inserted. The new {\bf Node} will + appear directly before the reference child. */ + ); + + /** Replaces an existing child {\bf Node} with a new child {\bf Node} in + * the list of children of a {\bf Node}. If {\bf newChild} is already in + * the tree, it will first be removed. {\bf returnNode} will contain the + * {\bf oldChild} {\bf Node}, appropriately removed from the tree (i.e. it + * will no longer have an owner document). + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMTER: Either {\bf nodeptr}, {\bf + * newChild}, or {\bf oldChild} is {\tt NULL}. + * \item {\tt IXML_HIERARCHY_REQUEST_ERR}: The {\bf newChild} is not + * a type of {\bf Node} that can be inserted into this tree or + * {\bf newChild} is an ancestor of {\bf nodePtr}. + * \item {\tt IXML_WRONG_DOCUMENT_ERR}: {\bf newChild} was created from + * a different document than {\bf nodeptr}. + * \item {\tt IXML_NO_MODIFICATION_ALLOWED_ERR}: {\bf nodeptr} or + * its parent is read-only. + * \item {\tt IXML_NOT_FOUND_ERR}: {\bf oldChild} is not a child of + * {\bf nodeptr}. + * \end{itemize} + */ + +int +ixmlNode_replaceChild(IXML_Node *nodeptr, + /** The parent of the {\bf Node} which contains the + child to replace. */ + IXML_Node* newChild, + /** The child with which to replace {\bf oldChild}. */ + IXML_Node* oldChild, + /** The child to replace with {\bf newChild}. */ + IXML_Node** returnNode + /** Pointer to a {\bf Node} to place the removed {\bf + oldChild} {\bf Node}. */ + ); + + /** Removes a child from the list of children of a {\bf Node}. + * {\bf returnNode} will contain the {\bf oldChild} {\bf Node}, + * appropriately removed from the tree (i.e. it will no longer have an + * owner document). + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf nodeptr} or + * {\bf oldChild} is {\tt NULL}. + * \item {\tt IXML_NO_MODIFICATION_ALLOWED_ERR}: {\bf nodeptr} or its + * parent is read-only. + * \item {\tt IXML_NOT_FOUND_ERR}: {\bf oldChild} is not among the + * children of {\bf nodeptr}. + * \end{itemize} + */ + +int +ixmlNode_removeChild(IXML_Node *nodeptr, + /** The parent of the child to remove. */ + IXML_Node* oldChild, + /** The child {\bf Node} to remove. */ + IXML_Node **returnNode + /** Pointer to a {\bf Node} to place the removed {\bf + oldChild} {\bf Node}. */ + ); + + /** Appends a child {\bf Node} to the list of children of a {\bf Node}. If + * {\bf newChild} is already in the tree, it is removed first. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf nodeptr} or + * {\bf newChild} is {\tt NULL}. + * \item {\tt IXML_HIERARCHY_REQUEST_ERR}: {\bf newChild} is of a type + * that cannot be added as a child of {\bf nodeptr} or + * {\bf newChild} is an ancestor of {\bf nodeptr}. + * \item {\tt IXML_WRONG_DOCUMENT_ERR}: {\bf newChild} was created from + * a different document than {\bf nodeptr}. + * \item {\tt IXML_NO_MODIFICATION_ALLOWED_ERR}: {\bf nodeptr} is a + * read-only {\bf Node}. + */ + +int +ixmlNode_appendChild(IXML_Node *nodeptr, + /** The {\bf Node} in which to append the new child. */ + IXML_Node* newChild + /** The new child to append. */ + ); + + /** Queries whether or not a {\bf Node} has children. + * + * @return [BOOL] {\tt TRUE} if the {\bf Node} has one or more children + * otherwise {\tt FALSE}. + */ + +BOOL +ixmlNode_hasChildNodes(IXML_Node *nodeptr + /** The {\bf Node} to query for children. */ + ); + + /** Clones a {\bf Node}. The new {\bf Node} does not have a parent. The + * {\bf deep} parameter controls whether the subtree of the {\bf Node} is + * also cloned. For details on cloning specific types of {\bf Node}s, + * refer to the DOM2-Core recommendation. + * + * @return [Node*] A clone of {\bf nodeptr} or {\tt NULL}. + */ + +IXML_Node* +ixmlNode_cloneNode(IXML_Node *nodeptr, + /** The {\bf Node} to clone. */ + BOOL deep + /** {\tt TRUE} to clone the subtree also or {\tt FALSE} + to clone only {\bf nodeptr}. */ + ); + + /** Queries whether this {\bf Node} has attributes. Note that only + * {\bf Element} nodes have attributes. + * + * @return [BOOL] {\tt TRUE} if the {\bf Node} has attributes otherwise + * {\tt FALSE}. + */ + +BOOL +ixmlNode_hasAttributes(IXML_Node *node + /** The {\bf Node} to query for attributes. */ + ); + + /** Frees a {\bf Node} and all {\bf Node}s in its subtree. + * + * @return [void] This function does not return a value. + */ + +void +ixmlNode_free(IXML_Node *IXML_Node + /** The {\bf Node} to free. */ + ); + +//@} + +/*================================================================ +* +* Attribute interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it Attr} + * The {\bf Attr} interface represents an attribute of an {\bf Element}. + * The document type definition (DTD) or schema usually dictate the + * allowable attributes and values for a particular element. For more + * information, refer to the {\it Interface Attr} section in the DOM2-Core. + */ +//@{ + + + /** Frees an {\bf Attr} node. + * + * @return [void] This function does not return a value. + */ + +void +ixmlAttr_free(IXML_Attr *attrNode + /** The {\bf Attr} node to free. */ + ); + +//@} + + +/*================================================================ +* +* CDATASection interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it CDATASection} + * The {\bf CDATASection} is used to escape blocks of text containing + * characters that would otherwise be regarded as markup. CDATA sections + * cannot be nested. Their primary purpose is for including material such + * XML fragments, without needing to escape all the delimiters. For more + * information, refer to the {\it Interface CDATASection} section in the + * DOM2-Core. + */ +//@{ + + + /** Initializes a {\bf CDATASection} node. + * + * @return [void] This function does not return a value. + */ + +void +ixmlCDATASection_init(IXML_CDATASection *nodeptr + /** The {\bf CDATASection} node to initialize. */ + ); + + + /** Frees a {\bf CDATASection} node. + * + * @return [void] This function does not return a value. + */ + +void +ixmlCDATASection_free(IXML_CDATASection *nodeptr + /** The {\bf CDATASection} node to free. */ + ); + +//@} + +/*================================================================ +* +* Document interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it Document} + * The {\bf Document} interface represents the entire XML document. + * In essence, it is the root of the document tree and provides the + * primary interface to the elements of the document. For more information, + * refer to the {\it Interface Document} section in the DOM2Core. + */ +//@{ + + /** Initializes a {\bf Document} node. + * + * @return [void] This function does not return a value. + */ + +void +ixmlDocument_init(IXML_Document *nodeptr + /** The {\bf Document} node to initialize. */ + ); + + /** Creates a new empty {\bf Document} node. The + * {\bf ixmlDocument_createDocumentEx} API differs from the {\bf + * ixmlDocument_createDocument} API in that it returns an error code + * describing the reason for the failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int ixmlDocument_createDocumentEx(IXML_Document** doc + /** Pointer to a {\bf Document} where the + new object will be stored. */ + ); + + + /** Creates a new empty {\bf Document} node. + * + * @return [Document*] A pointer to the new {\bf Document} or {\tt NULL} on + * failure. + */ + +IXML_Document* ixmlDocument_createDocument(); + + /** Creates a new {\bf Element} node with the given tag name. The new + * {\bf Element} node has a {\tt nodeName} of {\bf tagName} and + * the {\tt localName}, {\tt prefix}, and {\tt namespaceURI} set + * to {\tt NULL}. To create an {\bf Element} with a namespace, + * see {\bf ixmlDocument_createElementNS}. + * + * The {\bf ixmlDocument_createElementEx} API differs from the {\bf + * ixmlDocument_createElement} API in that it returns an error code + * describing the reason for failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc} or + * {\bf tagName} is {\tt NULL}. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlDocument_createElementEx(IXML_Document *doc, + /** The owner {\bf Document} of the new node. */ + const DOMString tagName, + /** The tag name of the new {\bf Element} + node. */ + IXML_Element **rtElement + /** Pointer to an {\bf Element} where the new + object will be stored. */ + ); + + /** Creates a new {\bf Element} node with the given tag name. The new + * {\bf Element} node has a {\tt nodeName} of {\bf tagName} and + * the {\tt localName}, {\tt prefix}, and {\tt namespaceURI} set + * to {\tt NULL}. To create an {\bf Element} with a namespace, + * see {\bf ixmlDocument_createElementNS}. + * + * @return [Document*] A pointer to the new {\bf Element} or {\tt NULL} on + * failure. + */ + +IXML_Element* +ixmlDocument_createElement(IXML_Document *doc, + /** The owner {\bf Document} of the new node. */ + const DOMString tagName + /** The tag name of the new {\bf Element} node. */ + ); + + + /** Creates a new {\bf Text} node with the given data. + * The {\bf ixmlDocument_createTextNodeEx} API differs from the {\bf + * ixmlDocument_createTextNode} API in that it returns an error code + * describing the reason for failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc} or {\bf data} + * is {\tt NULL}. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlDocument_createTextNodeEx(IXML_Document *doc, + /** The owner {\bf Document} of the new node. */ + const DOMString data, + /** The data to associate with the new {\bf + Text} node. */ + IXML_Node** textNode + /** A pointer to a {\bf Node} where the new + object will be stored. */ + ); + + + /** Creates a new {\bf Text} node with the given data. + * + * @return [Node*] A pointer to the new {\bf Node} or {\tt NULL} on failure. + */ + +IXML_Node* +ixmlDocument_createTextNode(IXML_Document *doc, + /** The owner {\bf Document} of the new node. */ + const DOMString data + /** The data to associate with the new {\bf Text} + node. */ + ); + + /** Creates a new {\bf CDATASection} node with given data. + * + * The {\bf ixmlDocument_createCDATASectionEx} API differs from the {\bf + * ixmlDocument_createCDATASection} API in that it returns an error code + * describing the reason for failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc} or {\bd data} + * is {\tt NULL}. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlDocument_createCDATASectionEx(IXML_Document *doc, + /** The owner {\bf Document} of the new + node. */ + DOMString data, + /** The data to associate with the new + {\bf CDATASection} node. */ + IXML_CDATASection** cdNode + /** A pointer to a {\bf Node} where the + new object will be stored. */ + ); + + + /** Creates a new {\bf CDATASection} node with given data. + * + * @return [CDATASection*] A pointer to the new {\bf CDATASection} or + * {\tt NULL} on failure. + */ + +IXML_CDATASection* +ixmlDocument_createCDATASection(IXML_Document *doc, + /** The owner {\bf Document} of the new + node. */ + DOMString data + /** The data to associate with the new {\bf + CDATASection} node. */ + ); + + /** Creates a new {\bf Attr} node with the given name. + * + * @return [Attr*] A pointer to the new {\bf Attr} or {\tt NULL} on failure. + */ + +IXML_Attr* +ixmlDocument_createAttribute(IXML_Document *doc, + /** The owner {\bf Document} of the new node. */ + char *name + /** The name of the new attribute. */ + ); + + + /** Creates a new {\bf Attr} node with the given name. + * + * The {\bf ixmlDocument_createAttributeEx} API differs from the {\bf + * ixmlDocument_createAttribute} API in that it returns an error code + * describing the reason for failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc} or {\bf name} + * is {\tt NULL}. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlDocument_createAttributeEx(IXML_Document *doc, + /** The owner {\bf Document} of the new + node. */ + char *name, + /** The name of the new attribute. */ + IXML_Attr** attrNode + /** A pointer to a {\bf Attr} where the new + object will be stored. */ + ); + + + /** Returns a {\bf NodeList} of all {\bf Elements} that match the given + * tag name in the order in which they were encountered in a preorder + * traversal of the {\bf Document} tree. + * + * @return [NodeList*] A pointer to a {\bf NodeList} containing the + * matching items or {\tt NULL} on an error. + */ + +IXML_NodeList* +ixmlDocument_getElementsByTagName(IXML_Document *doc, + /** The {\bf Document} to search. */ + DOMString tagName + /** The tag name to find. */ + ); + +// introduced in DOM level 2 + + /** Creates a new {\bf Element} node in the given qualified name and + * namespace URI. + * + * The {\bf ixmlDocument_createElementNSEx} API differs from the {\bf + * ixmlDocument_createElementNS} API in that it returns an error code + * describing the reason for failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc}, + * {\bf namespaceURI}, or {\bf qualifiedName} is {\tt NULL}. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlDocument_createElementNSEx(IXML_Document *doc, + /** The owner {\bf Document} of the new + node. */ + DOMString namespaceURI, + /** The namespace URI for the new {\bf + Element}. */ + DOMString qualifiedName, + /** The qualified name of the new {\bf + Element}. */ + IXML_Element** rtElement + /** A pointer to an {\bf Element} where the + new object will be stored. */ + ); + + + /** Creates a new {\bf Element} node in the given qualified name and + * namespace URI. + * + * @return [Element*] A pointer to the new {\bf Element} or {\tt NULL} on + * failure. + */ + +IXML_Element* +ixmlDocument_createElementNS(IXML_Document *doc, + /** The owner {\bf Document} of the new node. */ + DOMString namespaceURI, + /** The namespace URI for the new {\bf + Element}. */ + DOMString qualifiedName + /** The qualified name of the new {\bf + Element}. */ + ); + + /** Creates a new {\bf Attr} node with the given qualified name and + * namespace URI. + * + * The {\bf ixmlDocument_createAttributeNSEx} API differs from the {\bf + * ixmlDocument_createAttributeNS} API in that it returns an error code + * describing the reason for failure rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc}, + * {\bf namespaceURI}, or {\bf qualifiedName} is {\tt NULL}. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlDocument_createAttributeNSEx(IXML_Document *doc, + /** The owner {\bf Document} of the new + {\bf Attr}. */ + DOMString namespaceURI, + /** The namespace URI for the attribute. */ + DOMString qualifiedName, + /** The qualified name of the attribute. */ + IXML_Attr** attrNode + /** A pointer to an {\bf Attr} where the + new object will be stored. */ + ); + + /** Creates a new {\bf Attr} node with the given qualified name and + * namespace URI. + * + * @return [Attr*] A pointer to the new {\bf Attr} or {\tt NULL} on failure. + */ + +IXML_Attr* +ixmlDocument_createAttributeNS(IXML_Document *doc, + /** The owner {\bf Document} of the new + {\bf Attr}. */ + DOMString namespaceURI, + /** The namespace URI for the attribute. */ + DOMString qualifiedName + /** The qualified name of the attribute. */ + ); + + /** Returns a {\bf NodeList} of {\bf Elements} that match the given + * local name and namespace URI in the order they are encountered + * in a preorder traversal of the {\bf Document} tree. Either + * {\bf namespaceURI} or {\bf localName} can be the special {\tt "*"} + * character, which matches any namespace or any local name respectively. + * + * @return [NodeList*] A pointer to a {\bf NodeList} containing the + * matching items or {\tt NULL} on an error. + */ + +IXML_NodeList* +ixmlDocument_getElementsByTagNameNS(IXML_Document* doc, + /** The {\bf Document} to search. */ + DOMString namespaceURI, + /** The namespace of the elements to + find or {\tt "*"} to match any + namespace. */ + DOMString localName + /** The local name of the elements to + find or {\tt "*"} to match any local + name. */ + ); + + /** Returns the {\bf Element} whose {\tt ID} matches that given id. + * + * @return [Element*] A pointer to the matching {\bf Element} or + * {\tt NULL} on an error. + */ + +IXML_Element* +ixmlDocument_getElementById(IXML_Document* doc, + /** The owner {\bf Document} of the {\bf + Element}. */ + DOMString tagName + /** The name of the {\bf Element}.*/ + ); + + /** Frees a {\bf Document} object and all {\bf Node}s associated with it. + * Any {\bf Node}s extracted via any other interface function, e.g. + * {\bf ixmlDocument_GetElementById}, become invalid after this call unless + * explicitly cloned. + * + * @return [void] This function does not return a value. + */ + +void +ixmlDocument_free(IXML_Document* doc + /** The {\bf Document} to free. */ + ); + + /** Imports a {\bf Node} from another {\bf Document} into this + * {\bf Document}. The new {\bf Node} does not a have parent node: it is a + * clone of the original {\bf Node} with the {\tt ownerDocument} set to + * {\bf doc}. The {\bf deep} parameter controls whether all the children + * of the {\bf Node} are imported. Refer to the DOM2-Core recommendation + * for details on importing specific node types. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf doc} or + * {\bf importNode} is not a valid pointer. + * \item {\tt IXML_NOT_SUPPORTED_ERR}: {\bf importNode} is a + * {\bf Document}, which cannot be imported. + * \item {\tt IXML_FAILED}: The import operation failed because the + * {\bf Node} to be imported could not be cloned. + * \end{itemize} + */ + +int +ixmlDocument_importNode(IXML_Document* doc, + /** The {\bf Document} into which to import. */ + IXML_Node* importNode, + /** The {\bf Node} to import. */ + BOOL deep, + /** {\tt TRUE} to import all children of {\bf + importNode} or {\tt FALSE} to import only the + root node. */ + IXML_Node** rtNode + /** A pointer to a new {\bf Node} owned by {\bf + doc}. */ + ); +//@} + +/*================================================================ +* +* Element interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it Element} + * The {\bf Element} interface represents an element in an XML document. + * Only {\bf Element}s are allowed to have attributes, which are stored in the + * {\tt attributes} member of a {\bf Node}. The {\bf Element} interface + * extends the {\bf Node} interface and adds more operations to manipulate + * attributes. + */ +//@{ + + /** Initializes a {\bf IXML_Element} node. + * + * @return [void] This function does not return a value. + */ + +void ixmlElement_init(IXML_Element *element + /** The {\bf Element} to initialize.*/ + ); + + + /** Returns the name of the tag as a constant string. + * + * @return [const DOMString] A {\bf DOMString} representing the name of the + * {\bf Element}. + */ + +const DOMString +ixmlElement_getTagName(IXML_Element* element + /** The {\bf Element} from which to retrieve the + name. */ + ); + + /** Retrieves an attribute of an {\bf Element} by name. + * + * @return [DOMString] A {\bf DOMString} representing the value of the + * attribute. + */ + +DOMString +ixmlElement_getAttribute(IXML_Element* element, + /** The {\bf Element} from which to retrieve the + attribute. */ + DOMString name + /** The name of the attribute to retrieve. */ + ); + + /** Adds a new attribute to an {\bf Element}. If an attribute with the same + * name already exists, the attribute value will be updated with the + * new value in {\bf value}. + * + * @return [int] An integer representing of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element}, + * {\bf name}, or {\bf value} is {\tt NULL}. + * \item {\tt IXML_INVALID_CHARACTER_ERR}: {\bf name} contains an + * illegal character. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete the operation. + * \end{itemize} + */ + +int +ixmlElement_setAttribute(IXML_Element* element, + /** The {\bf Element} on which to set the + attribute. */ + DOMString name, + /** The name of the attribute. */ + DOMString value + /** The value of the attribute. Note that this is + a non-parsed string and any markup must be + escaped. */ + ); + + /** Removes an attribute by name. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element} or + * {\bf name} is {\tt NULL}. + * \end{itemize} + */ + +int +ixmlElement_removeAttribute(IXML_Element* element, + /** The {\bf Element} from which to remove the + attribute. */ + DOMString name + /** The name of the attribute to remove. */ + ); + + /** Retrieves an attribute node by name. See + * {\bf ixmlElement_getAttributeNodeNS} to retrieve an attribute node using + * a qualified name or namespace URI. + * + * @return [Attr*] A pointer to the attribute matching {\bf name} or + * {\tt NULL} on an error. + */ + +IXML_Attr* +ixmlElement_getAttributeNode(IXML_Element* element, + /** The {\bf Element} from which to get the + attribute node. */ + DOMString name + /** The name of the attribute node to find. */ + ); + + /** Adds a new attribute node to an {\bf Element}. If an attribute already + * exists with {\bf newAttr} as a name, it will be replaced with the + * new one and the old one will be returned in {\bf rtAttr}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element} or + * {\bf newAttr} is {\tt NULL}. + * \item {\tt IXML_WRONG_DOCUMENT_ERR}: {\bf newAttr} does not belong + * to the same one as {\bf element}. + * \item {\tt IXML_INUSE_ATTRIBUTE_ERR}: {\bf newAttr} is already + * an attribute of another {\bf Element}. + * \end{itemize} + */ + +int +ixmlElement_setAttributeNode(IXML_Element* element, + /** The {\bf Element} in which to add the new + attribute. */ + IXML_Attr* newAttr, + /** The new {\bf Attr} to add. */ + IXML_Attr** rtAttr + /** A pointer to an {\bf Attr} where the old + {\bf Attr} will be stored. This will have + a {\tt NULL} if no prior node + existed. */ + ); + + /** Removes the specified attribute node from an {\bf Element}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element} or + * {\bf oldAttr} is {\tt NULL}. + * \item {\tt IXML_NOT_FOUND_ERR}: {\bf oldAttr} is not among the list + * attributes of {\bf element}. + * \end{itemize} + */ + +int +ixmlElement_removeAttributeNode(IXML_Element* element, + /** The {\bf Element} from which to remove + the attribute. */ + IXML_Attr* oldAttr, + /** The attribute to remove from the {\bf + Element}. */ + IXML_Attr** rtAttr + /** A pointer to an attribute in which to + place the removed attribute. */ + ); + + /** Returns a {\bf NodeList} of all {\it descendant} {\bf Elements} with + * a given tag name, in the order in which they are encountered in a + * pre-order traversal of this {\bf Element} tree. + * + * @return [NodeList*] A {\bf NodeList} of the matching {\bf Element}s or + * {\tt NULL} on an error. + */ + +IXML_NodeList* +ixmlElement_getElementsByTagName(IXML_Element* element, + /** The {\bf Element} from which to start + the search. */ + DOMString tagName + /** The name of the tag for which to + search. */ + ); + +// introduced in DOM 2 + + /** Retrieves an attribute value using the local name and namespace URI. + * + * @return [DOMString] A {\bf DOMString} representing the value of the + * matching attribute. + */ + +DOMString +ixmlElement_getAttributeNS(IXML_Element* element, + /** The {\bf Element} from which to get the + attribute value. */ + DOMString namespaceURI, + /** The namespace URI of the attribute. */ + DOMString localname + /** The local name of the attribute. */ + ); + + /** Adds a new attribute to an {\bf Element} using the local name and + * namespace URI. If another attribute matches the same local name and + * namespace, the prefix is changed to be the prefix part of the + * {\tt qualifiedName} and the value is changed to {\bf value}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element}, + * {\bf namespaceURI}, {\bf qualifiedName}, or {\bf value} is + * {\tt NULL}. + * \item {\tt IXML_INVALID_CHARACTER_ERR}: {\bf qualifiedName} contains + * an invalid character. + * \item {\tt IXML_NAMESPACE_ERR}: Either the {\bf qualifiedName} or + * {\bf namespaceURI} is malformed. Refer to the DOM2-Core for + * possible reasons. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exist + * to complete the operation. + * \item {\tt IXML_FAILED}: The operation could not be completed. + * \end{itemize} + */ + +int +ixmlElement_setAttributeNS(IXML_Element* element, + /** The {\bf Element} on which to set the + attribute. */ + DOMString namespaceURI, + /** The namespace URI of the new attribute. */ + DOMString qualifiedName, + /** The qualified name of the attribute. */ + DOMString value + /** The new value for the attribute. */ + ); + + /** Removes an attribute using the namespace URI and local name. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element}, + * {\bf namespaceURI}, or {\bf localName} is {\tt NULL}. + * \end{itemize} + */ + +int +ixmlElement_removeAttributeNS(IXML_Element* element, + /** The {\bf Element} from which to remove the + the attribute. */ + DOMString namespaceURI, + /** The namespace URI of the attribute. */ + DOMString localName + /** The local name of the attribute.*/ + ); + + /** Retrieves an {\bf Attr} node by local name and namespace URI. + * + * @return [Attr*] A pointer to an {\bf Attr} or {\tt NULL} on an error. + */ + +IXML_Attr* +ixmlElement_getAttributeNodeNS(IXML_Element* element, + /** The {\bf Element} from which to get the + attribute. */ + DOMString namespaceURI, + /** The namespace URI of the attribute. */ + DOMString localName + /** The local name of the attribute. */ + ); + + /** Adds a new attribute node. If an attribute with the same local name + * and namespace URI already exists in the {\bf Element}, the existing + * attribute node is replaced with {\bf newAttr} and the old returned in + * {\bf rcAttr}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: Either {\bf element} or + * {\bf newAttr} is {\tt NULL}. + * \item {\tt IXML_WRONG_DOCUMENT_ERR}: {\bf newAttr} does not belong + * to the same document as {\bf element}. + * \item {\tt IXML_INUSE_ATTRIBUTE_ERR}: {\bf newAttr} already is an + * attribute of another {\bf Element}. + * \end{itemize} + */ + +int +ixmlElement_setAttributeNodeNS(IXML_Element* element, + /** The {\bf Element} in which to add the + attribute node. */ + IXML_Attr* newAttr, + /** The new {\bf Attr} to add. */ + IXML_Attr** rcAttr + /** A pointer to the replaced {\bf Attr}, if + it exists. */ + ); + + /** Returns a {\bf NodeList} of all {\it descendant} {\bf Elements} with a + * given tag name, in the order in which they are encountered in the + * pre-order traversal of the {\bf Element} tree. + * + * @return [NodeList*] A {\bf NodeList} of matching {\bf Element}s or + * {\tt NULL} on an error. + */ + +IXML_NodeList* +ixmlElement_getElementsByTagNameNS(IXML_Element* element, + /** The {\bf Element} from which to start + the search. */ + DOMString namespaceURI, + /** The namespace URI of the {\bf + Element}s to find. */ + DOMString localName + /** The local name of the {\bf Element}s + to find. */ + ); + + /** Queries whether the {\bf Element} has an attribute with the given name + * or a default value. + * + * @return [BOOL] {\tt TRUE} if the {\bf Element} has an attribute with + * this name or has a default value for that attribute, + * otherwise {\tt FALSE}. + */ + +BOOL +ixmlElement_hasAttribute(IXML_Element* element, + /** The {\bf Element} on which to check for an + attribute. */ + DOMString name + /** The name of the attribute for which to check. */ + ); + + /** Queries whether the {\bf Element} has an attribute with the given + * local name and namespace URI or has a default value for that attribute. + * + * @return [BOOL] {\tt TRUE} if the {\bf Element} has an attribute with + * the given namespace and local name or has a default + * value for that attribute, otherwise {\tt FALSE}. + */ + +BOOL +ixmlElement_hasAttributeNS(IXML_Element* element, + /** The {\bf Element} on which to check for the + attribute. */ + DOMString namespaceURI, + /** The namespace URI of the attribute. */ + DOMString localName + /** The local name of the attribute. */ + ); + + /** Frees the given {\bf Element} and any subtree of the {\bf Element}. + * + * @return [void] This function does not return a value. + */ + +void +ixmlElement_free(IXML_Element* element + /** The {\bf Element} to free. */ + ); + +//@} + +/*================================================================ +* +* NamedNodeMap interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it NamedNodeMap} + * A {\bf NamedNodeMap} object represents a list of objects that can be + * accessed by name. A {\bf NamedNodeMap} maintains the objects in + * no particular order. The {\bf Node} interface uses a {\bf NamedNodeMap} + * to maintain the attributes of a node. + */ +//@{ + + /** Returns the number of items contained in this {\bf NamedNodeMap}. + * + * @return [unsigned long] The number of nodes in this map. + */ + +unsigned long +ixmlNamedNodeMap_getLength(IXML_NamedNodeMap *nnMap + /** The {\bf NamedNodeMap} from which to retrieve + the size. */ + ); + + /** Retrieves a {\bf Node} from the {\bf NamedNodeMap} by name. + * + * @return [Node*] A {\bf Node} or {\tt NULL} if there is an error. + */ + +IXML_Node* +ixmlNamedNodeMap_getNamedItem(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} to search. */ + DOMString name + /** The name of the {\bf Node} to find. */ + ); + + /** Adds a new {\bf Node} to the {\bf NamedNodeMap} using the {\bf Node} + * name attribute. + * + * @return [Node*] The old {\bf Node} if the new {\bf Node} replaces it or + * {\tt NULL} if the {\bf Node} was not in the + * {\bf NamedNodeMap} before. + */ + +IXML_Node* +ixmlNamedNodeMap_setNamedItem(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} in which to add the + new {\bf Node}. */ + IXML_Node *arg + /** The new {\bf Node} to add to the {\bf + NamedNodeMap}. */ + ); + + /** Removes a {\bf Node} from a {\bf NamedNodeMap} specified by name. + * + * @return [Node*] A pointer to the {\bf Node}, if found, or {\tt NULL} if + * it wasn't. + */ + +IXML_Node* +ixmlNamedNodeMap_removeNamedItem(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} from which to + remove the item. */ + DOMString name + /** The name of the item to remove. */ + ); + + /** Retrieves a {\bf Node} from a {\bf NamedNodeMap} specified by a + * numerical index. + * + * @return [Node*] A pointer to the {\bf Node}, if found, or {\tt NULL} if + * it wasn't. + */ + +IXML_Node* +ixmlNamedNodeMap_item(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} from which to remove the + {\bf Node}. */ + unsigned long index + /** The index into the map to remove. */ + ); + +// introduced in DOM level 2 + + /** Retrieves a {\bf Node} from a {\bf NamedNodeMap} specified by + * namespace URI and local name. + * + * @return [Node*] A pointer to the {\bf Node}, if found, or {\tt NULL} if + * it wasn't + */ + +IXML_Node* +ixmlNamedNodeMap_getNamedItemNS(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} from which to + remove the {\bf Node}. */ + DOMString *namespaceURI, + /** The namespace URI of the {\bf Node} to + remove. */ + DOMString localName + /** The local name of the {\bf Node} to + remove. */ + ); + + /** Adds a new {\bf Node} to the {\bf NamedNodeMap} using the {\bf Node} + * local name and namespace URI attributes. + * + * @return [Node*] The old {\bf Node} if the new {\bf Node} replaces it or + * {\tt NULL} if the {\bf Node} was not in the + * {\bf NamedNodeMap} before. + */ + +IXML_Node* +ixmlNamedNodeMap_setNamedItemNS(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} in which to add + the {\bf Node}. */ + IXML_Node *arg + /** The {\bf Node} to add to the map. */ + ); + + /** Removes a {\bf Node} from a {\bf NamedNodeMap} specified by + * namespace URI and local name. + * + * @return [Node*] A pointer to the {\bf Node}, if found, or {\tt NULL} if + * it wasn't. + */ + +IXML_Node* +ixmlNamedNodeMap_removeNamedItemNS(IXML_NamedNodeMap *nnMap, + /** The {\bf NamedNodeMap} from which to + remove the {\bf Node}. */ + DOMString namespaceURI, + /** The namespace URI of the {\bf Node} + to remove. */ + DOMString localName + /** The local name of the {\bf Node} to + remove. */ + ); + + /** Frees a {\bf NamedNodeMap}. The {\bf Node}s inside the map are not + * freed, just the {\bf NamedNodeMap} object. + * + * @return [void] This function does not return a value. + */ + +void +ixmlNamedNodeMap_free(IXML_NamedNodeMap *nnMap + /** The {\bf NamedNodeMap to free}. */ + ); + +//@} + +/*================================================================ +* +* NodeList interfaces +* +* +*=================================================================*/ + +/**@name Interface {\it NodeList} + * The {\bf NodeList} interface abstracts an ordered collection of + * nodes. Note that changes to the underlying nodes will change + * the nodes contained in a {\bf NodeList}. The DOM2-Core refers to + * this as being {\it live}. + */ +//@{ + + /** Retrieves a {\bf Node} from a {\bf NodeList} specified by a + * numerical index. + * + * @return [Node*] A pointer to a {\bf Node} or {\tt NULL} if there was an + * error. + */ + +IXML_Node* +ixmlNodeList_item(IXML_NodeList *nList, + /** The {\bf NodeList} from which to retrieve the {\bf + Node}. */ + unsigned long index + /** The index into the {\bf NodeList} to retrieve. */ + ); + + /** Returns the number of {\bf Nodes} in a {\bf NodeList}. + * + * @return [unsigned long] The number of {\bf Nodes} in the {\bf NodeList}. + */ + +unsigned long +ixmlNodeList_length(IXML_NodeList *nList + /** The {\bf NodeList} for which to retrieve the + number of {\bf Nodes}. */ + ); + + /** Frees a {\bf NodeList} object. Since the underlying {\bf Nodes} are + * references, they are not freed using this operating. This only + * frees the {\bf NodeList} object. + * + * @return [void] This function does not return a value. + */ + +void +ixmlNodeList_free(IXML_NodeList *nList + /** The {\bf NodeList} to free. */ + ); + +//@} Interface NodeList +//@} DOM Interfaces + +/**@name IXML API + * The IXML API contains utility functions that are not part of the standard + * DOM interfaces. They include functions to create a DOM structure from a + * file or buffer, create an XML file from a DOM structure, and manipulate + * DOMString objects. + */ +//@{ + +/*================================================================ +* +* ixml interfaces +* +* +*=================================================================*/ + + /** Renders a {\bf Node} and all sub-elements into an XML document + * representation. The caller is required to free the {\bf DOMString} + * returned from this function using {\bf ixmlFreeDOMString} when it + * is no longer required. + * + * Note that this function can be used for any {\bf Node}-derived + * interface. The difference between {\bf ixmlPrintDocument} and + * {\bf ixmlPrintNode} is {\bf ixmlPrintDocument} includes the XML prolog + * while {\bf ixmlPrintNode} only produces XML elements. An XML + * document is not well formed unless it includes the prolog + * and at least one element. + * + * This function introduces lots of white space to print the + * {\bf DOMString} in readable format. + * + * @return [DOMString] A {\bf DOMString} with the XML document representation + * of the DOM tree or {\tt NULL} on an error. + */ + +DOMString +ixmlPrintDocument(IXML_Document *doc); + + /** Renders a {\bf Node} and all sub-elements into an XML text + * representation. The caller is required to free the {\bf DOMString} + * returned from this function using {\bf ixmlFreeDOMString} when it + * is no longer required. + * + * Note that this function can be used for any {\bf Node}-derived + * interface. The difference between {\bf ixmlPrintNode} and + * {\bf ixmlPrintDocument} is {\bf ixmlPrintNode} does not include + * the XML prolog, it only produces XML elements. + * + * This function introduces lots of white space to print the + * {\bf DOMString} in readable format. + * + * @return [DOMString] A {\bf DOMString} with the XML text representation + * of the DOM tree or {\tt NULL} on an error. + */ + +DOMString +ixmlPrintNode(IXML_Node *doc + /** The root of the {\bf Node} tree to render to XML text. */ + ); + + /** Renders a {\bf Node} and all sub-elements into an XML document + * representation. The caller is required to free the {\bf DOMString} + * returned from this function using {\bf ixmlFreeDOMString} when it + * is no longer required. + * + * Note that this function can be used for any {\bf Node}-derived + * interface. The difference between {\bf ixmlDocumenttoString} and + * {\bf ixmlNodetoString} is {\bf ixmlDocumenttoString} includes the XML + * prolog while {\bf ixmlNodetoString} only produces XML elements. An XML + * document is not well formed unless it includes the prolog + * and at least one element. + * + * @return [DOMString] A {\bf DOMString} with the XML text representation + * of the DOM tree or {\tt NULL} on an error. + */ + +DOMString +ixmlDocumenttoString(IXML_Document *doc); + + /** Renders a {\bf Node} and all sub-elements into an XML text + * representation. The caller is required to free the {\bf DOMString} + * returned from this function using {\bf ixmlFreeDOMString} when it + * is no longer required. + * + * Note that this function can be used for any {\bf Node}-derived + * interface. The difference between {\bf ixmlNodetoString} and + * {\bf ixmlDocumenttoString} is {\bf ixmlNodetoString} does not include + * the XML prolog, it only produces XML elements. + * + * @return [DOMString] A {\bf DOMString} with the XML text representation + * of the DOM tree or {\tt NULL} on an error. + */ + +DOMString +ixmlNodetoString(IXML_Node *doc + /** The root of the {\bf Node} tree to render to XML text. */ + ); + + + /** Makes the XML parser more tolerant to malformed text. + * + * If {\bf errorChar} is 0 (default), the parser is strict about XML + * encoding : invalid UTF-8 sequences or "&" entities are rejected, and + * the parsing aborts. + * If {\bf errorChar} is not 0, the parser is relaxed : invalid UTF-8 + * characters are replaced by the {\bf errorChar}, and invalid "&" entities + * are left untranslated. The parsing is then allowed to continue. + */ +void +ixmlRelaxParser(char errorChar); + + + /** Parses an XML text buffer converting it into an IXML DOM representation. + * + * @return [Document*] A {\bf Document} if the buffer correctly parses or + * {\tt NULL} on an error. + */ +IXML_Document* +ixmlParseBuffer(char *buffer + /** The buffer that contains the XML text to convert to a + {\bf Document}. */ + ); + + + /** Parses an XML text buffer converting it into an IXML DOM representation. + * + * The {\bf ixmlParseBufferEx} API differs from the {\bf ixmlParseBuffer} + * API in that it returns an error code representing the actual failure + * rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: The {\bf buffer} is not a valid + * pointer. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlParseBufferEx(char *buffer, + /** The buffer that contains the XML text to convert to a + {\bf Document}. */ + IXML_Document** doc + /** A point to store the {\bf Document} if file correctly + parses or {\bf NULL} on an error. */ + ); + + /** Parses an XML text file converting it into an IXML DOM representation. + * + * @return [Document*] A {\bf Document} if the file correctly parses or + * {\tt NULL} on an error. + */ + +IXML_Document* +ixmlLoadDocument(char* xmlFile + /** The filename of the XML text to convert to a {\bf + Document}. */ + ); + + /** Parses an XML text file converting it into an IXML DOM representation. + * + * The {\bf ixmlLoadDocumentEx} API differs from the {\bf ixmlLoadDocument} + * API in that it returns a an error code representing the actual failure + * rather than just {\tt NULL}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt IXML_SUCCESS}: The operation completed successfully. + * \item {\tt IXML_INVALID_PARAMETER}: The {\bf xmlFile} is not a valid + * pointer. + * \item {\tt IXML_INSUFFICIENT_MEMORY}: Not enough free memory exists + * to complete this operation. + * \end{itemize} + */ + +int +ixmlLoadDocumentEx(char* xmlFile, + /** The filename of the XML text to convert to a {\bf + Document}. */ + IXML_Document** doc + /** A pointer to the {\bf Document} if file correctly + parses or {\bf NULL} on an error. */ + ); + + /** Clones an existing {\bf DOMString}. + * + * @return [DOMString] A new {\bf DOMString} that is a duplicate of the + * original or {\tt NULL} if the operation could not + * be completed. + */ + +DOMString +ixmlCloneDOMString(const DOMString src + /** The source {\bf DOMString} to clone. */ + ); + + /** Frees a {\bf DOMString}. + * + * @return [void] This function does not return a value. + */ + +void +ixmlFreeDOMString(DOMString buf + /** The {\bf DOMString} to free. */ + ); + +#ifdef __cplusplus +} +#endif + +//@} IXML API + +#endif // _IXML_H_ diff --git a/libupnp/ixml/src/attr.c b/libupnp/ixml/src/attr.c new file mode 100644 index 0000000..4f0732d --- /dev/null +++ b/libupnp/ixml/src/attr.c @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "ixmlparser.h" + +/*================================================================ +* Function: Attr_init +* Initializes an attribute node +* External function. +* +*=================================================================*/ +void +ixmlAttr_init( IN IXML_Attr * attr ) +{ + if( attr != NULL ) { + memset( attr, 0, sizeof( IXML_Attr ) ); + } +} + +/*================================================================ +* Function: Attr_free +* Frees an attribute node. +* external function. +* +*=================================================================*/ +void +ixmlAttr_free( IN IXML_Attr * attr ) +{ + if( attr != NULL ) { + ixmlNode_free( ( IXML_Node * ) attr ); + } +} diff --git a/libupnp/ixml/src/document.c b/libupnp/ixml/src/document.c new file mode 100644 index 0000000..a0b8ef5 --- /dev/null +++ b/libupnp/ixml/src/document.c @@ -0,0 +1,804 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "ixmlparser.h" + +/*================================================================ +* ixmlDocument_init +* It initialize the document structure. +* External function. +* +*=================================================================*/ +void +ixmlDocument_init( IN IXML_Document * doc ) +{ + memset( doc, 0, sizeof( IXML_Document ) ); +} + +/*================================================================ +* ixmlDocument_free +* It frees the whole document tree. +* External function. +* +*=================================================================*/ +void +ixmlDocument_free( IN IXML_Document * doc ) +{ + if( doc != NULL ) { + ixmlNode_free( ( IXML_Node * ) doc ); + } + +} + +/*================================================================ +* ixmlDocument_setOwnerDocument +* +* When this function is called first time, nodeptr is the root +* of the subtree, so it is not necessay to do two steps +* recursion. +* +* Internal function called by ixmlDocument_importNode +* +*=================================================================*/ +void +ixmlDocument_setOwnerDocument( IN IXML_Document * doc, + IN IXML_Node * nodeptr ) +{ + if( nodeptr != NULL ) { + nodeptr->ownerDocument = doc; + ixmlDocument_setOwnerDocument( doc, + ixmlNode_getFirstChild( nodeptr ) ); + ixmlDocument_setOwnerDocument( doc, + ixmlNode_getNextSibling + ( nodeptr ) ); + } +} + +/*================================================================ +* ixmlDocument_importNode +* Imports a node from another document to this document. The +* returned node has no parent; (parentNode is null). The source +* node is not altered or removed from the original document; +* this method creates a new copy of the source node. + +* For all nodes, importing a node creates a node object owned +* by the importing document, with attribute values identical to +* the source node's nodeName and nodeType, plus the attributes +* related to namespaces (prefix, localName, and namespaceURI). +* As in the cloneNode operation on a node, the source node is +* not altered. +* +* External function. +* +*=================================================================*/ +int +ixmlDocument_importNode( IN IXML_Document * doc, + IN IXML_Node * importNode, + IN BOOL deep, + OUT IXML_Node ** rtNode ) +{ + unsigned short nodeType; + IXML_Node *newNode; + + *rtNode = NULL; + + if( ( doc == NULL ) || ( importNode == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + nodeType = ixmlNode_getNodeType( importNode ); + if( nodeType == eDOCUMENT_NODE ) { + return IXML_NOT_SUPPORTED_ERR; + } + + newNode = ixmlNode_cloneNode( importNode, deep ); + if( newNode == NULL ) { + return IXML_FAILED; + } + + ixmlDocument_setOwnerDocument( doc, newNode ); + *rtNode = newNode; + + return IXML_SUCCESS; +} + +/*================================================================ +* ixmlDocument_createElementEx +* Creates an element of the type specified. +* External function. +* Parameters: +* doc: pointer to document +* tagName: The name of the element, it is case-sensitive. +* Return Value: +* IXML_SUCCESS +* IXML_INVALID_PARAMETER: if either doc or tagName is NULL +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +*=================================================================*/ +int +ixmlDocument_createElementEx( IN IXML_Document * doc, + IN const DOMString tagName, + OUT IXML_Element ** rtElement ) +{ + + int errCode = IXML_SUCCESS; + IXML_Element *newElement = NULL; + + if( ( doc == NULL ) || ( tagName == NULL ) ) { + errCode = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + newElement = ( IXML_Element * ) malloc( sizeof( IXML_Element ) ); + if( newElement == NULL ) { + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + ixmlElement_init( newElement ); + newElement->tagName = strdup( tagName ); + if( newElement->tagName == NULL ) { + ixmlElement_free( newElement ); + newElement = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + // set the node fields + newElement->n.nodeType = eELEMENT_NODE; + newElement->n.nodeName = strdup( tagName ); + if( newElement->n.nodeName == NULL ) { + ixmlElement_free( newElement ); + newElement = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + newElement->n.ownerDocument = doc; + + ErrorHandler: + *rtElement = newElement; + return errCode; + +} + +/*================================================================ +* ixmlDocument_createElement +* Creates an element of the type specified. +* External function. +* Parameters: +* doc: pointer to document +* tagName: The name of the element, it is case-sensitive. +* Return Value: +* A new element object with the nodeName set to tagName, and +* localName, prefix and namespaceURI set to null. +* +*=================================================================*/ +IXML_Element * +ixmlDocument_createElement( IN IXML_Document * doc, + IN const DOMString tagName ) +{ + IXML_Element *newElement = NULL; + + ixmlDocument_createElementEx( doc, tagName, &newElement ); + return newElement; + +} + +/*================================================================ +* ixmlDocument_createDocumentEx +* Creates an document object +* Internal function. +* Parameters: +* rtDoc: the document created or NULL on failure +* Return Value: +* IXML_SUCCESS +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +*=================================================================*/ +int +ixmlDocument_createDocumentEx( OUT IXML_Document ** rtDoc ) +{ + IXML_Document *doc; + int errCode = IXML_SUCCESS; + + doc = NULL; + doc = ( IXML_Document * ) malloc( sizeof( IXML_Document ) ); + if( doc == NULL ) { + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + ixmlDocument_init( doc ); + + doc->n.nodeName = strdup( DOCUMENTNODENAME ); + if( doc->n.nodeName == NULL ) { + ixmlDocument_free( doc ); + doc = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + doc->n.nodeType = eDOCUMENT_NODE; + doc->n.ownerDocument = doc; + + ErrorHandler: + *rtDoc = doc; + return errCode; +} + +/*================================================================ +* ixmlDocument_createDocument +* Creates an document object +* Internal function. +* Parameters: +* none +* Return Value: +* A new document object with the nodeName set to "#document". +* +*=================================================================*/ +IXML_Document * +ixmlDocument_createDocument( ) +{ + IXML_Document *doc = NULL; + + ixmlDocument_createDocumentEx( &doc ); + + return doc; + +} + +/*================================================================ +* ixmlDocument_createTextNodeEx +* Creates an text node. +* External function. +* Parameters: +* data: text data for the text node. It is stored in nodeValue field. +* Return Value: +* IXML_SUCCESS +* IXML_INVALID_PARAMETER: if either doc or data is NULL +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +*=================================================================*/ +int +ixmlDocument_createTextNodeEx( IN IXML_Document * doc, + IN const char *data, + OUT IXML_Node ** textNode ) +{ + IXML_Node *returnNode; + int rc = IXML_SUCCESS; + + returnNode = NULL; + if( ( doc == NULL ) || ( data == NULL ) ) { + rc = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + returnNode = ( IXML_Node * ) malloc( sizeof( IXML_Node ) ); + if( returnNode == NULL ) { + rc = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + // initialize the node + ixmlNode_init( returnNode ); + + returnNode->nodeName = strdup( TEXTNODENAME ); + if( returnNode->nodeName == NULL ) { + ixmlNode_free( returnNode ); + returnNode = NULL; + rc = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + // add in node value + if( data != NULL ) { + returnNode->nodeValue = strdup( data ); + if( returnNode->nodeValue == NULL ) { + ixmlNode_free( returnNode ); + returnNode = NULL; + rc = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + } + + returnNode->nodeType = eTEXT_NODE; + returnNode->ownerDocument = doc; + + ErrorHandler: + *textNode = returnNode; + return rc; + +} + +/*================================================================ +* ixmlDocument_createTextNode +* Creates an text node. +* External function. +* Parameters: +* data: text data for the text node. It is stored in nodeValue field. +* Return Value: +* The new text node. +* +*=================================================================*/ +IXML_Node * +ixmlDocument_createTextNode( IN IXML_Document * doc, + IN const char *data ) +{ + IXML_Node *returnNode = NULL; + + ixmlDocument_createTextNodeEx( doc, data, &returnNode ); + + return returnNode; +} + +/*================================================================ +* ixmlDocument_createAttributeEx +* Creates an attribute of the given name. +* External function. +* Parameters: +* name: The name of the Attribute node. +* Return Value: +* IXML_SUCCESS +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +================================================================*/ +int +ixmlDocument_createAttributeEx( IN IXML_Document * doc, + IN char *name, + OUT IXML_Attr ** rtAttr ) +{ + IXML_Attr *attrNode = NULL; + int errCode = IXML_SUCCESS; + + attrNode = ( IXML_Attr * ) malloc( sizeof( IXML_Attr ) ); + if( attrNode == NULL ) { + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + if( ( doc == NULL ) || ( name == NULL ) ) { + ixmlAttr_free( attrNode ); + attrNode = NULL; + errCode = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + ixmlAttr_init( attrNode ); + + attrNode->n.nodeType = eATTRIBUTE_NODE; + + // set the node fields + attrNode->n.nodeName = strdup( name ); + if( attrNode->n.nodeName == NULL ) { + ixmlAttr_free( attrNode ); + attrNode = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + attrNode->n.ownerDocument = doc; + + ErrorHandler: + *rtAttr = attrNode; + return errCode; + +} + +/*================================================================ +* ixmlDocument_createAttribute +* Creates an attribute of the given name. +* External function. +* Parameters: +* name: The name of the Attribute node. +* Return Value: +* A new attr object with the nodeName attribute set to the +* given name, and the localName, prefix and namespaceURI set to NULL. +* The value of the attribute is the empty string. +* +================================================================*/ +IXML_Attr * +ixmlDocument_createAttribute( IN IXML_Document * doc, + IN char *name ) +{ + IXML_Attr *attrNode = NULL; + + ixmlDocument_createAttributeEx( doc, name, &attrNode ); + return attrNode; + +} + +/*================================================================ +* ixmlDocument_createAttributeNSEx +* Creates an attrbute of the given name and namespace URI +* External function. +* Parameters: +* namespaceURI: the namespace fo the attribute to create +* qualifiedName: qualifiedName of the attribute to instantiate +* Return Value: +* IXML_SUCCESS +* IXML_INVALID_PARAMETER: if either doc,namespaceURI or qualifiedName is NULL +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +*=================================================================*/ +int +ixmlDocument_createAttributeNSEx( IN IXML_Document * doc, + IN DOMString namespaceURI, + IN DOMString qualifiedName, + OUT IXML_Attr ** rtAttr ) +{ + IXML_Attr *attrNode = NULL; + int errCode = IXML_SUCCESS; + + if( ( doc == NULL ) || ( namespaceURI == NULL ) + || ( qualifiedName == NULL ) ) { + errCode = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + errCode = + ixmlDocument_createAttributeEx( doc, qualifiedName, &attrNode ); + if( errCode != IXML_SUCCESS ) { + goto ErrorHandler; + } + // set the namespaceURI field + attrNode->n.namespaceURI = strdup( namespaceURI ); + if( attrNode->n.namespaceURI == NULL ) { + ixmlAttr_free( attrNode ); + attrNode = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + // set the localName and prefix + errCode = + ixmlNode_setNodeName( ( IXML_Node * ) attrNode, qualifiedName ); + if( errCode != IXML_SUCCESS ) { + ixmlAttr_free( attrNode ); + attrNode = NULL; + goto ErrorHandler; + } + + ErrorHandler: + *rtAttr = attrNode; + return errCode; + +} + +/*================================================================ +* ixmlDocument_createAttributeNS +* Creates an attrbute of the given name and namespace URI +* External function. +* Parameters: +* namespaceURI: the namespace fo the attribute to create +* qualifiedName: qualifiedName of the attribute to instantiate +* Return Value: +* Creates an attribute node with the given namespaceURI and +* qualifiedName. The prefix and localname are extracted from +* the qualifiedName. The node value is empty. +* +*=================================================================*/ +IXML_Attr * +ixmlDocument_createAttributeNS( IN IXML_Document * doc, + IN DOMString namespaceURI, + IN DOMString qualifiedName ) +{ + IXML_Attr *attrNode = NULL; + + ixmlDocument_createAttributeNSEx( doc, namespaceURI, qualifiedName, + &attrNode ); + return attrNode; +} + +/*================================================================ +* ixmlDocument_createCDATASectionEx +* Creates an CDATASection node whose value is the specified string +* External function. +* Parameters: +* data: the data for the CDATASection contents. +* Return Value: +* IXML_SUCCESS +* IXML_INVALID_PARAMETER: if either doc or data is NULL +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +*=================================================================*/ +int +ixmlDocument_createCDATASectionEx( IN IXML_Document * doc, + IN DOMString data, + OUT IXML_CDATASection ** rtCD ) +{ + int errCode = IXML_SUCCESS; + IXML_CDATASection *cDSectionNode = NULL; + + if( ( doc == NULL ) || ( data == NULL ) ) { + errCode = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + cDSectionNode = + ( IXML_CDATASection * ) malloc( sizeof( IXML_CDATASection ) ); + if( cDSectionNode == NULL ) { + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + ixmlCDATASection_init( cDSectionNode ); + + cDSectionNode->n.nodeType = eCDATA_SECTION_NODE; + cDSectionNode->n.nodeName = strdup( CDATANODENAME ); + if( cDSectionNode->n.nodeName == NULL ) { + ixmlCDATASection_free( cDSectionNode ); + cDSectionNode = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + cDSectionNode->n.nodeValue = strdup( data ); + if( cDSectionNode->n.nodeValue == NULL ) { + ixmlCDATASection_free( cDSectionNode ); + cDSectionNode = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + cDSectionNode->n.ownerDocument = doc; + + ErrorHandler: + *rtCD = cDSectionNode; + return errCode; + +} + +/*================================================================ +* ixmlDocument_createCDATASection +* Creates an CDATASection node whose value is the specified string +* External function. +* Parameters: +* data: the data for the CDATASection contents. +* Return Value: +* The new CDATASection object. +* +*=================================================================*/ +IXML_CDATASection * +ixmlDocument_createCDATASection( IN IXML_Document * doc, + IN DOMString data ) +{ + + IXML_CDATASection *cDSectionNode = NULL; + + ixmlDocument_createCDATASectionEx( doc, data, &cDSectionNode ); + return cDSectionNode; +} + +/*================================================================ +* ixmlDocument_createElementNSEx +* Creates an element of the given qualified name and namespace URI. +* External function. +* Parameters: +* namespaceURI: the namespace URI of the element to create. +* qualifiedName: the qualified name of the element to instantiate. +* Return Value: +* Return Value: +* IXML_SUCCESS +* IXML_INVALID_PARAMETER: if either doc,namespaceURI or qualifiedName is NULL +* IXML_INSUFFICIENT_MEMORY: if not enough memory to finish this operations. +* +*=================================================================*/ +int +ixmlDocument_createElementNSEx( IN IXML_Document * doc, + IN DOMString namespaceURI, + IN DOMString qualifiedName, + OUT IXML_Element ** rtElement ) +{ + + IXML_Element *newElement = NULL; + int errCode = IXML_SUCCESS; + + if( ( doc == NULL ) || ( namespaceURI == NULL ) + || ( qualifiedName == NULL ) ) { + errCode = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + errCode = + ixmlDocument_createElementEx( doc, qualifiedName, &newElement ); + if( errCode != IXML_SUCCESS ) { + goto ErrorHandler; + } + // set the namespaceURI field + newElement->n.namespaceURI = strdup( namespaceURI ); + if( newElement->n.namespaceURI == NULL ) { + ixmlElement_free( newElement ); + newElement = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + // set the localName and prefix + errCode = + ixmlNode_setNodeName( ( IXML_Node * ) newElement, qualifiedName ); + if( errCode != IXML_SUCCESS ) { + ixmlElement_free( newElement ); + newElement = NULL; + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + newElement->n.nodeValue = NULL; + + ErrorHandler: + *rtElement = newElement; + return errCode; + +} + +/*================================================================ +* ixmlDocument_createElementNS +* Creates an element of the given qualified name and namespace URI. +* External function. +* Parameters: +* namespaceURI: the namespace URI of the element to create. +* qualifiedName: the qualified name of the element to instantiate. +* Return Value: +* The new element object with tagName qualifiedName, prefix and +* localName extraced from qualfiedName, nodeName of qualfiedName, +* namespaceURI of namespaceURI. +* +*=================================================================*/ +IXML_Element * +ixmlDocument_createElementNS( IN IXML_Document * doc, + IN DOMString namespaceURI, + IN DOMString qualifiedName ) +{ + IXML_Element *newElement = NULL; + + ixmlDocument_createElementNSEx( doc, namespaceURI, qualifiedName, + &newElement ); + return newElement; +} + +/*================================================================ +* ixmlDocument_getElementsByTagName +* Returns a nodeList of all the Elements with a given tag name +* in the order in which they are encountered in a preorder traversal +* of the document tree. +* External function. +* Parameters: +* tagName: the name of the tag to match on. The special value "*" +* matches all tags. +* Return Value: +* A new nodeList object containing all the matched Elements. +* +*=================================================================*/ +IXML_NodeList * +ixmlDocument_getElementsByTagName( IN IXML_Document * doc, + IN char *tagName ) +{ + IXML_NodeList *returnNodeList = NULL; + + if( ( doc == NULL ) || ( tagName == NULL ) ) { + return NULL; + } + + ixmlNode_getElementsByTagName( ( IXML_Node * ) doc, tagName, + &returnNodeList ); + return returnNodeList; +} + +/*================================================================ +* ixmlDocument_getElementsByTagNameNS +* Returns a nodeList of all the Elements with a given local name and +* namespace URI in the order in which they are encountered in a +* preorder traversal of the document tree. +* External function. +* Parameters: +* namespaceURI: the namespace of the elements to match on. The special +* value "*" matches all namespaces. +* localName: the local name of the elements to match on. The special +* value "*" matches all local names. +* Return Value: +* A new nodeList object containing all the matched Elements. +* +*=================================================================*/ +IXML_NodeList * +ixmlDocument_getElementsByTagNameNS( IN IXML_Document * doc, + IN DOMString namespaceURI, + IN DOMString localName ) +{ + IXML_NodeList *returnNodeList = NULL; + + if( ( doc == NULL ) || ( namespaceURI == NULL ) + || ( localName == NULL ) ) { + return NULL; + } + + ixmlNode_getElementsByTagNameNS( ( IXML_Node * ) doc, namespaceURI, + localName, &returnNodeList ); + return returnNodeList; +} + +/*================================================================ +* ixmlDocument_getElementById +* Returns the element whose ID is given by tagName. If no such +* element exists, returns null. +* External function. +* Parameter: +* tagName: the tag name for an element. +* Return Values: +* The matching element. +* +*=================================================================*/ +IXML_Element * +ixmlDocument_getElementById( IN IXML_Document * doc, + IN DOMString tagName ) +{ + IXML_Element *rtElement = NULL; + IXML_Node *nodeptr = ( IXML_Node * ) doc; + const char *name; + + if( ( nodeptr == NULL ) || ( tagName == NULL ) ) { + return rtElement; + } + + if( ixmlNode_getNodeType( nodeptr ) == eELEMENT_NODE ) { + name = ixmlNode_getNodeName( nodeptr ); + if( name == NULL ) { + return rtElement; + } + + if( strcmp( tagName, name ) == 0 ) { + rtElement = ( IXML_Element * ) nodeptr; + return rtElement; + } else { + rtElement = ixmlDocument_getElementById( ( IXML_Document * ) + ixmlNode_getFirstChild + ( nodeptr ), + tagName ); + if( rtElement == NULL ) { + rtElement = ixmlDocument_getElementById( ( IXML_Document + * ) + ixmlNode_getNextSibling + ( nodeptr ), + tagName ); + } + } + } else { + rtElement = ixmlDocument_getElementById( ( IXML_Document * ) + ixmlNode_getFirstChild + ( nodeptr ), tagName ); + if( rtElement == NULL ) { + rtElement = ixmlDocument_getElementById( ( IXML_Document * ) + ixmlNode_getNextSibling + ( nodeptr ), + tagName ); + } + } + + return rtElement; +} diff --git a/libupnp/ixml/src/element.c b/libupnp/ixml/src/element.c new file mode 100644 index 0000000..0f57c4e --- /dev/null +++ b/libupnp/ixml/src/element.c @@ -0,0 +1,969 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "ixmlparser.h" + +/*================================================================ +* ixmlElement_Init +* Initializes an element node. +* External function. +* +*=================================================================*/ +void +ixmlElement_init( IN IXML_Element * element ) +{ + if( element != NULL ) { + memset( element, 0, sizeof( IXML_Element ) ); + } +} + +/*================================================================ +* ixmlElement_getTagName +* Gets the element node's tagName +* External function. +* +*=================================================================*/ +const DOMString +ixmlElement_getTagName( IN IXML_Element * element ) +{ + + if( element != NULL ) { + return element->tagName; + } else { + return NULL; + } +} + +/*================================================================ +* ixmlElement_setTagName +* Sets the given element's tagName. +* Parameters: +* tagName: new tagName for the element. +* +*=================================================================*/ +int +ixmlElement_setTagName( IN IXML_Element * element, + IN char *tagName ) +{ + int rc = IXML_SUCCESS; + + assert( ( element != NULL ) && ( tagName != NULL ) ); + if( ( element == NULL ) || ( tagName == NULL ) ) { + return IXML_FAILED; + } + + if( element->tagName != NULL ) { + free( element->tagName ); + } + + element->tagName = strdup( tagName ); + if( element->tagName == NULL ) { + rc = IXML_INSUFFICIENT_MEMORY; + } + + return rc; + +} + +/*================================================================ +* ixmlElement_getAttribute +* Retrievea an attribute value by name. +* External function. +* Parameters: +* name: the name of the attribute to retrieve. +* Return Values: +* attribute value as a string, or the empty string if that attribute +* does not have a specified value. +* +*=================================================================*/ +DOMString +ixmlElement_getAttribute( IN IXML_Element * element, + IN DOMString name ) +{ + IXML_Node *attrNode; + + if( ( element == NULL ) || ( name == NULL ) ) { + return NULL; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, name ) == 0 ) { // found it + return attrNode->nodeValue; + } else { + attrNode = attrNode->nextSibling; + } + } + + return NULL; +} + +/*================================================================ +* ixmlElement_setAttribute +* Adds a new attribute. If an attribute with that name is already +* present in the element, its value is changed to be that of the value +* parameter. If not, a new attribute is inserted into the element. +* +* External function. +* Parameters: +* name: the name of the attribute to create or alter. +* value: value to set in string form +* Return Values: +* IXML_SUCCESS or failure code. +* +*=================================================================*/ +int +ixmlElement_setAttribute( IN IXML_Element * element, + IN char *name, + IN char *value ) +{ + IXML_Node *attrNode; + IXML_Attr *newAttrNode; + short errCode = IXML_SUCCESS; + + if( ( element == NULL ) || ( name == NULL ) || ( value == NULL ) ) { + errCode = IXML_INVALID_PARAMETER; + goto ErrorHandler; + } + + if( Parser_isValidXmlName( name ) == FALSE ) { + errCode = IXML_INVALID_CHARACTER_ERR; + goto ErrorHandler; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, name ) == 0 ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + if( attrNode == NULL ) { // add a new attribute + errCode = + ixmlDocument_createAttributeEx( ( IXML_Document * ) element->n. + ownerDocument, name, + &newAttrNode ); + if( errCode != IXML_SUCCESS ) { + goto ErrorHandler; + } + + attrNode = ( IXML_Node * ) newAttrNode; + + attrNode->nodeValue = strdup( value ); + if( attrNode->nodeValue == NULL ) { + ixmlAttr_free( newAttrNode ); + errCode = IXML_INSUFFICIENT_MEMORY; + goto ErrorHandler; + } + + errCode = + ixmlElement_setAttributeNode( element, newAttrNode, NULL ); + if( errCode != IXML_SUCCESS ) { + ixmlAttr_free( newAttrNode ); + goto ErrorHandler; + } + + } else { + if( attrNode->nodeValue != NULL ) { // attribute name has a value already + free( attrNode->nodeValue ); + } + + attrNode->nodeValue = strdup( value ); + if( attrNode->nodeValue == NULL ) { + errCode = IXML_INSUFFICIENT_MEMORY; + } + } + + ErrorHandler: + return errCode; +} + +/*================================================================ +* ixmlElement_removeAttribute +* Removes an attribute value by name. The attribute node is +* not removed. +* External function. +* Parameters: +* name: the name of the attribute to remove. +* Return Values: +* IXML_SUCCESS or error code. +* +*=================================================================*/ +int +ixmlElement_removeAttribute( IN IXML_Element * element, + IN char *name ) +{ + + IXML_Node *attrNode; + + if( ( element == NULL ) || ( name == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, name ) == 0 ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + if( attrNode != NULL ) { // has the attribute + if( attrNode->nodeValue != NULL ) { + free( attrNode->nodeValue ); + attrNode->nodeValue = NULL; + } + } + + return IXML_SUCCESS; +} + +/*================================================================ +* ixmlElement_getAttributeNode +* Retrieve an attribute node by name. +* External function. +* Parameters: +* name: the name(nodeName) of the attribute to retrieve. +* Return Value: +* The attr node with the specified name (nodeName) or NULL if +* there is no such attribute. +* +*=================================================================*/ +IXML_Attr * +ixmlElement_getAttributeNode( IN IXML_Element * element, + IN char *name ) +{ + + IXML_Node *attrNode; + + if( ( element == NULL ) || ( name == NULL ) ) { + return NULL; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, name ) == 0 ) { // found it + break; + } else { + attrNode = attrNode->nextSibling; + } + } + + return ( IXML_Attr * ) attrNode; + +} + +/*================================================================ +* ixmlElement_setAttributeNode +* Adds a new attribute node. If an attribute with that name(nodeName) +* is already present in the element, it is replaced by the new one. +* External function. +* Parameters: +* The attr node to add to the attribute list. +* Return Value: +* if newAttr replaces an existing attribute, the replaced +* attr node is returned, otherwise NULL is returned. +* +*=================================================================*/ +int +ixmlElement_setAttributeNode( IN IXML_Element * element, + IN IXML_Attr * newAttr, + OUT IXML_Attr ** rtAttr ) +{ + + IXML_Node *attrNode; + IXML_Node *node; + IXML_Node *nextAttr = NULL; + IXML_Node *prevAttr = NULL; + IXML_Node *preSib, + *nextSib; + + if( ( element == NULL ) || ( newAttr == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + if( newAttr->n.ownerDocument != element->n.ownerDocument ) { + return IXML_WRONG_DOCUMENT_ERR; + } + + if( newAttr->ownerElement != NULL ) { + return IXML_INUSE_ATTRIBUTE_ERR; + } + + newAttr->ownerElement = element; + node = ( IXML_Node * ) newAttr; + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, node->nodeName ) == 0 ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + if( attrNode != NULL ) // already present, will replace by newAttr + { + preSib = attrNode->prevSibling; + nextSib = attrNode->nextSibling; + + if( preSib != NULL ) { + preSib->nextSibling = node; + } + + if( nextSib != NULL ) { + nextSib->prevSibling = node; + } + + if( element->n.firstAttr == attrNode ) { + element->n.firstAttr = node; + } + + if( rtAttr != NULL ) { + *rtAttr = ( IXML_Attr * ) attrNode; + } + } else // add this attribute + { + if( element->n.firstAttr != NULL ) { + prevAttr = element->n.firstAttr; + nextAttr = prevAttr->nextSibling; + while( nextAttr != NULL ) { + prevAttr = nextAttr; + nextAttr = prevAttr->nextSibling; + } + prevAttr->nextSibling = node; + node->prevSibling = prevAttr; + } else // this is the first attribute node + { + element->n.firstAttr = node; + node->prevSibling = NULL; + node->nextSibling = NULL; + } + + if( rtAttr != NULL ) { + *rtAttr = NULL; + } + } + + return IXML_SUCCESS; +} + +/*================================================================ +* ixmlElement_findAttributeNode +* Find a attribute node whose contents are the same as the oldAttr. +* Internal only to parser. +* Parameter: +* oldAttr: the attribute node to match +* Return: +* if found it, the attribute node is returned, +* otherwise, return NULL. +* +*=================================================================*/ +IXML_Node * +ixmlElement_findAttributeNode( IN IXML_Element * element, + IN IXML_Attr * oldAttr ) +{ + IXML_Node *attrNode; + IXML_Node *oldAttrNode = ( IXML_Node * ) oldAttr; + + assert( ( element != NULL ) && ( oldAttr != NULL ) ); + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { // parentNode, prevSib, nextSib and ownerDocument doesn't matter + if( ixmlNode_compare( attrNode, oldAttrNode ) == TRUE ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + return attrNode; + +} + +/*================================================================ +* ixmlElement_removeAttributeNode +* Removes the specified attribute node. +* External function. +* +* Parameters: +* oldAttr: the attr node to remove from the attribute list. +* +* Return Value: +* IXML_SUCCESS or failure +* +*=================================================================*/ +int +ixmlElement_removeAttributeNode( IN IXML_Element * element, + IN IXML_Attr * oldAttr, + OUT IXML_Attr ** rtAttr ) +{ + IXML_Node *attrNode; + IXML_Node *preSib, + *nextSib; + + if( ( element == NULL ) || ( oldAttr == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + attrNode = ixmlElement_findAttributeNode( element, oldAttr ); + if( attrNode != NULL ) { // has the attribute + preSib = attrNode->prevSibling; + nextSib = attrNode->nextSibling; + + if( preSib != NULL ) { + preSib->nextSibling = nextSib; + } + + if( nextSib != NULL ) { + nextSib->prevSibling = preSib; + } + + if( element->n.firstAttr == attrNode ) { + element->n.firstAttr = nextSib; + } + + /* ( IXML_Attr * ) */ attrNode->parentNode = NULL; + /* ( IXML_Attr * ) */ attrNode->prevSibling = NULL; + /* ( IXML_Attr * ) */ attrNode->nextSibling = NULL; + *rtAttr = ( IXML_Attr * ) attrNode; + return IXML_SUCCESS; + + } else { + return IXML_NOT_FOUND_ERR; + } + +} + +/*================================================================ +* ixmlElement_getElementsByTagName +* Returns a nodeList of all descendant Elements with a given +* tag name, in the order in which they are encountered in a preorder +* traversal of this element tree. +* External function. +* +* Parameters: +* tagName: the name of the tag to match on. The special value "*" +* matches all tags. +* +* Return Value: +* a nodeList of matching element nodes. +* +*=================================================================*/ +IXML_NodeList * +ixmlElement_getElementsByTagName( IN IXML_Element * element, + IN char *tagName ) +{ + IXML_NodeList *returnNodeList = NULL; + + if( ( element != NULL ) && ( tagName != NULL ) ) { + ixmlNode_getElementsByTagName( ( IXML_Node * ) element, tagName, + &returnNodeList ); + } + return returnNodeList; +} + +/*================================================================ +* ixmlElement_getAttributeNS +* Retrieves an attribute value by local name and namespace URI. +* External function. +* +* Parameters: +* namespaceURI: the namespace URI of the attribute to retrieve. +* localName: the local name of the attribute to retrieve. +* +* Return Value: +* the attr value as a string, or NULL if that attribute does +* not have the specified value. +* +*=================================================================*/ +DOMString +ixmlElement_getAttributeNS( IN IXML_Element * element, + IN DOMString namespaceURI, + IN DOMString localName ) +{ + IXML_Node *attrNode; + + if( ( element == NULL ) || ( namespaceURI == NULL ) + || ( localName == NULL ) ) { + return NULL; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->localName, localName ) == 0 && strcmp( attrNode->namespaceURI, namespaceURI ) == 0 ) { // found it + return attrNode->nodeValue; + } else { + attrNode = attrNode->nextSibling; + } + } + + return NULL; + +} + +/*================================================================ +* ixmlElement_setAttributeNS +* Adds a new attribute. If an attribute with the same local name +* and namespace URI is already present on the element, its prefix +* is changed to be the prefix part of the qualifiedName, and its +* value is changed to be the value parameter. This value is a +* simple string. +* External function. +* +* Parameter: +* namespaceURI: the namespace of the attribute to create or alter. +* qualifiedName: the qualified name of the attribute to create or alter. +* value: the value to set in string form. +* +* Return Value: +* IXML_SUCCESS or failure +* +*=================================================================*/ +int +ixmlElement_setAttributeNS( IN IXML_Element * element, + IN DOMString namespaceURI, + IN DOMString qualifiedName, + IN DOMString value ) +{ + IXML_Node *attrNode = NULL; + IXML_Node newAttrNode; + IXML_Attr *newAttr; + int rc; + + if( ( element == NULL ) || ( namespaceURI == NULL ) || + ( qualifiedName == NULL ) || ( value == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + if( Parser_isValidXmlName( qualifiedName ) == FALSE ) { + return IXML_INVALID_CHARACTER_ERR; + } + + ixmlNode_init( &newAttrNode ); + + newAttrNode.nodeName = strdup( qualifiedName ); + if( newAttrNode.nodeName == NULL ) { + return IXML_INSUFFICIENT_MEMORY; + } + + rc = Parser_setNodePrefixAndLocalName( &newAttrNode ); + if( rc != IXML_SUCCESS ) { + Parser_freeNodeContent( &newAttrNode ); + return rc; + } + // see DOM 2 spec page 59 + if( ( newAttrNode.prefix != NULL && namespaceURI == NULL ) || + ( strcmp( newAttrNode.prefix, "xml" ) == 0 && + strcmp( namespaceURI, + "http://www.w3.org/XML/1998/namespace" ) != 0 ) + || ( strcmp( qualifiedName, "xmlns" ) == 0 + && strcmp( namespaceURI, + "http://www.w3.org/2000/xmlns/" ) != 0 ) ) { + Parser_freeNodeContent( &newAttrNode ); + return IXML_NAMESPACE_ERR; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->localName, newAttrNode.localName ) == 0 && + strcmp( attrNode->namespaceURI, namespaceURI ) == 0 ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + if( attrNode != NULL ) { + if( attrNode->prefix != NULL ) { + free( attrNode->prefix ); // remove the old prefix + } + // replace it with the new prefix + attrNode->prefix = strdup( newAttrNode.prefix ); + if( attrNode->prefix == NULL ) { + Parser_freeNodeContent( &newAttrNode ); + return IXML_INSUFFICIENT_MEMORY; + } + + if( attrNode->nodeValue != NULL ) { + free( attrNode->nodeValue ); + } + + attrNode->nodeValue = strdup( value ); + if( attrNode->nodeValue == NULL ) { + free( attrNode->prefix ); + Parser_freeNodeContent( &newAttrNode ); + return IXML_INSUFFICIENT_MEMORY; + } + + } else { + // add a new attribute + rc = ixmlDocument_createAttributeNSEx( ( IXML_Document * ) + element->n.ownerDocument, + namespaceURI, qualifiedName, + &newAttr ); + if( rc != IXML_SUCCESS ) { + return rc; + } + + newAttr->n.nodeValue = strdup( value ); + if( newAttr->n.nodeValue == NULL ) { + ixmlAttr_free( newAttr ); + return IXML_INSUFFICIENT_MEMORY; + } + + if( ixmlElement_setAttributeNodeNS( element, newAttr, NULL ) != + IXML_SUCCESS ) { + ixmlAttr_free( newAttr ); + return IXML_FAILED; + } + + } + + Parser_freeNodeContent( &newAttrNode ); + return IXML_SUCCESS; +} + +/*================================================================ +* ixmlElement_removeAttributeNS +* Removes an attribute by local name and namespace URI. The replacing +* attribute has the same namespace URI and local name, as well as +* the original prefix. +* External function. +* +* Parameters: +* namespaceURI: the namespace URI of the attribute to remove. +* localName: the local name of the atribute to remove. +* +* Return Value: +* IXML_SUCCESS or failure. +* +*=================================================================*/ +int +ixmlElement_removeAttributeNS( IN IXML_Element * element, + IN DOMString namespaceURI, + IN DOMString localName ) +{ + IXML_Node *attrNode; + + if( ( element == NULL ) || ( namespaceURI == NULL ) + || ( localName == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->localName, localName ) == 0 && + strcmp( attrNode->namespaceURI, namespaceURI ) == 0 ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + if( attrNode != NULL ) { // has the attribute + if( attrNode->nodeValue != NULL ) { + free( attrNode->nodeValue ); + attrNode->nodeValue = NULL; + } + } + + return IXML_SUCCESS; + +} + +/*================================================================ +* ixmlElement_getAttributeNodeNS +* Retrieves an attr node by local name and namespace URI. +* External function. +* +* Parameter: +* namespaceURI: the namespace of the attribute to retrieve. +* localName: the local name of the attribute to retrieve. +* +* Return Value: +* The attr node with the specified attribute local name and +* namespace URI or null if there is no such attribute. +* +*=================================================================*/ +IXML_Attr * +ixmlElement_getAttributeNodeNS( IN IXML_Element * element, + IN DOMString namespaceURI, + IN DOMString localName ) +{ + + IXML_Node *attrNode; + + if( ( element == NULL ) || ( namespaceURI == NULL ) + || ( localName == NULL ) ) { + return NULL; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->localName, localName ) == 0 && strcmp( attrNode->namespaceURI, namespaceURI ) == 0 ) { // found it + break; + } else { + attrNode = attrNode->nextSibling; + } + } + + return ( IXML_Attr * ) attrNode; + +} + +/*================================================================ +* ixmlElement_setAttributeNodeNS +* Adds a new attribute. If an attribute with that local name and +* that namespace URI is already present in the element, it is replaced +* by the new one. +* External function. +* +* Parameter: +* newAttr: the attr node to add to the attribute list. +* +* Return Value: +* If the newAttr attribute replaces an existing attribute with the +* same local name and namespace, the replaced attr node is returned, +* otherwise null is returned. +* +*=================================================================*/ +int +ixmlElement_setAttributeNodeNS( IN IXML_Element * element, + IN IXML_Attr * newAttr, + OUT IXML_Attr ** rtAttr ) +{ + IXML_Node *attrNode; + IXML_Node *node; + IXML_Node *prevAttr = NULL, + *nextAttr = NULL; + IXML_Node *preSib, + *nextSib; + + if( ( element == NULL ) || ( newAttr == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + if( newAttr->n.ownerDocument != element->n.ownerDocument ) { + return IXML_WRONG_DOCUMENT_ERR; + } + + if( ( newAttr->ownerElement != NULL ) + && ( newAttr->ownerElement != element ) ) { + return IXML_INUSE_ATTRIBUTE_ERR; + } + + newAttr->ownerElement = element; + node = ( IXML_Node * ) newAttr; + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->localName, node->localName ) == 0 && + strcmp( attrNode->namespaceURI, node->namespaceURI ) == 0 ) { + break; //found it + } else { + attrNode = attrNode->nextSibling; + } + } + + if( attrNode != NULL ) // already present, will replace by newAttr + { + preSib = attrNode->prevSibling; + nextSib = attrNode->nextSibling; + + if( preSib != NULL ) { + preSib->nextSibling = node; + } + + if( nextSib != NULL ) { + nextSib->prevSibling = node; + } + + if( element->n.firstAttr == attrNode ) { + element->n.firstAttr = node; + } + + *rtAttr = ( IXML_Attr * ) attrNode; + + } else // add this attribute + { + if( element->n.firstAttr != NULL ) // element has attribute already + { + prevAttr = element->n.firstAttr; + nextAttr = prevAttr->nextSibling; + while( nextAttr != NULL ) { + prevAttr = nextAttr; + nextAttr = prevAttr->nextSibling; + } + prevAttr->nextSibling = node; + } else // this is the first attribute node + { + element->n.firstAttr = node; + node->prevSibling = NULL; + node->nextSibling = NULL; + } + + if( rtAttr != NULL ) { + *rtAttr = NULL; + } + } + + return IXML_SUCCESS; +} + +/*================================================================ +* ixmlElement_getElementsByTagNameNS +* Returns a nodeList of all the descendant Elements with a given +* local name and namespace in the order in which they are encountered +* in a preorder traversal of the element tree. +* External function. +* +* Parameters: +* namespaceURI: the namespace URI of the elements to match on. The +* special value "*" matches all namespaces. +* localName: the local name of the elements to match on. The special +* value "*" matches all local names. +* +* Return Value: +* A new nodeList object containing all the matched Elements. +* +*=================================================================*/ +IXML_NodeList * +ixmlElement_getElementsByTagNameNS( IN IXML_Element * element, + IN DOMString namespaceURI, + IN DOMString localName ) +{ + IXML_Node *node = ( IXML_Node * ) element; + IXML_NodeList *nodeList = NULL; + + if( ( element != NULL ) && ( namespaceURI != NULL ) + && ( localName != NULL ) ) { + ixmlNode_getElementsByTagNameNS( node, namespaceURI, localName, + &nodeList ); + } + + return nodeList; +} + +/*================================================================ +* ixmlElement_hasAttribute +* Returns true when an attribute with a given name is specified on +* this element, false otherwise. +* External function. +* +* Parameters: +* name: the name of the attribute to look for. +* +* Return Value: +* ture if an attribute with the given name is specified on this +* element, false otherwise. +* +*=================================================================*/ +BOOL +ixmlElement_hasAttribute( IN IXML_Element * element, + IN DOMString name ) +{ + + IXML_Node *attrNode; + + if( ( element == NULL ) || ( name == NULL ) ) { + return FALSE; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, name ) == 0 ) { + return TRUE; + } else { + attrNode = attrNode->nextSibling; + } + } + + return FALSE; +} + +/*================================================================ +* ixmlElement_hasAttributeNS +* Returns true when attribute with a given local name and namespace +* URI is specified on this element, false otherwise. +* External function. +* +* Parameters: +* namespaceURI: the namespace URI of the attribute to look for. +* localName: the local name of the attribute to look for. +* +* Return Value: +* true if an attribute with the given local name and namespace URI +* is specified, false otherwise. +* +*=================================================================*/ +BOOL +ixmlElement_hasAttributeNS( IN IXML_Element * element, + IN DOMString namespaceURI, + IN DOMString localName ) +{ + + IXML_Node *attrNode; + + if( ( element == NULL ) || ( namespaceURI == NULL ) + || ( localName == NULL ) ) { + return FALSE; + } + + attrNode = element->n.firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->localName, localName ) == 0 && + strcmp( attrNode->namespaceURI, namespaceURI ) == 0 ) { + return TRUE; + } else { + attrNode = attrNode->nextSibling; + } + } + + return FALSE; +} + +/*================================================================ +* ixmlElement_free +* frees a element node. +* External function. +* +*=================================================================*/ +void +ixmlElement_free( IN IXML_Element * element ) +{ + if( element != NULL ) { + ixmlNode_free( ( IXML_Node * ) element ); + } +} diff --git a/libupnp/ixml/src/inc/ixmlmembuf.h b/libupnp/ixml/src/inc/ixmlmembuf.h new file mode 100644 index 0000000..4ab756f --- /dev/null +++ b/libupnp/ixml/src/inc/ixmlmembuf.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _IXML_MEMBUF_H +#define _IXML_MEMBUF_H + +#include +#include "ixml.h" + +#define MINVAL( a, b ) ( (a) < (b) ? (a) : (b) ) +#define MAXVAL( a, b ) ( (a) > (b) ? (a) : (b) ) + +#define XINLINE inline + +#define MEMBUF_DEF_SIZE_INC 20 + + +typedef struct // ixml_membuf +{ + char *buf; + + size_t length; + size_t capacity; + size_t size_inc; + +} ixml_membuf; + +//-------------------------------------------------- +//////////////// functions ///////////////////////// +//-------------------------------------------------- +/* +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +*/ + +void ixml_membuf_init(INOUT ixml_membuf *m); +void ixml_membuf_destroy(INOUT ixml_membuf *m); +int ixml_membuf_assign(INOUT ixml_membuf *m, IN const void *buf, + IN size_t buf_len ); +int ixml_membuf_assign_str(INOUT ixml_membuf *m, IN const char *c_str ); +int ixml_membuf_append(INOUT ixml_membuf *m, IN const void *buf); +int ixml_membuf_append_str(INOUT ixml_membuf *m, IN const char *c_str); +int ixml_membuf_insert(INOUT ixml_membuf *m, IN const void* buf, + IN size_t buf_len, int index ); + +#endif // _IXML_MEMBUF_H diff --git a/libupnp/ixml/src/inc/ixmlparser.h b/libupnp/ixml/src/inc/ixmlparser.h new file mode 100644 index 0000000..e056d5d --- /dev/null +++ b/libupnp/ixml/src/inc/ixmlparser.h @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _IXMLPARSER_H +#define _IXMLPARSER_H + +#include "ixml.h" +#include "ixmlmembuf.h" + +// Parser definitions +#define QUOT """ +#define LT "<" +#define GT ">" +#define APOS "'" +#define AMP "&" +#define ESC_HEX "&#x" +#define ESC_DEC "&#" + +typedef struct _IXML_NamespaceURI +{ + char *nsURI; + char *prefix; + struct _IXML_NamespaceURI *nextNsURI; +} IXML_NamespaceURI; + + +typedef struct _IXML_ElementStack +{ + char *element; + char *prefix; + char *namespaceUri; + IXML_NamespaceURI *pNsURI; + struct _IXML_ElementStack *nextElement; +} IXML_ElementStack; + + +typedef enum +{ + eELEMENT, + eATTRIBUTE, + eCONTENT, +} PARSER_STATE; + +typedef struct _Parser +{ + char *dataBuffer; //data buffer + char *curPtr; //ptr to the token parsed + char *savePtr; //Saves for backup + ixml_membuf lastElem; + ixml_membuf tokenBuf; + + IXML_Node *pNeedPrefixNode; + IXML_ElementStack *pCurElement; + IXML_Node *currentNodePtr; + PARSER_STATE state; + + BOOL bHasTopLevel; + +} Parser; + + + +int Parser_LoadDocument( IXML_Document **retDoc, char * xmlFile, BOOL file); +BOOL Parser_isValidXmlName( DOMString name); +int Parser_setNodePrefixAndLocalName(IXML_Node *newIXML_NodeIXML_Attr); +void Parser_freeNodeContent( IXML_Node *IXML_Nodeptr); + +void Parser_setErrorChar( char c ); + +void ixmlAttr_free(IXML_Attr *attrNode); +void ixmlAttr_init(IXML_Attr *attrNode); + +int ixmlElement_setTagName(IXML_Element *element, char *tagName); + +void ixmlNamedNodeMap_init(IXML_NamedNodeMap *nnMap); +int ixmlNamedNodeMap_addToNamedNodeMap(IXML_NamedNodeMap **nnMap, IXML_Node *add); + +void ixmlNode_init(IXML_Node *IXML_Nodeptr); +BOOL ixmlNode_compare(IXML_Node *srcIXML_Node, IXML_Node *destIXML_Node); + +void ixmlNode_getElementsByTagName( IXML_Node *n, char *tagname, IXML_NodeList **list); +void ixmlNode_getElementsByTagNameNS( IXML_Node *IXML_Node, char *namespaceURI, + char *localName, IXML_NodeList **list); + +int ixmlNode_setNodeProperties(IXML_Node* node, IXML_Node *src); +int ixmlNode_setNodeName( IXML_Node* node, DOMString qualifiedName); + +void ixmlNodeList_init(IXML_NodeList *nList); +int ixmlNodeList_addToNodeList(IXML_NodeList **nList, IXML_Node *add); + +#endif // _IXMLPARSER_H + diff --git a/libupnp/ixml/src/ixml.c b/libupnp/ixml/src/ixml.c new file mode 100644 index 0000000..c13edb7 --- /dev/null +++ b/libupnp/ixml/src/ixml.c @@ -0,0 +1,530 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "ixmlmembuf.h" +#include "ixmlparser.h" + +/*================================================================ +* copy_with_escape +* +* +*=================================================================*/ +static void +copy_with_escape( INOUT ixml_membuf * buf, + IN char *p ) +{ + int i; + int plen; + + if( p == NULL ) + return; + + plen = strlen( p ); + + for( i = 0; i < plen; i++ ) { + switch ( p[i] ) { + case '<': + ixml_membuf_append_str( buf, "<" ); + break; + + case '>': + ixml_membuf_append_str( buf, ">" ); + break; + + case '&': + ixml_membuf_append_str( buf, "&" ); + break; + + case '\'': + ixml_membuf_append_str( buf, "'" ); + break; + + case '\"': + ixml_membuf_append_str( buf, """ ); + break; + + default: + ixml_membuf_append( buf, &p[i] ); + } + } +} + +/*================================================================ +* ixmlPrintDomTreeRecursive +* It is a recursive function to print all the node in a tree. +* Internal to parser only. +* +*=================================================================*/ +void +ixmlPrintDomTreeRecursive( IN IXML_Node * nodeptr, + IN ixml_membuf * buf ) +{ + char *nodeName = NULL; + char *nodeValue = NULL; + IXML_Node *child = NULL, + *sibling = NULL; + + if( nodeptr != NULL ) { + nodeName = ( char * )ixmlNode_getNodeName( nodeptr ); + nodeValue = ixmlNode_getNodeValue( nodeptr ); + + switch ( ixmlNode_getNodeType( nodeptr ) ) { + + case eTEXT_NODE: + copy_with_escape( buf, nodeValue ); + break; + + case eCDATA_SECTION_NODE: + ixml_membuf_append_str( buf, nodeValue ); + break; + + case ePROCESSING_INSTRUCTION_NODE: + ixml_membuf_append_str( buf, "\n" ); + break; + + case eDOCUMENT_NODE: + ixmlPrintDomTreeRecursive( ixmlNode_getFirstChild + ( nodeptr ), buf ); + break; + + case eATTRIBUTE_NODE: + ixml_membuf_append_str( buf, nodeName ); + ixml_membuf_append_str( buf, "=\"" ); + if( nodeValue != NULL ) { + ixml_membuf_append_str( buf, nodeValue ); + } + ixml_membuf_append_str( buf, "\"" ); + if( nodeptr->nextSibling != NULL ) { + ixml_membuf_append_str( buf, " " ); + ixmlPrintDomTreeRecursive( nodeptr->nextSibling, buf ); + } + break; + + case eELEMENT_NODE: + ixml_membuf_append_str( buf, "<" ); + ixml_membuf_append_str( buf, nodeName ); + + if( nodeptr->firstAttr != NULL ) { + ixml_membuf_append_str( buf, " " ); + ixmlPrintDomTreeRecursive( nodeptr->firstAttr, buf ); + } + + child = ixmlNode_getFirstChild( nodeptr ); + if( ( child != NULL ) + && ( ixmlNode_getNodeType( child ) == + eELEMENT_NODE ) ) { + ixml_membuf_append_str( buf, ">\n" ); + } else { + ixml_membuf_append_str( buf, ">" ); + } + + // output the children + ixmlPrintDomTreeRecursive( ixmlNode_getFirstChild + ( nodeptr ), buf ); + + // Done with children. Output the end tag. + ixml_membuf_append_str( buf, "" ); + } else { + ixml_membuf_append_str( buf, ">\n" ); + } + ixmlPrintDomTreeRecursive( ixmlNode_getNextSibling + ( nodeptr ), buf ); + break; + + default: + break; + } + } +} + +/*================================================================ +* ixmlPrintDomTree +* Print a DOM tree. +* Element, and Attribute nodes are handled differently. +* We don't want to print the Element and Attribute nodes' sibling. +* External function. +* +*=================================================================*/ +void +ixmlPrintDomTree( IN IXML_Node * nodeptr, + IN ixml_membuf * buf ) +{ + char *nodeName = NULL; + char *nodeValue = NULL; + IXML_Node *child = NULL; + + if( ( nodeptr == NULL ) || ( buf == NULL ) ) { + return; + } + + nodeName = ( char * )ixmlNode_getNodeName( nodeptr ); + nodeValue = ixmlNode_getNodeValue( nodeptr ); + + switch ( ixmlNode_getNodeType( nodeptr ) ) { + + case eTEXT_NODE: + case eCDATA_SECTION_NODE: + case ePROCESSING_INSTRUCTION_NODE: + case eDOCUMENT_NODE: + ixmlPrintDomTreeRecursive( nodeptr, buf ); + break; + + case eATTRIBUTE_NODE: + ixml_membuf_append_str( buf, nodeName ); + ixml_membuf_append_str( buf, "=\"" ); + ixml_membuf_append_str( buf, nodeValue ); + ixml_membuf_append_str( buf, "\"" ); + break; + + case eELEMENT_NODE: + ixml_membuf_append_str( buf, "<" ); + ixml_membuf_append_str( buf, nodeName ); + + if( nodeptr->firstAttr != NULL ) { + ixml_membuf_append_str( buf, " " ); + ixmlPrintDomTreeRecursive( nodeptr->firstAttr, buf ); + } + + child = ixmlNode_getFirstChild( nodeptr ); + if( ( child != NULL ) + && ( ixmlNode_getNodeType( child ) == eELEMENT_NODE ) ) { + ixml_membuf_append_str( buf, ">\n" ); + } else { + ixml_membuf_append_str( buf, ">" ); + } + + // output the children + ixmlPrintDomTreeRecursive( ixmlNode_getFirstChild( nodeptr ), + buf ); + + // Done with children. Output the end tag. + ixml_membuf_append_str( buf, "\n" ); + break; + + default: + break; + } +} + +/*================================================================ +* ixmlDomTreetoString +* Converts a DOM tree into a text string +* Element, and Attribute nodes are handled differently. +* We don't want to print the Element and Attribute nodes' sibling. +* External function. +* +*=================================================================*/ +void +ixmlDomTreetoString( IN IXML_Node * nodeptr, + IN ixml_membuf * buf ) +{ + char *nodeName = NULL; + char *nodeValue = NULL; + IXML_Node *child = NULL; + + if( ( nodeptr == NULL ) || ( buf == NULL ) ) { + return; + } + + nodeName = ( char * )ixmlNode_getNodeName( nodeptr ); + nodeValue = ixmlNode_getNodeValue( nodeptr ); + + switch ( ixmlNode_getNodeType( nodeptr ) ) { + + case eTEXT_NODE: + case eCDATA_SECTION_NODE: + case ePROCESSING_INSTRUCTION_NODE: + case eDOCUMENT_NODE: + ixmlPrintDomTreeRecursive( nodeptr, buf ); + break; + + case eATTRIBUTE_NODE: + ixml_membuf_append_str( buf, nodeName ); + ixml_membuf_append_str( buf, "=\"" ); + ixml_membuf_append_str( buf, nodeValue ); + ixml_membuf_append_str( buf, "\"" ); + break; + + case eELEMENT_NODE: + ixml_membuf_append_str( buf, "<" ); + ixml_membuf_append_str( buf, nodeName ); + + if( nodeptr->firstAttr != NULL ) { + ixml_membuf_append_str( buf, " " ); + ixmlPrintDomTreeRecursive( nodeptr->firstAttr, buf ); + } + + child = ixmlNode_getFirstChild( nodeptr ); + if( ( child != NULL ) + && ( ixmlNode_getNodeType( child ) == eELEMENT_NODE ) ) { + ixml_membuf_append_str( buf, ">" ); + } else { + ixml_membuf_append_str( buf, ">" ); + } + + // output the children + ixmlPrintDomTreeRecursive( ixmlNode_getFirstChild( nodeptr ), + buf ); + + // Done with children. Output the end tag. + ixml_membuf_append_str( buf, "" ); + break; + + default: + break; + } +} + +/*================================================================ +* ixmlLoadDocumentEx +* Parses the given file, and returns the DOM tree from it. +* External function. +* +*=================================================================*/ +int +ixmlLoadDocumentEx( IN char *xmlFile, + IXML_Document ** doc ) +{ + + if( ( xmlFile == NULL ) || ( doc == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + return Parser_LoadDocument( doc, xmlFile, TRUE ); +} + +/*================================================================ +* ixmlLoadDocument +* Parses the given file, and returns the DOM tree from it. +* External function. +* +*=================================================================*/ +IXML_Document * +ixmlLoadDocument( IN char *xmlFile ) +{ + + IXML_Document *doc = NULL; + + ixmlLoadDocumentEx( xmlFile, &doc ); + return doc; +} + +/*================================================================ +* ixmlPrintDocument +* Prints entire document, prepending XML prolog first. +* Puts lots of white spaces. +* External function. +* +*=================================================================*/ + +DOMString +ixmlPrintDocument(IXML_Document *doc) +{ + IXML_Node* rootNode = ( IXML_Node * )doc; + ixml_membuf memBuf; + ixml_membuf *buf = &memBuf; + + if( rootNode == NULL ) { + return NULL; + } + + ixml_membuf_init( buf ); + ixml_membuf_append_str( buf, "\n" ); + ixmlPrintDomTree( rootNode, buf ); + return buf->buf; + +} + +/*================================================================ +* ixmlPrintNode +* Print DOM tree under node. Puts lots of white spaces +* External function. +* +*=================================================================*/ +DOMString +ixmlPrintNode( IN IXML_Node * node ) +{ + + ixml_membuf memBuf; + ixml_membuf *buf = &memBuf; + + if( node == NULL ) { + return NULL; + } + + ixml_membuf_init( buf ); + ixmlPrintDomTree( node, buf ); + return buf->buf; + +} + +/*================================================================ +* ixmlDocumenttoString +* converts DOM tree under node to text string, +* prepending XML prolog first. +* External function. +* +*=================================================================*/ + +DOMString +ixmlDocumenttoString(IXML_Document *doc) +{ + IXML_Node* rootNode = ( IXML_Node * )doc; + ixml_membuf memBuf; + ixml_membuf *buf = &memBuf; + + if( rootNode == NULL ) { + return NULL; + } + + ixml_membuf_init( buf ); + ixml_membuf_append_str( buf, "\n" ); + ixmlDomTreetoString( rootNode, buf ); + return buf->buf; + +} + +/*================================================================ +* ixmlNodetoString +* converts DOM tree under node to text string +* External function. +* +*=================================================================*/ +DOMString +ixmlNodetoString( IN IXML_Node * node ) +{ + + ixml_membuf memBuf; + ixml_membuf *buf = &memBuf; + + if( node == NULL ) { + return NULL; + } + + ixml_membuf_init( buf ); + ixmlDomTreetoString( node, buf ); + return buf->buf; + +} + +/*================================================================ +* ixmlRelaxParser +* Makes the XML parser more tolerant to malformed text. +* External function. +* +*=================================================================*/ +void +ixmlRelaxParser(char errorChar) +{ + Parser_setErrorChar( errorChar ); +} + + +/*================================================================ +* ixmlParseBufferEx +* Parse xml file stored in buffer. +* External function. +* +*=================================================================*/ +int +ixmlParseBufferEx( IN char *buffer, + IXML_Document ** retDoc ) +{ + + if( ( buffer == NULL ) || ( retDoc == NULL ) ) { + return IXML_INVALID_PARAMETER; + } + + if( strlen( buffer ) == 0 ) { + return IXML_INVALID_PARAMETER; + } + + return Parser_LoadDocument( retDoc, buffer, FALSE ); +} + +/*================================================================ +* ixmlParseBuffer +* Parse xml file stored in buffer. +* External function. +* +*=================================================================*/ +IXML_Document * +ixmlParseBuffer( IN char *buffer ) +{ + IXML_Document *doc = NULL; + + ixmlParseBufferEx( buffer, &doc ); + return doc; +} + +/*================================================================ +* ixmlCloneDOMString +* Clones a DOM String. +* External function. +* +*=================================================================*/ +DOMString +ixmlCloneDOMString( IN const DOMString src ) +{ + if( src == NULL ) { + return NULL; + } + + return ( strdup( src ) ); +} + +/*================================================================ +* ixmlFreeDOMString +* Frees a DOM String. +* External function. +* +*=================================================================*/ +void +ixmlFreeDOMString( IN DOMString buf ) +{ + if( buf != NULL ) { + free( buf ); + } +} diff --git a/libupnp/ixml/src/ixmlmembuf.c b/libupnp/ixml/src/ixmlmembuf.c new file mode 100644 index 0000000..31cbac6 --- /dev/null +++ b/libupnp/ixml/src/ixmlmembuf.c @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include "ixmlmembuf.h" +#include "ixml.h" + +/*================================================================ +* ixml_membuf_set_size +* +* Increases or decreases buffer cap so that at least +* 'new_length' bytes can be stored +* +* On error, m's fields do not change. +* +* returns: +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* +*=================================================================*/ +static int +ixml_membuf_set_size( INOUT ixml_membuf * m, + IN size_t new_length ) +{ + size_t diff; + size_t alloc_len; + char *temp_buf; + + if( new_length >= m->length ) // increase length + { + // need more mem? + if( new_length <= m->capacity ) { + return 0; // have enough mem; done + } + + diff = new_length - m->length; + alloc_len = MAXVAL( m->size_inc, diff ) + m->capacity; + } else // decrease length + { + assert( new_length <= m->length ); + + // if diff is 0..m->size_inc, don't free + if( ( m->capacity - new_length ) <= m->size_inc ) { + return 0; + } + + alloc_len = new_length + m->size_inc; + } + + assert( alloc_len >= new_length ); + + temp_buf = realloc( m->buf, alloc_len + 1 ); + if( temp_buf == NULL ) { + // try smaller size + alloc_len = new_length; + temp_buf = realloc( m->buf, alloc_len + 1 ); + + if( temp_buf == NULL ) { + return IXML_INSUFFICIENT_MEMORY; + } + } + // save + m->buf = temp_buf; + m->capacity = alloc_len; + return 0; +} + +/*================================================================ +* membuffer_init +* +* +*=================================================================*/ +void +ixml_membuf_init( INOUT ixml_membuf * m ) +{ + assert( m != NULL ); + + m->size_inc = MEMBUF_DEF_SIZE_INC; + m->buf = NULL; + m->length = 0; + m->capacity = 0; +} + +/*================================================================ +* membuffer_destroy +* +* +*=================================================================*/ +void +ixml_membuf_destroy( INOUT ixml_membuf * m ) +{ + if( m == NULL ) { + return; + } + + free( m->buf ); + ixml_membuf_init( m ); +} + +/*================================================================ +* ixml_membuf_assign +* +* +*=================================================================*/ +int +ixml_membuf_assign( INOUT ixml_membuf * m, + IN const void *buf, + IN size_t buf_len ) +{ + int return_code; + + assert( m != NULL ); + + // set value to null + if( buf == NULL ) { + ixml_membuf_destroy( m ); + return IXML_SUCCESS; + } + // alloc mem + return_code = ixml_membuf_set_size( m, buf_len ); + if( return_code != 0 ) { + return return_code; + } + // copy + memcpy( m->buf, buf, buf_len ); + m->buf[buf_len] = 0; // null-terminate + + m->length = buf_len; + + return IXML_SUCCESS; + +} + +/*================================================================ +* ixml_membuf_assign_str +* +* +*=================================================================*/ +int +ixml_membuf_assign_str( INOUT ixml_membuf * m, + IN const char *c_str ) +{ + return ixml_membuf_assign( m, c_str, strlen( c_str ) ); +} + +/*================================================================ +* ixml_membuf_append +* +* +*=================================================================*/ +int +ixml_membuf_append( INOUT ixml_membuf * m, + IN const void *buf ) +{ + assert( m != NULL ); + + return ixml_membuf_insert( m, buf, 1, m->length ); +} + +/*================================================================ +* ixml_membuf_append_str +* +* +*=================================================================*/ +int +ixml_membuf_append_str( INOUT ixml_membuf * m, + IN const char *c_str ) +{ + return ixml_membuf_insert( m, c_str, strlen( c_str ), m->length ); +} + +/*================================================================ +* ixml_membuf_insert +* +* +*=================================================================*/ +int +ixml_membuf_insert( INOUT ixml_membuf * m, + IN const void *buf, + IN size_t buf_len, + int index ) +{ + int return_code; + + assert( m != NULL ); + + if( index < 0 || index > ( int )m->length ) + return IXML_INDEX_SIZE_ERR; + + if( buf == NULL || buf_len == 0 ) { + return 0; + } + // alloc mem + return_code = ixml_membuf_set_size( m, m->length + buf_len ); + if( return_code != 0 ) { + return return_code; + } + // insert data + // move data to right of insertion point + memmove( m->buf + index + buf_len, m->buf + index, m->length - index ); + memcpy( m->buf + index, buf, buf_len ); + m->length += buf_len; + m->buf[m->length] = 0; // null-terminate + + return 0; +} diff --git a/libupnp/ixml/src/ixmlparser.c b/libupnp/ixml/src/ixmlparser.c new file mode 100644 index 0000000..6a4ffb2 --- /dev/null +++ b/libupnp/ixml/src/ixmlparser.c @@ -0,0 +1,2477 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include "ixmlparser.h" + +static char g_error_char = '\0'; + +static const char LESSTHAN = '<'; +static const char GREATERTHAN = '>'; +static const char SLASH = '/'; +static const char EQUALS = '='; +static const char QUOTE = '\"'; +static const char SINGLEQUOTE = '\''; + +static const char *WHITESPACE = "\n\t\r "; +static const char *COMPLETETAG = "/>"; +static const char *ENDTAG = ""; +static const char *BEGIN_PI = ""; +static const char *BEGIN_DOCTYPE = ""; +static const char *DEC_NUMBERS = "0123456789"; +static const char *HEX_NUMBERS = "0123456789ABCDEFabcdef"; + +typedef struct char_info { + unsigned short l, + h; +} char_info_t; + +typedef char utf8char[8]; + +/*==============================================================================* +* Letter table contains all characters in XML 1.0 plus ":", "_" and +* ideographic. +* +* This table contains all the characters that an element name can start with. +* See XML 1.0 (2nd Edition) for more details. +* +*===============================================================================*/ +static char_info_t Letter[] = { + {0x003A, 0x003A}, // character ":" + {0x0041, 0x005A}, + {0x005F, 0x005F}, // character "_" + {0x0061, 0x007A}, {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x00FF}, + {0x0100, 0x0131}, {0x0134, 0x013E}, {0x0141, 0x0148}, {0x014A, 0x017E}, + {0x0180, 0x01C3}, {0x01CD, 0x01F0}, {0x01F4, 0x01F5}, {0x01FA, 0x0217}, + {0x0250, 0x02A8}, {0x02BB, 0x02C1}, {0x0386, 0x0386}, {0x0388, 0x038A}, + {0x038C, 0x038C}, {0x038E, 0x03A1}, {0x03A3, 0x03CE}, {0x03D0, 0x03D6}, + {0x03DA, 0x03DA}, + {0x03DC, 0x03DC}, {0x03DE, 0x03DE}, {0x03E0, 0x03E0}, {0x03E2, 0x03F3}, + {0x0401, 0x040C}, {0x040E, 0x044F}, {0x0451, 0x045C}, {0x045E, 0x0481}, + {0x0490, 0x04C4}, {0x04C7, 0x04C8}, {0x04CB, 0x04CC}, {0x04D0, 0x04EB}, + {0x04EE, 0x04F5}, {0x04F8, 0x04F9}, {0x0531, 0x0556}, {0x0559, 0x0559}, + {0x0561, 0x0586}, {0x05D0, 0x05EA}, {0x05F0, 0x05F2}, {0x0621, 0x063A}, + {0x0641, 0x064A}, {0x0671, 0x06B7}, {0x06BA, 0x06BE}, {0x06C0, 0x06CE}, + {0x06D0, 0x06D3}, {0x06D5, 0x06D5}, {0x06E5, 0x06E6}, {0x0905, 0x0939}, + {0x093D, 0x093D}, {0x0958, 0x0961}, {0x0985, 0x098C}, {0x098F, 0x0990}, + {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, + {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, {0x09F0, 0x09F1}, {0x0A05, 0x0A0A}, + {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, + {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, + {0x0A72, 0x0A74}, {0x0A85, 0x0A8B}, {0x0A8D, 0x0A8D}, {0x0A8F, 0x0A91}, + {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, + {0x0ABD, 0x0ABD}, {0x0AE0, 0x0AE0}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, + {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B36, 0x0B39}, + {0x0B3D, 0x0B3D}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, + {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB5}, + {0x0BB7, 0x0BB9}, {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, + {0x0C2A, 0x0C33}, {0x0C35, 0x0C39}, {0x0C60, 0x0C61}, {0x0C85, 0x0C8C}, + {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, + {0x0D12, 0x0D28}, {0x0D2A, 0x0D39}, {0x0D60, 0x0D61}, {0x0E01, 0x0E2E}, + {0x0E30, 0x0E30}, {0x0E32, 0x0E33}, {0x0E40, 0x0E45}, {0x0E81, 0x0E82}, + {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, {0x0E8D, 0x0E8D}, + {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, + {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EAE}, {0x0EB0, 0x0EB0}, + {0x0EB2, 0x0EB3}, {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0F40, 0x0F47}, + {0x0F49, 0x0F69}, {0x10A0, 0x10C5}, {0x10D0, 0x10F6}, {0x1100, 0x1100}, + {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, {0x110B, 0x110C}, + {0x110E, 0x1112}, {0x113C, 0x113C}, {0x113E, 0x113E}, {0x1140, 0x1140}, + {0x114C, 0x114C}, {0x114E, 0x114E}, {0x1150, 0x1150}, {0x1154, 0x1155}, + {0x1159, 0x1159}, {0x115F, 0x1161}, {0x1163, 0x1163}, {0x1165, 0x1165}, + {0x1167, 0x1167}, {0x1169, 0x1169}, {0x116D, 0x116E}, {0x1172, 0x1173}, + {0x1175, 0x1175}, {0x119E, 0x119E}, {0x11A8, 0x11A8}, {0x11AB, 0x11AB}, + {0x11AE, 0x11AF}, {0x11B7, 0x11B8}, {0x11BA, 0x11BA}, {0x11BC, 0x11C2}, + {0x11EB, 0x11EB}, {0x11F0, 0x11F0}, {0x11F9, 0x11F9}, {0x1E00, 0x1E9B}, + {0x1EA0, 0x1EF9}, {0x1F00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, + {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, + {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FBC}, + {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, + {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, + {0x2126, 0x2126}, {0x212A, 0x212B}, {0x212E, 0x212E}, {0x2180, 0x2182}, + {0x3007, 0x3007}, {0x3021, 0x3029}, // these two are ideographic + {0x3041, 0x3094}, {0x30A1, 0x30FA}, {0x3105, 0x312C}, + {0x4E00, 0x9FA5}, // ideographic + {0xAC00, 0xD7A3} +}; + +#define LETTERTABLESIZE (sizeof(Letter)/sizeof(Letter[0])) + +/*==============================================================================* +* NameChar table contains +* CombiningChar, Extender, Digit, '-', '.', less '_', ':' +* NameChar ::= Digit | '-' | '.' | CombiningChar | Extender +* See XML 1.0 2nd Edition +* +*===============================================================================*/ +static char_info_t NameChar[] = { + {0x002D, 0x002D}, // character "-" + {0x002E, 0x002E}, // character "." + {0x0030, 0x0039}, // digit + {0x00B7, 0x00B7}, {0x02D0, 0x02D0}, {0x02D1, 0x02D1}, // extended + {0x0300, 0x0345}, {0x0360, 0x0361}, + {0x0387, 0x0387}, // extended + {0x0483, 0x0486}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C4}, + {0x0640, 0x0640}, // extended + {0x064B, 0x0652}, + {0x0660, 0x0669}, // digit + {0x0670, 0x0670}, + {0x06D6, 0x06DC}, {0x06DD, 0x06DF}, {0x06E0, 0x06E4}, {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, + {0x06F0, 0x06F9}, // digit + {0x0901, 0x0903}, {0x093C, 0x093C}, + {0x093E, 0x094C}, {0x094D, 0x094D}, {0x0951, 0x0954}, {0x0962, 0x0963}, + {0x0966, 0x096F}, // digit + {0x0981, 0x0983}, {0x09BC, 0x09BC}, {0x09BE, 0x09BE}, + {0x09BF, 0x09BF}, {0x09C0, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, + {0x09D7, 0x09D7}, {0x09E2, 0x09E3}, + {0x09E6, 0x09EF}, // digit + {0x0A02, 0x0A02}, + {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A3E}, {0x0A3F, 0x0A3F}, {0x0A40, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, + {0x0A66, 0x0A6F}, // digit + {0x0A70, 0x0A71}, + {0x0A81, 0x0A83}, {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, + {0x0AE6, 0x0AEF}, // digit + {0x0B01, 0x0B03}, {0x0B3C, 0x0B3C}, + {0x0B3E, 0x0B43}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, + {0x0B66, 0x0B6F}, // digit + {0x0B82, 0x0B83}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, + {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, + {0x0BE7, 0x0BEF}, // digit + {0x0C01, 0x0C03}, + {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0C66, 0x0C6F}, // digit + {0x0C82, 0x0C83}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, + {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, + {0x0CE6, 0x0CEF}, // digit + {0x0D02, 0x0D03}, + {0x0D3E, 0x0D43}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, + {0x0D66, 0x0D6F}, // digit + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, + {0x0E46, 0x0E46}, // extended + {0x0E47, 0x0E4E}, + {0x0E50, 0x0E59}, // digit + {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, + {0x0EC6, 0x0EC6}, // extended + {0x0EC8, 0x0ECD}, + {0x0ED0, 0x0ED9}, // digit + {0x0F18, 0x0F19}, + {0x0F20, 0x0F29}, // digit + {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F3E, 0x0F3E}, {0x0F3F, 0x0F3F}, {0x0F71, 0x0F84}, + {0x0F86, 0x0F8B}, {0x0F90, 0x0F95}, {0x0F97, 0x0F97}, {0x0F99, 0x0FAD}, + {0x0FB1, 0x0FB7}, {0x0FB9, 0x0FB9}, {0x20D0, 0x20DC}, {0x20E1, 0x20E1}, + {0x3005, 0x3005}, // extended + {0x302A, 0x302F}, + {0x3031, 0x3035}, // extended + {0x3099, 0x3099}, {0x309A, 0x309A}, // combining char + {0x309D, 0x309E}, {0x30FC, 0x30FE} // extended +}; + +#define NAMECHARTABLESIZE (sizeof(NameChar)/sizeof(NameChar[0])) + +// functions used in this file +static void Parser_free( Parser * myParser ); +static int Parser_skipDocType( char **pstr ); +static int Parser_skipProlog( Parser * xmlParser ); +static int Parser_skipMisc( Parser * xmlParser ); +static void Parser_freeElementStackItem( IXML_ElementStack * pItem ); +static void Parser_freeNsURI( IXML_NamespaceURI * pNsURI ); + +static int Parser_getNextNode( Parser * myParser, + IXML_Node * newNode, + BOOL * isEnd ); +static int Parser_getNextToken( Parser * myParser ); +static int Parser_xmlNamespace( Parser * myParser, + IXML_Node * newNode ); +static BOOL Parser_ElementPrefixDefined( Parser * myParser, + IXML_Node * newNode, + char **nsURI ); +static int Parser_setElementNamespace( IXML_Element * newElement, + char *nsURI ); +static int Parser_parseDocument( IXML_Document ** retDoc, + Parser * domParser ); +static BOOL Parser_hasDefaultNamespace( Parser * xmlParser, + IXML_Node * newNode, + char **nsURI ); +static int Parser_getChar( IN char *src, + INOUT int *cLen ); + +/*==============================================================================* +* Parser_isCharInTable +* will determine whether character c is in the table of tbl +* (either Letter table or NameChar table) +* +*===============================================================================*/ +static BOOL +Parser_isCharInTable( IN int c, + IN char_info_t * tbl, + IN int sz ) +{ + int t = 0, + b = sz, + m; + + while( t <= b ) { + m = ( t + b ) / 2; + if( c < tbl[m].l ) { + b = m - 1; + } else if( c > tbl[m].h ) { + t = m + 1; + } else { + return TRUE; + } + } + return FALSE; +} + +/*==============================================================================* +* Parser_isXmlChar +* see XML 1.0 (2nd Edition) 2.2. +* Internal to parser only +* +*===============================================================================*/ +static BOOL +Parser_isXmlChar( IN int c ) +{ + return ( c == 0x9 || c == 0xA || c == 0xD || + ( c >= 0x20 && c <= 0xD7FF ) || + ( c >= 0xE000 && c <= 0xFFFD ) || + ( c >= 0x10000 && c <= 0x10FFFF ) ); +} + +/*==============================================================================* +* Parser_isNameChar +* check whether c (int) is in LetterTable or NameCharTable +* Internal to parser only. +* +*===============================================================================*/ +static BOOL +Parser_isNameChar( IN int c, + IN BOOL bNameChar ) +{ + if( Parser_isCharInTable( c, Letter, LETTERTABLESIZE ) ) { + return TRUE; + } + + if( bNameChar + && Parser_isCharInTable( c, NameChar, NAMECHARTABLESIZE ) ) { + return TRUE; + } + return FALSE; +} + +/*==============================================================================* +* Parser_isValidXmlName +* Check to see whether name is a valid xml name. +* External function. +* +*===============================================================================*/ +BOOL +Parser_isValidXmlName( IN DOMString name ) +{ + char *pstr = NULL; + int i = 0, + nameLen = 0; + + assert( name != NULL ); + + nameLen = strlen( name ); + + pstr = name; + if( Parser_isNameChar( *pstr, FALSE ) == TRUE ) { + for( i = 1; i < nameLen; i++ ) { + if( Parser_isNameChar( *( pstr + 1 ), TRUE ) == FALSE ) { //illegal char + return FALSE; + } + } + } + + return TRUE; +} + +/*==============================================================================* +* Parser_setErrorChar: +* If 'c' is 0 (default), the parser is strict about XML encoding : +* invalid UTF-8 sequences or "&" entities are rejected, and the parsing +* aborts. +* If 'c' is not 0, the parser is relaxed : invalid UTF-8 characters +* are replaced by this character, and invalid "&" entities are left +* untranslated. The parsing is then allowed to continue. +* External function. +* +*===============================================================================*/ +void +Parser_setErrorChar( IN char c ) +{ + g_error_char = c; +} + + +/*==============================================================================* +* Parser_intToUTF8: +* Encoding a character to its UTF-8 character string, and return its length +* internal function. +* +*===============================================================================*/ +static int +Parser_intToUTF8( IN int c, + IN utf8char s ) +{ + if( c < 0 ) { + return 0; + } + + if( c <= 127 ) { + s[0] = c; + s[1] = 0; + return 1; + } else if( c <= 0x07FF ) { // 0x0080 < c <= 0x07FF + s[0] = 0xC0 | ( c >> 6 ); + s[1] = 0x80 | ( c & 0x3f ); + s[2] = 0; + return 2; + } else if( c <= 0xFFFF ) { // 0x0800 < c <= 0xFFFF + s[0] = 0xE0 | ( c >> 12 ); + s[1] = 0x80 | ( ( c >> 6 ) & 0x3f ); + s[2] = 0x80 | ( c & 0x3f ); + s[3] = 0; + return 3; + } else if( c <= 0x1FFFFF ) { // 0x10000 < c <= 0x1FFFFF + s[0] = 0xF0 | ( c >> 18 ); + s[1] = 0x80 | ( ( c >> 12 ) & 0x3f ); + s[2] = 0x80 | ( ( c >> 6 ) & 0x3f ); + s[3] = 0x80 | ( c & 0x3f ); + s[4] = 0; + return 4; + } else if( c <= 0x3FFFFFF ) { // 0x200000 < c <= 3FFFFFF + s[0] = 0xF8 | ( c >> 24 ); + s[1] = 0x80 | ( ( c >> 18 ) & 0x3f ); + s[2] = 0x80 | ( ( c >> 12 ) & 0x3f ); + s[3] = 0x80 | ( ( c >> 6 ) & 0x3f ); + s[4] = 0x80 | ( c & 0x3f ); + s[5] = 0; + return 5; + } else if( c <= 0x7FFFFFFF ) { // 0x4000000 < c <= 7FFFFFFF + s[0] = 0xFC | ( c >> 30 ); + s[1] = 0x80 | ( ( c >> 24 ) & 0x3f ); + s[2] = 0x80 | ( ( c >> 18 ) & 0x3f ); + s[3] = 0x80 | ( ( c >> 12 ) & 0x3f ); + s[4] = 0x80 | ( ( c >> 6 ) & 0x3f ); + s[5] = 0x80 | ( c & 0x3f ); + s[6] = 0; + return 6; + } else { // illegal + return 0; + } +} + +/*==============================================================================* +* Parser_UTF8ToInt +* In UTF-8, characters are encoded using sequences of 1 to 6 octets. +* This functions will return a UTF-8 character value and its octets number. +* Internal to parser only. +* Internal to parser only +* +*===============================================================================*/ +static int +Parser_UTF8ToInt( IN char *ss, + OUT int *len ) +{ + + unsigned char *s = ( unsigned char * )ss; + int c = *s; + + if( c <= 127 ) { // if c<=127, c is just the character. + *len = 1; + return c; + } else if( ( c & 0xE0 ) == 0xC0 && ( s[1] & 0xc0 ) == 0x80 ) { // a sequence of 110xxxxx and 10xxxxxx? + *len = 2; + return ( ( ( c & 0x1f ) << 6 ) | ( s[1] & 0x3f ) ); + } else if( ( c & 0xF0 ) == 0xE0 && ( s[1] & 0xc0 ) == 0x80 && ( s[2] & 0xc0 ) == 0x80 ) { // a sequence of 1110xxxx,10xxxxxx and 10xxxxxx ? + *len = 3; + return ( ( ( c & 0xf ) << 12 ) | ( ( s[1] & 0x3f ) << 6 ) | + ( s[2] & 0x3f ) ); + } else if( ( c & 0xf8 ) == 0xf0 && ( s[1] & 0xc0 ) == 0x80 && ( s[2] & 0xc0 ) == 0x80 && ( s[3] & 0xc0 ) == 0x80 ) { // a sequence of 11110xxx,10xxxxxx,10xxxxxx and 10xxxxxx ? + *len = 4; + return ( ( ( c & 0x7 ) << 18 ) | ( ( s[1] & 0x3f ) << 12 ) | + ( ( s[2] & 0x3f ) << 6 ) | ( s[3] & 0x3f ) ); + } else if( ( c & 0xfc ) == 0xf8 && ( s[1] & 0xc0 ) == 0x80 && ( s[2] & 0xc0 ) == 0x80 && ( s[3] & 0xc0 ) == 0x80 && ( s[4] & 0xc0 ) == 0x80 ) { // a sequence of 111110xx,10xxxxxx,10xxxxxx,10xxxxxx,10xxxxxx ? + *len = 5; + return ( ( ( c & 0x3 ) << 24 ) | ( ( s[1] & 0x3f ) << 18 ) | + ( ( s[2] & 0x3f ) << 12 ) | ( ( s[3] & 0x3f ) << 6 ) | + ( s[4] & 0x3f ) ); + } else if( ( c & 0xfe ) == 0xfc && ( s[1] & 0xc0 ) == 0x80 && ( s[2] & 0xc0 ) == 0x80 && ( s[3] & 0xc0 ) == 0x80 && ( s[4] & 0xc0 ) == 0x80 && ( s[5] & 0xc0 ) == 0x80 ) { // a sequence of 1111110x,10xxxxxx,10xxxxxx,10xxxxxx,10xxxxxx and 10xxxxxx ? + *len = 6; + return ( ( ( c & 0x1 ) << 30 ) | ( ( s[1] & 0x3f ) << 24 ) | + ( ( s[2] & 0x3f ) << 18 ) | ( ( s[3] & 0x3f ) << 12 ) | + ( ( s[4] & 0x3f ) << 6 ) | ( s[5] & 0x3f ) ); + } else { // none of above, error + if (g_error_char) { + *len = 1; + return g_error_char; + } else { + *len = 0; + return -1; + } + } +} + +/*==============================================================================* +* Parser_init +* Initializes a xml parser. +* Internal to parser only +* +*===============================================================================*/ +static Parser * +Parser_init( ) +{ + Parser *newParser = NULL; + + newParser = ( Parser * ) malloc( sizeof( Parser ) ); + if( newParser == NULL ) { + return NULL; + } + + memset( newParser, 0, sizeof( Parser ) ); + + ixml_membuf_init( &( newParser->tokenBuf ) ); + ixml_membuf_init( &( newParser->lastElem ) ); + + return newParser; +} + +/*================================================================ +* Parser_isValidEndElement +* check if a new node->nodeName matches top of element stack. +* Internal to parser only. +* +*=================================================================*/ +static int +Parser_isValidEndElement( IN Parser * xmlParser, + IN IXML_Node * newNode ) +{ + return ( strcmp( xmlParser->pCurElement->element, newNode->nodeName ) + == 0 ); +} + +/*=============================================================== +* Parser_pushElement +* push a new element onto element stack +* Internal to parser only. +* +*=================================================================*/ +static int +Parser_pushElement( IN Parser * xmlParser, + IN IXML_Node * newElement ) +{ + + IXML_ElementStack *pCurElement = NULL; + IXML_ElementStack *pNewStackElement = NULL; + + assert( newElement ); + if( newElement != NULL ) { + // push new element + pNewStackElement = + ( IXML_ElementStack * ) malloc( sizeof( IXML_ElementStack ) ); + if( pNewStackElement == NULL ) { + return IXML_INSUFFICIENT_MEMORY; + } + + memset( pNewStackElement, 0, sizeof( IXML_ElementStack ) ); + // the element member includes both prefix and name + + pNewStackElement->element = strdup( newElement->nodeName ); + if( pNewStackElement->element == NULL ) { + free( pNewStackElement ); + return IXML_INSUFFICIENT_MEMORY; + } + + if( newElement->prefix != 0 ) { + pNewStackElement->prefix = strdup( newElement->prefix ); + if( pNewStackElement->prefix == NULL ) { + Parser_freeElementStackItem( pNewStackElement ); + free( pNewStackElement ); + return IXML_INSUFFICIENT_MEMORY; + } + } + + if( newElement->namespaceURI != 0 ) { + pNewStackElement->namespaceUri = + strdup( newElement->namespaceURI ); + if( pNewStackElement->namespaceUri == NULL ) { + Parser_freeElementStackItem( pNewStackElement ); + free( pNewStackElement ); + return IXML_INSUFFICIENT_MEMORY; + } + } + + pCurElement = xmlParser->pCurElement; + + // insert the new element into the top of the stack + pNewStackElement->nextElement = pCurElement; + xmlParser->pCurElement = pNewStackElement; + + } + + return IXML_SUCCESS; +} + +/*================================================================ +* Parser_popElement +* Remove element from element stack. +* Internal to parser only. +* +*=================================================================*/ +static void +Parser_popElement( IN Parser * xmlParser ) +{ + IXML_ElementStack *pCur = NULL; + IXML_NamespaceURI *pnsUri = NULL, + *pNextNS = NULL; + + pCur = xmlParser->pCurElement; + if( pCur != NULL ) { + xmlParser->pCurElement = pCur->nextElement; + + Parser_freeElementStackItem( pCur ); + + pnsUri = pCur->pNsURI; + while( pnsUri != NULL ) { + pNextNS = pnsUri->nextNsURI; + + Parser_freeNsURI( pnsUri ); + free( pnsUri ); + pnsUri = pNextNS; + } + + free( pCur ); + } + +} + +/*================================================================ +* Parser_readFileOrBuffer +* read a xml file or buffer contents into xml parser. +* Internal to parser only. +* +*=================================================================*/ +static int +Parser_readFileOrBuffer( IN Parser * xmlParser, + IN char *xmlFileName, + IN BOOL file ) +{ + int fileSize = 0; + int bytesRead = 0; + FILE *xmlFilePtr = NULL; + + if( file ) { + xmlFilePtr = fopen( xmlFileName, "rb" ); + if( xmlFilePtr == NULL ) { + return IXML_NO_SUCH_FILE; + } else { + fseek( xmlFilePtr, 0, SEEK_END ); + fileSize = ftell( xmlFilePtr ); + if( fileSize == 0 ) { + fclose( xmlFilePtr ); + return IXML_SYNTAX_ERR; + } + + xmlParser->dataBuffer = ( char * )malloc( fileSize + 1 ); + if( xmlParser->dataBuffer == NULL ) { + fclose( xmlFilePtr ); + return IXML_INSUFFICIENT_MEMORY; + } + + fseek( xmlFilePtr, 0, SEEK_SET ); + bytesRead = + fread( xmlParser->dataBuffer, 1, fileSize, xmlFilePtr ); + xmlParser->dataBuffer[bytesRead] = '\0'; // append null + fclose( xmlFilePtr ); + } + } else { + xmlParser->dataBuffer = strdup( xmlFileName ); + if( xmlParser->dataBuffer == NULL ) { + return IXML_INSUFFICIENT_MEMORY; + } + } + + return IXML_SUCCESS; +} + +/*================================================================ +* Parser_LoadDocument +* parses a xml file and return the DOM tree. +* Internal to parser only +* +*=================================================================*/ +int +Parser_LoadDocument( OUT IXML_Document ** retDoc, + IN char *xmlFileName, + IN BOOL file ) +{ + int rc = IXML_SUCCESS; + Parser *xmlParser = NULL; + + xmlParser = Parser_init( ); + if( xmlParser == NULL ) { + return IXML_INSUFFICIENT_MEMORY; + } + + rc = Parser_readFileOrBuffer( xmlParser, xmlFileName, file ); + if( rc != IXML_SUCCESS ) { + Parser_free( xmlParser ); + return rc; + } + + xmlParser->curPtr = xmlParser->dataBuffer; + rc = Parser_parseDocument( retDoc, xmlParser ); + return rc; + +} + +/*================================================================ +* isTopLevelElement +* decides whether we have top level element already. +* Internal to parser only. +* +*=================================================================*/ +static int +isTopLevelElement( IN Parser * xmlParser ) +{ + assert( xmlParser ); + return ( xmlParser->pCurElement == NULL ); +} + +/*================================================================ +* isDuplicateAttribute +* Decide whether the new attribute is the same as an +* existing one. +* Internal to parser only. +* +*=================================================================*/ +static int +isDuplicateAttribute( IN Parser * xmlParser, + IN IXML_Node * newAttrNode ) +{ + IXML_Node *elementNode = NULL; + IXML_Node *attrNode = NULL; + + elementNode = xmlParser->currentNodePtr; + attrNode = elementNode->firstAttr; + while( attrNode != NULL ) { + if( strcmp( attrNode->nodeName, newAttrNode->nodeName ) == 0 ) { + return TRUE; + } + + attrNode = attrNode->nextSibling; + } + + return FALSE; +} + +/*================================================================ +* Parser_processAttributeName +* processes the attribute name. +* Internal to parser only. +* +*=================================================================*/ +static int +Parser_processAttributeName( IN IXML_Document * rootDoc, + IN Parser * xmlParser, + IN IXML_Node * newNode ) +{ + IXML_Attr *attr = NULL; + int rc = IXML_SUCCESS; + + if( isDuplicateAttribute( xmlParser, newNode ) == TRUE ) { + return IXML_SYNTAX_ERR; + } + + rc = ixmlDocument_createAttributeEx( rootDoc, newNode->nodeName, + &attr ); + if( rc != IXML_SUCCESS ) { + return rc; + } + + rc = ixmlNode_setNodeProperties( ( IXML_Node * ) attr, newNode ); + if( rc != IXML_SUCCESS ) { + return rc; + } + + rc = ixmlElement_setAttributeNode( ( IXML_Element * ) xmlParser-> + currentNodePtr, attr, NULL ); + return rc; +} + +/*================================================================ +* Parser_processElementName +* Processes element name +* Internal to parser only. +* +*=================================================================*/ +static int +Parser_processElementName( IN IXML_Document * rootDoc, + IN Parser * xmlParser, + IN IXML_Node * newNode ) +{ + IXML_Element *newElement = NULL; + char *nsURI = NULL; + int rc = IXML_SUCCESS; + + if( xmlParser->bHasTopLevel == TRUE ) { + if( isTopLevelElement( xmlParser ) == TRUE ) { + return IXML_SYNTAX_ERR; + } + } else { + xmlParser->bHasTopLevel = TRUE; + } + + xmlParser->savePtr = xmlParser->curPtr; + rc = ixmlDocument_createElementEx( rootDoc, newNode->nodeName, + &newElement ); + if( rc != IXML_SUCCESS ) { + return rc; + } + + rc = ixmlNode_setNodeProperties( ( IXML_Node * ) newElement, newNode ); + if( rc != IXML_SUCCESS ) { + ixmlElement_free( newElement ); + return rc; + } + + if( newNode->prefix != NULL ) { // element has namespace prefix + if( Parser_ElementPrefixDefined( xmlParser, newNode, &nsURI ) != + TRUE ) { + // read next node to see whether it includes namespace definition + xmlParser->pNeedPrefixNode = ( IXML_Node * ) newElement; + } else { // fill in the namespace + Parser_setElementNamespace( newElement, nsURI ); + } + } else // does element has default namespace + { + // the node may have default namespace definition + if( Parser_hasDefaultNamespace( xmlParser, newNode, &nsURI ) == + TRUE ) { + Parser_setElementNamespace( newElement, nsURI ); + } else if( xmlParser->state == eATTRIBUTE ) { + // the default namespace maybe defined later + xmlParser->pNeedPrefixNode = ( IXML_Node * ) newElement; + } + } + + rc = ixmlNode_appendChild( xmlParser->currentNodePtr, + ( IXML_Node * ) newElement ); + if( rc != IXML_SUCCESS ) { + ixmlElement_free( newElement ); + return rc; + } + + xmlParser->currentNodePtr = ( IXML_Node * ) newElement; + + // push element to stack + rc = Parser_pushElement( xmlParser, ( IXML_Node * ) newElement ); + return rc; +} + +/*================================================================ +* Parser_eTagVerification +* Verifies endof element tag is the same as the openning +* element tag. +* Internal to parser only. +* +*=================================================================*/ +static int +Parser_eTagVerification( IN Parser * xmlParser, + IN IXML_Node * newNode ) +{ + + assert( newNode->nodeName ); + assert( xmlParser->currentNodePtr ); + + if( newNode->nodeType == eELEMENT_NODE ) { + if( Parser_isValidEndElement( xmlParser, newNode ) == TRUE ) { + Parser_popElement( xmlParser ); + } else { // syntax error + return IXML_SYNTAX_ERR; + } + } + + if( strcmp( newNode->nodeName, xmlParser->currentNodePtr->nodeName ) == + 0 ) { + xmlParser->currentNodePtr = xmlParser->currentNodePtr->parentNode; + } else { + return IXML_SYNTAX_ERR; + } + + return IXML_SUCCESS; + +} + +/*================================================================ +* Parser_freeNodeContent +* frees a node contents +* Internal to parser only. +* +*=================================================================*/ +void +Parser_freeNodeContent( IN IXML_Node * nodeptr ) +{ + + if( nodeptr == NULL ) { + return; + } + + if( nodeptr->nodeName != NULL ) { + free( nodeptr->nodeName ); + } + + if( nodeptr->nodeValue != NULL ) { + free( nodeptr->nodeValue ); + } + + if( nodeptr->namespaceURI != NULL ) { + free( nodeptr->namespaceURI ); + } + + if( nodeptr->prefix != NULL ) { + free( nodeptr->prefix ); + } + + if( nodeptr->localName != NULL ) { + free( nodeptr->localName ); + } + +} + +/*================================================================ +* Parser_parseDocument +* Parses the xml file and returns the DOM document tree. +* External function. +* +*=================================================================*/ +static int +Parser_parseDocument( OUT IXML_Document ** retDoc, + IN Parser * xmlParser ) +{ + + IXML_Document *gRootDoc = NULL; + IXML_Node newNode; + BOOL bETag = FALSE; + IXML_Node *tempNode = NULL; + int rc = IXML_SUCCESS; + IXML_CDATASection *cdataSecNode = NULL; + + ixmlNode_init( &newNode ); + + rc = ixmlDocument_createDocumentEx( &gRootDoc ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + + xmlParser->currentNodePtr = ( IXML_Node * ) gRootDoc; + + rc = Parser_skipProlog( xmlParser ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + + while( bETag == FALSE ) { + // clear the newNode contents + ixmlNode_init( &newNode ); + + if( Parser_getNextNode( xmlParser, &newNode, &bETag ) == + IXML_SUCCESS ) { + if( bETag == FALSE ) { + switch ( newNode.nodeType ) { + case eELEMENT_NODE: + rc = Parser_processElementName( gRootDoc, + xmlParser, + &newNode ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + break; + + case eTEXT_NODE: + rc = ixmlDocument_createTextNodeEx( gRootDoc, + newNode. + nodeValue, + &tempNode ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + + rc = ixmlNode_appendChild( xmlParser-> + currentNodePtr, + tempNode ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + + break; + + case eCDATA_SECTION_NODE: + rc = ixmlDocument_createCDATASectionEx( gRootDoc, + newNode. + nodeValue, + &cdataSecNode ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + + rc = ixmlNode_appendChild( xmlParser-> + currentNodePtr, + &( cdataSecNode->n ) ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + break; + + case eATTRIBUTE_NODE: + rc = Parser_processAttributeName( gRootDoc, + xmlParser, + &newNode ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + break; + + default: + break; + } + } else // ETag==TRUE, endof element tag. + { + rc = Parser_eTagVerification( xmlParser, &newNode ); + if( rc != IXML_SUCCESS ) { + goto ErrorHandler; + } + xmlParser->state = eCONTENT; + } + + // reset bETag flag + bETag = FALSE; + + } else if( bETag == TRUE ) { // file is done + break; + } else { + rc = IXML_FAILED; + goto ErrorHandler; + } + Parser_freeNodeContent( &newNode ); + + } + + if( xmlParser->pCurElement != NULL ) { + rc = IXML_SYNTAX_ERR; + goto ErrorHandler; + } + + *retDoc = ( IXML_Document * ) gRootDoc; + Parser_free( xmlParser ); + return rc; + + ErrorHandler: + Parser_freeNodeContent( &newNode ); + ixmlDocument_free( gRootDoc ); + Parser_free( xmlParser ); + return rc; + +} + +/*==============================================================================* +* Parser_setLastElem +* set the last element to be the given string. +* Internal to parser only. +* +*===============================================================================*/ +static int +Parser_setLastElem( IN Parser * xmlParser, + IN const char *s ) +{ + int rc; + + if( ( xmlParser == NULL ) || ( s == NULL ) ) { + return IXML_FAILED; + } + + rc = ixml_membuf_assign_str( &( xmlParser->lastElem ), s ); + return rc; +} + +/*==============================================================================* +* +* Parser_clearTokenBuf +* clear token buffer. +* Internal to parser only. +* +*===============================================================================*/ +static void +Parser_clearTokenBuf( IN Parser * xmlParser ) +{ + ixml_membuf_destroy( &( xmlParser->tokenBuf ) ); +} + +/*==============================================================================* +* +* Parser_appendTokBufStr +* Appends string s to token buffer +* Internal to parser only. +* +*===============================================================================*/ +static int +Parser_appendTokBufStr( IN Parser * xmlParser, + IN const char *s ) +{ + int rc = IXML_SUCCESS; + + if( s != NULL ) { + rc = ixml_membuf_append_str( &( xmlParser->tokenBuf ), s ); + } + + return rc; +} + +/*==============================================================================* +* +* Parser_appendTokBufChar +* Appends c to token buffer. +* Internal to parser only. +* +*===============================================================================*/ +static int +Parser_appendTokBufChar( IN Parser * xmlParser, + IN char c ) +{ + int rc; + + rc = ixml_membuf_append( &( xmlParser->tokenBuf ), &c ); + return rc; +} + +/*==============================================================================* +* +* Parser_skipWhiteSpaces +* skip white spaces +* Internal to parser only +* +*===============================================================================*/ +static void +Parser_skipWhiteSpaces( IN Parser * xmlParser ) +{ + while( ( *( xmlParser->curPtr ) != 0 ) && + ( strchr( WHITESPACE, *( xmlParser->curPtr ) ) != NULL ) ) { + xmlParser->curPtr++; + } + +} + +/*==============================================================================* +* Parser_getChar +* returns next char value and its length +* Internal to parser only +* +*===============================================================================*/ +static int +Parser_getChar( IN char *src, + INOUT int *cLen ) +{ + char *pnum; + int sum; + char c; + int i; + + if( src == NULL || cLen == NULL ) { + return -1; + } + + *cLen = 0; + + if( *src != '&' ) { + if( *src > 0 && Parser_isXmlChar( *src ) ) { + *cLen = 1; + return *src; + } + + i = Parser_UTF8ToInt( src, cLen ); + if( !Parser_isXmlChar( i ) ) { + return ( g_error_char ? g_error_char : -1 ); + } + return i; + } else if( strncasecmp( src, QUOT, strlen( QUOT ) ) == 0 ) { + *cLen = strlen( QUOT ); + return '"'; + } else if( strncasecmp( src, LT, strlen( LT ) ) == 0 ) { + *cLen = strlen( LT ); + return '<'; + } else if( strncasecmp( src, GT, strlen( GT ) ) == 0 ) { + *cLen = strlen( GT ); + return '>'; + } else if( strncasecmp( src, APOS, strlen( APOS ) ) == 0 ) { + *cLen = strlen( APOS ); + return '\''; + } else if( strncasecmp( src, AMP, strlen( AMP ) ) == 0 ) { + *cLen = strlen( AMP ); + return '&'; + } else if( strncasecmp( src, ESC_HEX, strlen( ESC_HEX ) ) == 0 ) { // Read in escape characters of type &#xnn where nn is a hexadecimal value + pnum = src + strlen( ESC_HEX ); + sum = 0; + while( strchr( HEX_NUMBERS, *pnum ) != 0 ) { + c = *pnum; + if( c <= '9' ) { + sum = sum * 16 + ( c - '0' ); + } else if( c <= 'F' ) { + sum = sum * 16 + ( c - 'A' + 10 ); + } else { + sum = sum * 16 + ( c - 'a' + 10 ); + } + + pnum++; + } + + if( ( pnum == src ) || *pnum != ';' || !Parser_isXmlChar( sum ) ) { + goto fail_entity; + } + + *cLen = pnum - src + 1; + return sum; + + } else if( strncasecmp( src, ESC_DEC, strlen( ESC_DEC ) ) == 0 ) { + // Read in escape characters of type &#nn where nn is a decimal value + pnum = src + strlen( ESC_DEC ); + sum = 0; + while( strchr( DEC_NUMBERS, *pnum ) != 0 ) { + sum = sum * 10 + ( *pnum - '0' ); + pnum++; + } + + if( ( pnum == src ) || *pnum != ';' || !Parser_isXmlChar( sum ) ) { + goto fail_entity; + } + + *cLen = pnum - src + 1; + return sum; + } + +fail_entity: + if (g_error_char) { + *cLen = 1; + return '&'; + } + return -1; +} + +/*==============================================================================* +* Parser_copyToken +* copy string in src into xml parser token buffer +* Internal to parser only. +* +*===============================================================================*/ +static int +Parser_copyToken( IN Parser * xmlParser, + IN char *src, + IN int len ) +{ + int i, + c, + cl; + char *psrc, + *pend; + utf8char uch; + + if( !src || len <= 0 ) { + return IXML_FAILED; + } + + psrc = src; + pend = src + len; + + while( psrc < pend ) { + if( ( c = Parser_getChar( psrc, &cl ) ) <= 0 ) { + return IXML_FAILED; + } + + if( cl == 1 ) { + Parser_appendTokBufChar( xmlParser, ( char )c ); + psrc++; + } else { + + i = Parser_intToUTF8( c, uch ); + if( i == 0 ) { + return IXML_FAILED; + } + + Parser_appendTokBufStr( xmlParser, uch ); + psrc += cl; + } + } + + if( psrc > pend ) { + return IXML_FAILED; + } else { + return IXML_SUCCESS; // success + } + +} + +/*==============================================================================* +* +* Parser_skipString +* Skips all characters in the string until it finds the skip key. +* Then it skips the skip key and returns. +* Internal to parser only +* +*===============================================================================*/ +static int +Parser_skipString( INOUT char **pstrSrc, + IN const char *strSkipKey ) +{ + if( !( *pstrSrc ) || !strSkipKey ) { + return IXML_FAILED; + } + + while( ( **pstrSrc ) + && strncmp( *pstrSrc, strSkipKey, + strlen( strSkipKey ) ) != 0 ) { + ( *pstrSrc )++; + } + + if( **pstrSrc == '\0' ) { + return IXML_SYNTAX_ERR; + } + *pstrSrc = *pstrSrc + strlen( strSkipKey ); + + return IXML_SUCCESS; //success +} + +/*==============================================================================* +* +* Function: +* Returns: +* +* +*===============================================================================*/ +static int +Parser_skipPI( INOUT char **pSrc ) +{ + char *pEnd = NULL; + + assert( *pSrc ); + if( *pSrc == NULL ) { + return IXML_FAILED; + } + + if( ( strncasecmp( *pSrc, ( char * )XMLDECL, strlen( XMLDECL ) ) == 0 ) || ( strncasecmp( *pSrc, ( char * )XMLDECL2, strlen( XMLDECL2 ) ) == 0 ) ) { // not allowed + return IXML_SYNTAX_ERR; + } + + if( strncasecmp( *pSrc, ( char * )BEGIN_PI, strlen( BEGIN_PI ) ) == 0 ) { + pEnd = strstr( *pSrc, END_PI ); + if( ( pEnd != NULL ) && ( pEnd != *pSrc ) ) { + *pSrc = pEnd + strlen( BEGIN_PI ); + } else { + return IXML_SYNTAX_ERR; + } + } + + return IXML_SUCCESS; +} + +/*==============================================================================* +* Parser_skipXMLDecl: +* skips XML declarations. +* Internal only to parser. +* +*===============================================================================*/ +static int +Parser_skipXMLDecl( INOUT Parser * xmlParser ) +{ + int rc = IXML_FAILED; + + assert( xmlParser ); + if( xmlParser == NULL ) { + return rc; + } + + rc = Parser_skipString( &( xmlParser->curPtr ), END_PI ); + Parser_skipWhiteSpaces( xmlParser ); + return rc; + +} + +/*==============================================================================* +* Parser_skipProlog +* skip prolog +* Internal to parser only. +* +*===============================================================================*/ +static int +Parser_skipProlog( INOUT Parser * xmlParser ) +{ + int rc = IXML_SUCCESS; + + assert( xmlParser != NULL ); + if( xmlParser == NULL ) { + return IXML_FAILED; + } + + Parser_skipWhiteSpaces( xmlParser ); + + if( strncmp( xmlParser->curPtr, ( char * )XMLDECL, strlen( XMLDECL ) ) == 0 ) { // curPtr, ( char * )BEGIN_DOCTYPE, strlen( BEGIN_DOCTYPE ) ) == 0 ) { // curPtr++; + rc = Parser_skipDocType( &( xmlParser->curPtr ) ); + } + + if( rc == IXML_SUCCESS ) { + rc = Parser_skipMisc( xmlParser ); + } + + return rc; +} + +/*==============================================================================* +* +* Function: +* Returns: +* Skips all characters in the string until it finds the skip key. +* Then it skips the skip key and returns. +* +*===============================================================================*/ +static int +Parser_skipComment( INOUT char **pstrSrc ) +{ + char *pStrFound = NULL; + + assert( ( *pstrSrc ) != NULL ); + if( *pstrSrc == NULL ) { + return IXML_FAILED; + } + + pStrFound = strstr( *pstrSrc, END_COMMENT ); + if( ( pStrFound != NULL ) && ( pStrFound != *pstrSrc ) && + ( *( pStrFound - 1 ) != '-' ) ) { + *pstrSrc = pStrFound + strlen( END_COMMENT ); + } else { + return IXML_SYNTAX_ERR; + } + + return IXML_SUCCESS; +} + +/*==============================================================================* +* Parser_skipDocType +* skips document type declaration +* +*===============================================================================*/ +static int +Parser_skipDocType( INOUT char **pstr ) +{ + char *pCur = *pstr; + char *pNext = NULL; // default there is no nested < + int num = 1; + + assert( ( *pstr ) != NULL ); + if( *pstr == NULL ) { + return IXML_FAILED; + } + + while( ( pCur != NULL ) && ( num != 0 ) && ( *pCur != 0 ) ) { + if( *pCur == '<' ) { + num++; + } else if( *pCur == '>' ) { + num--; + } else if( *pCur == '"' ) { + pNext = strchr( pCur + 1, '"' ); + if( pNext == NULL ) { + return IXML_SYNTAX_ERR; + } + + pCur = pNext; + } + + pCur++; + } + + if( num == 0 ) { + *pstr = pCur; + return IXML_SUCCESS; + } else { + return IXML_SYNTAX_ERR; + } +} + +/*==============================================================================* +* +* Parser_skipMisc: +* skip comment, PI and white space +* +* +*===============================================================================*/ +static int +Parser_skipMisc( IN Parser * xmlParser ) +{ + int rc = IXML_SUCCESS; + int done = FALSE; + + while( ( done == FALSE ) && ( rc == IXML_SUCCESS ) ) { + if( strncasecmp( xmlParser->curPtr, ( char * )BEGIN_COMMENT, strlen( BEGIN_COMMENT ) ) == 0 ) { // + } + + for (i = 1; i < argc; i++) { + int rc; + IXML_Document* doc = NULL; + DOMString s; + char* p; + + printf ("Test \"%s\" \n", argv[i]); + printf (" Loading ... "); + fflush (stdout); + + rc = ixmlLoadDocumentEx (argv[i], &doc); + if (rc != IXML_SUCCESS) { + fprintf (stderr, + "** error : can't load document %s : " + "error %d (%s)\n", + argv[i], rc, get_ixml_error_string (rc)); + exit (EXIT_FAILURE); // ----------> + } + + printf ("OK\n"); + + printf (" Printing ... "); + fflush (stdout); + + s = ixmlPrintDocument (doc); + if (s == NULL || s[0] == '\0') { + fprintf (stderr, + "** error : can't print loaded document %s\n", + argv[i]); + exit (EXIT_FAILURE); // ----------> + } + p = s + strlen(s)-1; + while (isspace(*p) && p > s) + p--; + if (*s != '<' || *p != '>') { + fprintf (stderr, + "** error : malformed printed document '%s' :" + "%s\n", argv[i], s); + exit (EXIT_FAILURE); // ----------> + } + + printf ("OK\n"); + + ixmlFreeDOMString (s); + ixmlDocument_free (doc); + } + + exit (EXIT_SUCCESS); +} + + + + diff --git a/libupnp/ixml/test/test_document.sh b/libupnp/ixml/test/test_document.sh new file mode 100644 index 0000000..63de635 --- /dev/null +++ b/libupnp/ixml/test/test_document.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +./test_document `find $srcdir/test/testdata -name *.xml` + diff --git a/libupnp/ixml/test/testdata/empty_attribute.xml b/libupnp/ixml/test/testdata/empty_attribute.xml new file mode 100644 index 0000000..415b9fc --- /dev/null +++ b/libupnp/ixml/test/testdata/empty_attribute.xml @@ -0,0 +1,7 @@ + + + Empty XML Attribute + + + + diff --git a/libupnp/ixml/test/testdata/p43pass1.xml b/libupnp/ixml/test/testdata/p43pass1.xml new file mode 100644 index 0000000..de17c1b --- /dev/null +++ b/libupnp/ixml/test/testdata/p43pass1.xml @@ -0,0 +1,27 @@ + +CharData"> +]> + +CharData + + +CharData + +é" +CharData + +]]> + +CharData + +é" +CharData + +]]> +>" +CharData + diff --git a/libupnp/ixml/test/testdata/tvcontrolSCPD.xml b/libupnp/ixml/test/testdata/tvcontrolSCPD.xml new file mode 100644 index 0000000..f556a59 --- /dev/null +++ b/libupnp/ixml/test/testdata/tvcontrolSCPD.xml @@ -0,0 +1,178 @@ + + + + + 1 + 0 + + + + + + + + PowerOn + + + + Power + Power + out + + + + + + PowerOff + + + + Power + Power + out + + + + + + SetChannel + + + + NewChannel + + Channel + out + + + + Channel + Channel + in + + + + + + IncreaseChannel + + + Channel + + Channel + out + + + + + + DecreaseChannel + + + Channel + + Channel + out + + + + + + SetVolume + + + + NewVolume + + Volume + out + + + Volume + Volume + in + + + + + + IncreaseVolume + + + + Volume + + Volume + out + + + + + + + DecreaseVolume + + + + Volume + + Volume + out + + + + + + + + + + + + + Power + Boolean + 0 + + + + Channel + i4 + + 1 + 100 + 1 + + 1 + + + + Volume + i4 + + 0 + 10 + 1 + + 5 + + + + + + + + + + + + + + + + + + + + + diff --git a/libupnp/ixml/test/testdata/tvdevicedesc.xml b/libupnp/ixml/test/testdata/tvdevicedesc.xml new file mode 100644 index 0000000..e14d854 --- /dev/null +++ b/libupnp/ixml/test/testdata/tvdevicedesc.xml @@ -0,0 +1,37 @@ + + + + 1 + 0 + + + urn:schemas-upnp-org:device:tvdevice:1 + UPnP Television Emulator + TV Manufacturer Name + http://www.manufacturer.com + UPnP Television Device Emulator 1.0 + TVEmulator + 1.0 + http://www.manufacturer.com/TVEmulator/ + 123456789001 + uuid:Upnp-TVEmulator-1_0-1234567890001 + 123456789 + + + urn:schemas-upnp-org:service:tvcontrol:1 + urn:upnp-org:serviceId:tvcontrol1 + /upnp/control/tvcontrol1 + /upnp/event/tvcontrol1 + /tvcontrolSCPD.xml + + + urn:schemas-upnp-org:service:tvpicture:1 + urn:upnp-org:serviceId:tvpicture1 + /upnp/control/tvpicture1 + /upnp/event/tvpicture1 + /tvpictureSCPD.xml + + + /tvdevicepres.html + + diff --git a/libupnp/ixml/test/testdata/tvpictureSCPD.xml b/libupnp/ixml/test/testdata/tvpictureSCPD.xml new file mode 100644 index 0000000..8388fd7 --- /dev/null +++ b/libupnp/ixml/test/testdata/tvpictureSCPD.xml @@ -0,0 +1,251 @@ + + + + + 1 + 0 + + + + + + + SetColor + + + NewColor + + Color + out + + + + Color + Color + in + + + + + + IncreaseColor + + + Color + + Color + out + + + + + + DecreaseColor + + + Color + + Color + out + + + + + + SetTint + + + NewTint + + Tint + out + + + + Tint + Tint + in + + + + + + IncreaseTint + + + Tint + + Tint + out + + + + + + DecreaseTint + + + Tint + + Tint + out + + + + + + SetContrast + + + NewContrast + + Contrast + out + + + Contrast + Contrast + in + + + + + + IncreaseContrast + + + Contrast + + Contrast + out + + + + + + DecreaseContrast + + + Contrast + + Contrast + out + + + + + + SetBrightness + + + + NewBrightness + + Brightness + out + + + + Brightness + Brightness + in + + + + + + IncreaseBrightness + + + + Brightness + + Brightness + out + + + + + + DecreaseBrightness + + + + Brightness + + Brightness + out + + + + + + + + + + + + Color + i4 + + 1 + 10 + 1 + + 5 + + + + Tint + i4 + + 1 + 10 + 1 + + 5 + + + + Contrast + i4 + + 1 + 10 + 1 + + 5 + + + + Brightness + i4 + + 1 + 10 + 1 + + 5 + + + + + + + + + + + + + + + + + + + + + + diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/legal.html b/libupnp/ixml/test/testdata/xml.ascc.net/legal.html new file mode 100644 index 0000000..d05b8f2 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/legal.html @@ -0,0 +1,85 @@ + + + + + + + + LEGAL NOTICES + + + + +

LEGAL NOTICES

+

Unless otherwise stated, pages hosted on the + website
+ Chinese XML Now!
+ are copyright (C) 1998 the designated authors and + translators,
+ and the Computing Center, Academia Sinica.

+

Unless otherwise stated, permission is granted to + use, copy, link to,
+ translate and republish in any form, including for commercial + use,
+ providing due acknowledgement of the copyright holder
+ and the Chinese XML Now! website is made.

+

If you believe any pages here are subject to + another copyright,
+ please email us (mailto: + ricko@gate.sinica.edu.tw)
+ with the appropriate correction.

+
+

The "Chinese Numberplate" logos copyright + (C) 1998 Rick Jelliffe and Computing Center, Academia Sinica.

+

The logo is provided as a public service for + information,
+ and does not represent a test result or endorsement.

+

For integrity, we do reserve the right to ask + makers who use
+ an incorrect version of the logo to correct it or to abstain + from using the logo.
+

+
+

The Chinese XML Test pages do not represent
+ an exhaustive test of some software's Chinese capability.
+ The tests merely represent a base-level which may
+ identify particular important characteristics quickly.

+

Permission is granted to copy and use the test + files
+ as part of any test suite. However, copiers must take
+ the utmost care to ensure that the test files
+ are maintained in the encoding given in their
+ XML encoding header.

+
+

Caveat: + Information provided about products at this site is
+ provided as a public service only, and does not imply
+ an endorsement of any product, any company, or any + technology.

+

Information at this site should be treated as an + indication only:
+ products and capabilities change quickly, so information
+ may only be correct at the time of publication.
+ Furthermore, due to the technical complexity and novelty
+ of XML technology, and the poor state of product + documentation,
+ it is possible that some information may be incorrect,
+ despite our best endevours.

+

If you believe any information here is + incorrect,
+ please email us (mailto: + ricko@gate.sinica.edu.tw)
+ with the appropriate correction.

+
+ + + diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-0.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-0.xml new file mode 100644 index 0000000..2e308f4 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-0.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #0: utf-8 + This file is the null test. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="utf-8"?>. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-1.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-1.xml new file mode 100644 index 0000000..c2c5ac6 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-1.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #1: UTF-8 + This file is the null test. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-10.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-10.xml new file mode 100644 index 0000000..8b2bb45 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-10.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #10: UTF-8 + This file has 1 Chinese character, directly entered. + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-11.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-11.xml new file mode 100644 index 0000000..8ef9d74 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-11.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #11: UTF-8 + This file has 1 Chinese character, directly entered. + This tests Native Language Markup (NLM). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: <中 />. + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-12.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-12.xml new file mode 100644 index 0000000..afc7240 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-12.xml @@ -0,0 +1,17 @@ + + + + + + Chinese Test #12: UTF-8 + This file has two attributes + with Chinese characters, directly entered. + This tests Native Language Markup (NLM). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + (In the DOCTYPE test, the + name element has an ID attribute, and this + element has an IDREF attribute.) + diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-2.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-2.xml new file mode 100644 index 0000000..63ee8b5 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-2.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #2: UTF-8 + This file has 1 Chinese character, encoded using + a decimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [中]. + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-3.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-3.xml new file mode 100644 index 0000000..59ecd5f --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-3.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #3: UTF-8 + This file has 1 Chinese character, encoded using + a hexadecimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [中]. + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-4.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-4.xml new file mode 100644 index 0000000..17666b4 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-4.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #4: UTF-8 + This file has 1 Chinese character, encoded using + a decimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-5.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-5.xml new file mode 100644 index 0000000..ea3eb56 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-5.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #5: UTF-8 + This file has 1 Chinese character, encoded using + a decimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-6.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-6.xml new file mode 100644 index 0000000..9aa0007 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-6.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #6: UTF-8 + This file has CDATA which looks like + a decimal numeric character reference. It is a + file with no non-ASCII characters (i.e. it is also a + UTF-8, ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + This is not the Chinese character for middle. + It should not look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-7.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-7.xml new file mode 100644 index 0000000..083d1c0 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-7.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #7: UTF-8 + This file has CDATA which looks like + a hexadecimal numeric character reference. It is a + file with no non-ASCII characters (i.e. it is also a + UTF-8, ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + This is not the Chinese character for middle. + It should not look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-8.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-8.xml new file mode 100644 index 0000000..47dd3e1 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-8.xml @@ -0,0 +1,16 @@ + + + + + + Chinese Test #8: UTF-8 + This file has 1 Chinese character, directly entered. + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [中] It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-9.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-9.xml new file mode 100644 index 0000000..3c2a0c3 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wf/zh-utf8-9.xml @@ -0,0 +1,13 @@ + + + + + + Chinese Test #9: UTF-8 + This file has 1 Chinese character, directly entered. + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [究]. + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-0.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-0.xml new file mode 100644 index 0000000..3961f9b --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-0.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #0: utf-8 + This file is the null test. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="utf-8"?>. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-1.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-1.xml new file mode 100644 index 0000000..b78d06e --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-1.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #1: UTF-8 + This file is the null test. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-10.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-10.xml new file mode 100644 index 0000000..352c5f7 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-10.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #10: UTF-8 + This file has 1 Chinese character, directly entered. + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-11.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-11.xml new file mode 100644 index 0000000..a392e45 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-11.xml @@ -0,0 +1,14 @@ + + + + + + Chinese Test #11: UTF-8 + This file has 1 Chinese character, directly entered. + This tests Native Language Markup (NLM). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: <中 />. + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-12.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-12.xml new file mode 100644 index 0000000..8261414 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-12.xml @@ -0,0 +1,17 @@ + + + + + + Chinese Test #12: UTF-8 + This file has two attributes + with Chinese characters, directly entered. + This tests Native Language Markup (NLM). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + (In the DOCTYPE test, the + name element has an ID attribute, and this + element has an IDREF attribute.) + diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-2.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-2.xml new file mode 100644 index 0000000..3d933ba --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-2.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #2: UTF-8 + This file has 1 Chinese character, encoded using + a decimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [中]. + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-3.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-3.xml new file mode 100644 index 0000000..927f936 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-3.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #3: UTF-8 + This file has 1 Chinese character, encoded using + a hexadecimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [中]. + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-4.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-4.xml new file mode 100644 index 0000000..a91e3dc --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-4.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #4: UTF-8 + This file has 1 Chinese character, encoded using + a decimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-5.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-5.xml new file mode 100644 index 0000000..50070cd --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-5.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #5: UTF-8 + This file has 1 Chinese character, encoded using + a decimal numeric character reference. It is a UTF-8 encoded + file with no non-ASCII characters (i.e. it is also an + ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-6.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-6.xml new file mode 100644 index 0000000..0cd0760 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-6.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #6: UTF-8 + This file has CDATA which looks like + a decimal numeric character reference. It is a + file with no non-ASCII characters (i.e. it is also a + UTF-8, ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + This is not the Chinese character for middle. + It should not look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-7.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-7.xml new file mode 100644 index 0000000..45a62d9 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-7.xml @@ -0,0 +1,20 @@ + + + + + + Chinese Test #7: UTF-8 + This file has CDATA which looks like + a hexadecimal numeric character reference. It is a + file with no non-ASCII characters (i.e. it is also a + UTF-8, ASCII or iso-8859-n file). + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + + This is not the Chinese character for middle. + It should not look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-8.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-8.xml new file mode 100644 index 0000000..282b945 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-8.xml @@ -0,0 +1,16 @@ + + + + + + Chinese Test #8: UTF-8 + This file has 1 Chinese character, directly entered. + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [中] It is the Chinese character for middle. + It should look like a box with a vertical + line through its middle. + + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-9.xml b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-9.xml new file mode 100644 index 0000000..561fb13 --- /dev/null +++ b/libupnp/ixml/test/testdata/xml.ascc.net/wfns/zh-utf8-9.xml @@ -0,0 +1,13 @@ + + + + + + Chinese Test #9: UTF-8 + This file has 1 Chinese character, directly entered. + + The XML header of this file is + <?xml encoding="UTF-8"?>. + + The character is here: [究]. + \ No newline at end of file diff --git a/libupnp/ixml/test/testdata/xmlconf/014.xml b/libupnp/ixml/test/testdata/xmlconf/014.xml new file mode 100644 index 0000000..b451bc3 --- /dev/null +++ b/libupnp/ixml/test/testdata/xmlconf/014.xml @@ -0,0 +1,5 @@ + + +]> + diff --git a/libupnp/ixml/test/testdata/xmlconf/040.xml b/libupnp/ixml/test/testdata/xmlconf/040.xml new file mode 100644 index 0000000..7185a7d --- /dev/null +++ b/libupnp/ixml/test/testdata/xmlconf/040.xml @@ -0,0 +1,5 @@ + + +]> + diff --git a/libupnp/ixml/test/testdata/xmlconf/059.xml b/libupnp/ixml/test/testdata/xmlconf/059.xml new file mode 100644 index 0000000..a2f741b --- /dev/null +++ b/libupnp/ixml/test/testdata/xmlconf/059.xml @@ -0,0 +1,10 @@ + + + +]> + + + + + diff --git a/libupnp/ixml/test/testdata/xmlconf/111.xml b/libupnp/ixml/test/testdata/xmlconf/111.xml new file mode 100644 index 0000000..6c8aa9d --- /dev/null +++ b/libupnp/ixml/test/testdata/xmlconf/111.xml @@ -0,0 +1,5 @@ + + +]> + diff --git a/libupnp/ixml/test/testdata/xmlconf/element.xml b/libupnp/ixml/test/testdata/xmlconf/element.xml new file mode 100644 index 0000000..9f02030 --- /dev/null +++ b/libupnp/ixml/test/testdata/xmlconf/element.xml @@ -0,0 +1,38 @@ + + + + + +]> + + + + + + + + + + + + + + allowed + ]]> + + also + ]]> + + moreover + + allowed & stuff + + also + + moreover + moreover + + too + + diff --git a/libupnp/libupnp.pc.in b/libupnp/libupnp.pc.in new file mode 100644 index 0000000..6de54b6 --- /dev/null +++ b/libupnp/libupnp.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libupnp +Description: Linux SDK for UPnP Devices +Version: @VERSION@ +Libs: @PTHREAD_LIBS@ -L${libdir} -lupnp -lthreadutil -lixml +Cflags: @PTHREAD_CFLAGS@ -I${includedir} + + diff --git a/libupnp/m4/acx_pthread.m4 b/libupnp/m4/acx_pthread.m4 new file mode 100644 index 0000000..06af842 --- /dev/null +++ b/libupnp/m4/acx_pthread.m4 @@ -0,0 +1,238 @@ +dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl +dnl @summary figure out how to build C programs using POSIX threads +dnl +dnl This macro figures out how to build C programs using POSIX threads. +dnl It sets the PTHREAD_LIBS output variable to the threads library and +dnl linker flags, and the PTHREAD_CFLAGS output variable to any special +dnl C compiler flags that are needed. (The user can also force certain +dnl compiler flags/libs to be tested by setting these environment +dnl variables.) +dnl +dnl Also sets PTHREAD_CC to any special C compiler that is needed for +dnl multi-threaded programs (defaults to the value of CC otherwise). +dnl (This is necessary on AIX to use the special cc_r compiler alias.) +dnl +dnl NOTE: You are assumed to not only compile your program with these +dnl flags, but also link it with them as well. e.g. you should link +dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS +dnl $LIBS +dnl +dnl If you are only building threads programs, you may wish to use +dnl these variables in your default LIBS, CFLAGS, and CC: +dnl +dnl LIBS="$PTHREAD_LIBS $LIBS" +dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +dnl CC="$PTHREAD_CC" +dnl +dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to +dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if a threads +dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to +dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the +dnl default action will define HAVE_PTHREAD. +dnl +dnl Please let the authors know if this macro fails on any platform, or +dnl if you have any other suggestions or comments. This macro was based +dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with +dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros +dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. +dnl We are also grateful for the helpful feedback of numerous users. +dnl +dnl @category InstalledPackages +dnl @author Steven G. Johnson +dnl @version 2005-06-15 +dnl @license GPLWithACException + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/libupnp/m4/ax_cflags_gcc_option.m4 b/libupnp/m4/ax_cflags_gcc_option.m4 new file mode 100644 index 0000000..dcd8579 --- /dev/null +++ b/libupnp/m4/ax_cflags_gcc_option.m4 @@ -0,0 +1,187 @@ +dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]]) +dnl +dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like +dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds the +dnl optionflag to CFLAGS if it is understood. You can override the +dnl shellvar-default of CFLAGS of course. The order of arguments stems +dnl from the explicit macros like AX_CFLAGS_WARN_ALL. +dnl +dnl The cousin AX_CXXFLAGS_GCC_OPTION would check for an option to add +dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C +dnl (since it is possible to use different compilers for C and C++). +dnl +dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or +dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options. +dnl However, if you use this macro in a few places, it would be great +dnl if you would make up a new function-macro and submit it to the +dnl ac-archive. +dnl +dnl - $1 option-to-check-for : required ("-option" as non-value) +dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case) +dnl - $3 action-if-found : add value to shellvariable +dnl - $4 action-if-not-found : nothing +dnl +dnl note: in earlier versions, $1-$2 were swapped. We try to detect the +dnl situation and accept a $2=~/-/ as being the old +dnl option-to-check-for. +dnl +dnl also: there are other variants that emerged from the original macro +dnl variant which did just test an option to be possibly added. +dnl However, some compilers accept an option silently, or possibly for +dnl just another option that was not intended. Therefore, we have to do +dnl a generic test for a compiler family. For gcc we check "-pedantic" +dnl being accepted which is also understood by compilers who just want +dnl to be compatible with gcc even when not being made from gcc +dnl sources. +dnl +dnl see also: +dnl +dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION +dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION +dnl +dnl @category C +dnl @author Guido Draheim +dnl @version 2003-11-04 +dnl @license GPLWithACException + +AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl ------------------------------------------------------------------------- + +AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])]) + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[AX_CXXFLAGS_GCC_OPTION_NEW($@)],[AX_CXXFLAGS_GCC_OPTION_OLD($@)])]) diff --git a/libupnp/m4/ax_cflags_warn_all.m4 b/libupnp/m4/ax_cflags_warn_all.m4 new file mode 100644 index 0000000..fa9bf30 --- /dev/null +++ b/libupnp/m4/ax_cflags_warn_all.m4 @@ -0,0 +1,118 @@ +dnl @synopsis AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] +dnl +dnl Try to find a compiler option that enables most reasonable +dnl warnings. This macro is directly derived from VL_PROG_CC_WARNINGS +dnl which is split up into two AX_CFLAGS_WARN_ALL and +dnl AX_CFLAGS_WARN_ALL_ANSI +dnl +dnl For the GNU CC compiler it will be -Wall (and -ansi -pedantic) The +dnl result is added to the shellvar being CFLAGS by default. +dnl +dnl Currently this macro knows about GCC, Solaris C compiler, Digital +dnl Unix C compiler, C for AIX Compiler, HP-UX C compiler, IRIX C +dnl compiler, NEC SX-5 (Super-UX 10) C compiler, and Cray J90 (Unicos +dnl 10.0.0.8) C compiler. +dnl +dnl - $1 shell-variable-to-add-to : CFLAGS +dnl - $2 add-value-if-not-found : nothing +dnl - $3 action-if-found : add value to shellvariable +dnl - $4 action-if-not-found : nothing +dnl +dnl @category C +dnl @author Guido Draheim +dnl @version 2003-01-06 +dnl @license GPLWithACException + +AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_warn_all])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % -Wall" dnl GCC + "-xstrconst % -v" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix + "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + "-ansi -ansiE % -fullwarn" dnl IRIX + "+ESlit % +w1" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[ + AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_warn_all])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % -Wall" dnl GCC + "-xstrconst % -v" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix + "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + "-ansi -ansiE % -fullwarn" dnl IRIX + "+ESlit % +w1" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[ + AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl implementation tactics: +dnl the for-argument contains a list of options. The first part of +dnl these does only exist to detect the compiler - usually it is +dnl a global option to enable -ansi or -extrawarnings. All other +dnl compilers will fail about it. That was needed since a lot of +dnl compilers will give false positives for some option-syntax +dnl like -Woption or -Xoption as they think of it is a pass-through +dnl to later compile stages or something. The "%" is used as a +dnl delimimiter. A non-option comment can be given after "%%" marks. diff --git a/libupnp/m4/ax_cflags_warn_all_ansi.m4 b/libupnp/m4/ax_cflags_warn_all_ansi.m4 new file mode 100644 index 0000000..020b2e9 --- /dev/null +++ b/libupnp/m4/ax_cflags_warn_all_ansi.m4 @@ -0,0 +1,118 @@ +dnl @synopsis AX_CFLAGS_WARN_ALL_ANSI [(shellvar [,default, [A/NA]])] +dnl +dnl Try to find a compiler option that enables most reasonable +dnl warnings. This macro is directly derived from VL_PROG_CC_WARNINGS +dnl which is split up into two AX_CFLAGS_WARN_ALL and +dnl AX_CFLAGS_WARN_ALL_ANSI +dnl +dnl For the GNU CC compiler it will be -Wall (and -ansi -pedantic) The +dnl result is added to the shellvar being CFLAGS by default. +dnl +dnl Currently this macro knows about GCC, Solaris C compiler, Digital +dnl Unix C compiler, C for AIX Compiler, HP-UX C compiler, IRIX C +dnl compiler, NEC SX-5 (Super-UX 10) C compiler, and Cray J90 (Unicos +dnl 10.0.0.8) C compiler. +dnl +dnl - $1 shell-variable-to-add-to : CFLAGS +dnl - $2 add-value-if-not-found : nothing +dnl - $3 action-if-found : add value to shellvariable +dnl - $4 action-if-not-found : nothing +dnl +dnl @category C +dnl @author Guido Draheim +dnl @version 2003-01-06 +dnl @license GPLWithACException + +AC_DEFUN([AX_CFLAGS_WARN_ALL_ANSI],[dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_warn_all_ansi])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum ansi warnings], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +# IRIX C compiler: +# -use_readonly_const is the default for IRIX C, +# puts them into .rodata, but they are copied later. +# need to be "-G0 -rdatashared" for strictmode but +# I am not sure what effect that has really. - guidod +for ac_arg dnl +in "-pedantic % -Wall -ansi -pedantic" dnl GCC + "-xstrconst % -v -Xc" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos -std1" dnl Digital Unix + " % -qlanglvl=ansi -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + " % -ansi -ansiE -fullwarn" dnl IRIX + "+ESlit % +w1 -Aa" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg -Xc" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2 -h conform" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[ + AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_WARN_ALL_ANSI],[dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_warn_all_ansi])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum ansi warnings], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +# IRIX C compiler: +# -use_readonly_const is the default for IRIX C, +# puts them into .rodata, but they are copied later. +# need to be "-G0 -rdatashared" for strictmode but +# I am not sure what effect that has really. - guidod +for ac_arg dnl +in "-pedantic % -Wall -ansi -pedantic" dnl GCC + "-xstrconst % -v -Xc" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos -std1" dnl Digital Unix + " % -qlanglvl=ansi -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + " % -ansi -ansiE -fullwarn" dnl IRIX + "+ESlit % +w1 -Aa" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg -Xc" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2 -h conform" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[ + AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) diff --git a/libupnp/m4/rt_bool_arg_enable.m4 b/libupnp/m4/rt_bool_arg_enable.m4 new file mode 100644 index 0000000..91300f8 --- /dev/null +++ b/libupnp/m4/rt_bool_arg_enable.m4 @@ -0,0 +1,53 @@ +dnl @synopsis RT_BOOL_ARG_ENABLE([FLAG],[DEFAULT],[HELP STRING]) +dnl +dnl This macro declares a configure option with 'AC_ARG_ENABLE'. +dnl It is a boolean argument (yes or no values only), and +dnl the corresponding shell variable 'enable_arg' is guaranteed to +dnl be one or the other. +dnl A message is also printed. +dnl +dnl Arguments +dnl $1 = flag name e.g. [debug] +dnl $2 = default value, shall be m4 constant, either [yes] or [no] +dnl $3 = help string (default value is appended) e.g. [compile debugging code] +dnl +dnl @version $Id: rt_bool_arg_enable.m4,v 1.2 2006/02/18 14:35:09 r3mi Exp $ +dnl @author Rémi Turboult +dnl @license GPLWithACException +dnl +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. +dnl +AC_DEFUN([RT_BOOL_ARG_ENABLE],[ + dnl + m4_pushdef([Name],AS_TR_SH($1))dnl + m4_pushdef([NAME],AS_TR_CPP($1))dnl + dnl + AC_MSG_CHECKING([for --enable-]Name) + dnl + dnl use some m4 sugar to have only one 'AC_ARG_ENABLE' declaration, + dnl else "configure --help" is confused + dnl + AC_ARG_ENABLE(Name, + [m4_case([$2], + [yes],AS_HELP_STRING([--disable-]Name, + [disable $3 @<:@default=enabled@:>@]), + [no],AS_HELP_STRING([--enable-]Name, + [enable $3 @<:@default=disabled@:>@]), + [m4_fatal([incorrect boolean argument '$2'])] + )]) + test "x$enable_[$1]" != [x]m4_if([$2],[yes],[no],[yes]) dnl + && enable_[]Name=[$2] + AC_MSG_RESULT($enable_[]Name)dnl + AM_CONDITIONAL([ENABLE_]NAME, test x"$enable_[]Name" = xyes) + dnl + m4_popdef([NAME])dnl + m4_popdef([Name])dnl + dnl +])dnl + + + diff --git a/libupnp/m4/type_socklen_t.m4 b/libupnp/m4/type_socklen_t.m4 new file mode 100644 index 0000000..349aad8 --- /dev/null +++ b/libupnp/m4/type_socklen_t.m4 @@ -0,0 +1,25 @@ +dnl @synopsis TYPE_SOCKLEN_T +dnl +dnl Check whether sys/socket.h defines type socklen_t. Please note that +dnl some systems require sys/types.h to be included before sys/socket.h +dnl can be compiled. +dnl +dnl @category Misc +dnl @author Lars Brinkhoff +dnl @version 2005-01-11 +dnl @license GPLWithACException + +AC_DEFUN([TYPE_SOCKLEN_T], +[AC_CACHE_CHECK([for socklen_t], ac_cv_type_socklen_t, +[ + AC_TRY_COMPILE( + [#include + #include ], + [socklen_t len = 42; return 0;], + ac_cv_type_socklen_t=yes, + ac_cv_type_socklen_t=no) +]) + if test $ac_cv_type_socklen_t != yes; then + AC_DEFINE(socklen_t, int, [Substitute for socklen_t]) + fi +]) diff --git a/libupnp/threadutil/Makefile.am b/libupnp/threadutil/Makefile.am new file mode 100644 index 0000000..1964fc1 --- /dev/null +++ b/libupnp/threadutil/Makefile.am @@ -0,0 +1,32 @@ +# $Id: Makefile.am,v 1.2 2006/02/27 21:38:56 r3mi Exp $ +# +# "Makefile.am" for "libupnp/threadutil" +# +# (C) Copyright 2005 Rémi Turboult +# + +AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc +AM_CFLAGS = $(PTHREAD_CFLAGS) + +if ENABLE_DEBUG + AM_CPPFLAGS += -DDEBUG -DSTATS +else + AM_CPPFLAGS += -DNO_DEBUG -DNDEBUG +endif + + +lib_LTLIBRARIES = libthreadutil.la + +libthreadutil_la_LDFLAGS = -version-info $(LT_VERSION_THREADUTIL) + +libthreadutil_la_SOURCES = \ + src/FreeList.c src/LinkedList.c \ + src/ThreadPool.c src/TimerThread.c \ + src/iasnprintf.c + +upnpincludedir = $(includedir)/upnp +upnpinclude_HEADERS = \ + inc/FreeList.h inc/LinkedList.h \ + inc/ThreadPool.h inc/TimerThread.h \ + inc/iasnprintf.h inc/ithread.h + diff --git a/libupnp/threadutil/inc/FreeList.h b/libupnp/threadutil/inc/FreeList.h new file mode 100644 index 0000000..dd56135 --- /dev/null +++ b/libupnp/threadutil/inc/FreeList.h @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef FREE_LIST_H +#define FREE_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#include + + +#include "ithread.h" +#include + +/**************************************************************************** + * Name: FreeListNode + * + * Description: + * free list node. points to next free item. + * memory for node is borrowed from allocated items. + * Internal Use Only. + *****************************************************************************/ +typedef struct FREELISTNODE +{ + struct FREELISTNODE*next; //pointer to next free node +} FreeListNode; + + +/**************************************************************************** + * Name: FreeList + * + * Description: + * Stores head and size of free list, as well as mutex for protection. + * Internal Use Only. + *****************************************************************************/ +typedef struct FREELIST +{ + FreeListNode *head; //head of free list + size_t element_size; //size of elements in free + //list + int maxFreeListLength; //max size of free structures + //to keep + int freeListLength; //current size of free list + +}FreeList; + +/**************************************************************************** + * Function: FreeListInit + * + * Description: + * Initializes Free List. Must be called first. + * And only once for FreeList. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * size_t - size of elements to store in free list + * maxFreeListSize - max size that the free list can grow to + * before returning memory to O.S. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + *****************************************************************************/ +int FreeListInit(FreeList *free_list, + size_t elementSize, + int maxFreeListSize); + +/**************************************************************************** + * Function: FreeListAlloc + * + * Description: + * Allocates chunk of set size. + * If a free item is available in the list, returnes the stored item. + * Otherwise calls the O.S. to allocate memory. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * Returns: + * Non NULL on success. NULL on failure. + *****************************************************************************/ +void * FreeListAlloc (FreeList *free_list); + +/**************************************************************************** + * Function: FreeListFree + * + * Description: + * Returns an item to the Free List. + * If the free list is smaller than the max size than + * adds the item to the free list. + * Otherwise returns the item to the O.S. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + *****************************************************************************/ +int FreeListFree (FreeList *free_list,void * element); + +/**************************************************************************** + * Function: FreeListDestroy + * + * Description: + * Releases the resources stored with the free list. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + *****************************************************************************/ +int FreeListDestroy (FreeList *free_list); + + +#ifdef __cplusplus +} +#endif + +#endif // FREE_LIST_H diff --git a/libupnp/threadutil/inc/LinkedList.h b/libupnp/threadutil/inc/LinkedList.h new file mode 100644 index 0000000..2572723 --- /dev/null +++ b/libupnp/threadutil/inc/LinkedList.h @@ -0,0 +1,330 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef LINKED_LIST_H +#define LINKED_LIST_H + +#include "FreeList.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EOUTOFMEM (-7 & 1<<29) + +#define FREELISTSIZE 100 +#define LIST_SUCCESS 1 +#define LIST_FAIL 0 + +/**************************************************************************** + * Name: free_routine + * + * Description: + * Function for freeing list items + *****************************************************************************/ +typedef void (*free_function)(void *arg); + +/**************************************************************************** + * Name: cmp_routine + * + * Description: + * Function for comparing list items + * Returns 1 if itemA==itemB + *****************************************************************************/ +typedef int (*cmp_routine)(void *itemA,void *itemB); + +/**************************************************************************** + * Name: ListNode + * + * Description: + * linked list node. stores generic item and pointers to next and prev. + * Internal Use Only. + *****************************************************************************/ +typedef struct LISTNODE +{ + struct LISTNODE *prev; //previous node + struct LISTNODE *next; //next node + void *item; //item +} ListNode; + +/**************************************************************************** + * Name: LinkedList + * + * Description: + * linked list (no protection). Internal Use Only. + * Because this is for internal use, parameters are NOT checked for + * validity. + * The first item of the list is stored at node: head->next + * The last item of the list is stored at node: tail->prev + * If head->next=tail, then list is empty. + * To iterate through the list: + * + * LinkedList g; + * ListNode *temp = NULL; + * for (temp = ListHead(g);temp!=NULL;temp = ListNext(g,temp)) + * { + * } + * + *****************************************************************************/ +typedef struct LINKEDLIST +{ + ListNode head; //head, first item is stored at: head->next + ListNode tail; //tail, last item is stored at: tail->prev + long size; //size of list + FreeList freeNodeList; //free list to use + free_function free_func; //free function to use + cmp_routine cmp_func; //compare function to use +} LinkedList; + +/**************************************************************************** + * Function: ListInit + * + * Description: + * Initializes LinkedList. Must be called first. + * And only once for List. + * Parameters: + * list - must be valid, non null, pointer to a linked list. + * cmp_func - function used to compare items. (May be NULL) + * free_func - function used to free items. (May be NULL) + * Returns: + * 0 on success, EOUTOFMEM on failure. + *****************************************************************************/ +int ListInit(LinkedList *list,cmp_routine cmp_func, free_function free_func); + +/**************************************************************************** + * Function: ListAddHead + * + * Description: + * Adds a node to the head of the list. + * Node gets immediately after list.head. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * void * item - item to be added + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode *ListAddHead(LinkedList *list, void *item); + +/**************************************************************************** + * Function: ListAddTail + * + * Description: + * Adds a node to the tail of the list. + * Node gets added immediately before list.tail. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * void * item - item to be added + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode *ListAddTail(LinkedList *list, void *item); + +/**************************************************************************** + * Function: ListAddAfter + * + * Description: + * Adds a node after the specified node. + * Node gets added immediately after bnode. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * void * item - item to be added + * ListNode * bnode - node to add after + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode *ListAddAfter(LinkedList *list, void *item, ListNode *bnode); + + +/**************************************************************************** + * Function: ListAddBefore + * + * Description: + * Adds a node before the specified node. + * Node gets added immediately before anode. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * ListNode * anode - node to add the in front of. + * void * item - item to be added + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode *ListAddBefore(LinkedList *list,void *item, ListNode *anode); + + +/**************************************************************************** + * Function: ListDelNode + * + * Description: + * Removes a node from the list + * The memory for the node is freed. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * ListNode *dnode - done to delete. + * freeItem - if !0 then item is freed using free function. + * if 0 (or free function is NULL) then item is not freed + * Returns: + * The pointer to the item stored in the node or NULL if the item is freed. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +void *ListDelNode(LinkedList *list,ListNode *dnode, int freeItem); + +/**************************************************************************** + * Function: ListDestroy + * + * Description: + * Removes all memory associated with list nodes. + * Does not free LinkedList *list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * freeItem - if !0 then items are freed using the free_function. + * if 0 (or free function is NULL) then items are not freed. + * Returns: + * 0 on success. Always returns 0. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +int ListDestroy(LinkedList *list, int freeItem); + + +/**************************************************************************** + * Function: ListHead + * + * Description: + * Returns the head of the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The head of the list. NULL if list is empty. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode* ListHead(LinkedList *list); + +/**************************************************************************** + * Function: ListTail + * + * Description: + * Returns the tail of the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The tail of the list. NULL if list is empty. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode* ListTail(LinkedList *list); + +/**************************************************************************** + * Function: ListNext + * + * Description: + * Returns the next item in the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The next item in the list. NULL if there are no more items in list. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode* ListNext(LinkedList *list, ListNode * node); + +/**************************************************************************** + * Function: ListPrev + * + * Description: + * Returns the previous item in the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The previous item in the list. NULL if there are no more items in list. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode* ListPrev(LinkedList *list, ListNode * node); + +/**************************************************************************** + * Function: ListFind + * + * Description: + * Finds the specified item in the list. + * Uses the compare function specified in ListInit. If compare function + * is NULL then compares items as pointers. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * ListNode *start - the node to start from, NULL if to start from + * beginning. + * void * item - the item to search for. + * Returns: + * The node containing the item. NULL if no node contains the item. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode* ListFind(LinkedList *list, ListNode *start, void * item); + +/**************************************************************************** + * Function: ListSize + * + * Description: + * Returns the size of the list. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + + * Returns: + * The number of items in the list. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +int ListSize(LinkedList* list); + + +#ifdef __cplusplus +} +#endif + +#endif //LINKED_LIST_H diff --git a/libupnp/threadutil/inc/ThreadPool.h b/libupnp/threadutil/inc/ThreadPool.h new file mode 100644 index 0000000..cd7f558 --- /dev/null +++ b/libupnp/threadutil/inc/ThreadPool.h @@ -0,0 +1,539 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef THREADPOOL_H +#define THREADPOOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +//Size of job free list +#define JOBFREELISTSIZE 100 + +#define INFINITE_THREADS -1 + +#define EMAXTHREADS (-8 & 1<<29) + +//Invalid Policy +#define INVALID_POLICY (-9 & 1<<29) + +//Invalid JOB Id +#define INVALID_JOB_ID (-2 & 1<<29) + +typedef enum duration {SHORT_TERM,PERSISTENT} Duration; + +typedef enum priority {LOW_PRIORITY, + MED_PRIORITY, + HIGH_PRIORITY} ThreadPriority; + +#define DEFAULT_PRIORITY MED_PRIORITY //default priority used by TPJobInit +#define DEFAULT_MIN_THREADS 1 //default minimum used by TPAttrInit +#define DEFAULT_MAX_THREADS 10 //default max used by TPAttrInit +#define DEFAULT_JOBS_PER_THREAD 10 //default jobs per thread used by TPAttrInit +#define DEFAULT_STARVATION_TIME 500 //default starvation time used by TPAttrInit +#define DEFAULT_IDLE_TIME 10 * 1000 //default idle time used by TPAttrInit +#define DEFAULT_FREE_ROUTINE NULL //default free routine used TPJobInit + +#define STATS 1 //always include stats because code change is minimal + + +//Statistics +#ifdef STATS +#define STATSONLY(x) x +#else +#define STATSONLY(x) +#endif + +#ifdef _DEBUG +#define DEBUG 1 +#endif + +//DEBUGGING +#ifdef DEBUG +#define DBGONLY(x) x +#else +#define DBGONLY(x) +#endif + +#include "LinkedList.h" +#include +#include "FreeList.h" + +#include "ithread.h" +#include +#include +#define EXPORT +typedef int PolicyType; +#define DEFAULT_POLICY SCHED_OTHER +#define DEFAULT_SCHED_PARAM 0 //default priority + +/**************************************************************************** + * Name: free_routine + * + * Description: + * Function for freeing a thread argument + *****************************************************************************/ +typedef void (*free_routine)(void *arg); + +/**************************************************************************** + * Name: ThreadPoolAttr + * + * Description: + * Attributes for thread pool. Used to set and change parameters of + * thread pool + *****************************************************************************/ +typedef struct THREADPOOLATTR +{ + int minThreads; //minThreads, ThreadPool will always maintain at least + //this many threads + + int maxThreads; //maxThreads, ThreadPool will never have more than this + //number of threads + + int maxIdleTime; //maxIdleTime (in milliseconds) + // this is the maximum time a thread will remain idle + // before dying + + int jobsPerThread; //jobs per thread to maintain + + int starvationTime; //the time a low priority or med priority + //job waits before getting bumped + //up a priority (in milliseconds) + + PolicyType schedPolicy; //scheduling policy to use + +} ThreadPoolAttr; + +/**************************************************************************** + * Name: ThreadPool + * + * Description: + * Internal ThreadPool Job + *****************************************************************************/ +typedef struct THREADPOOLJOB +{ + start_routine func; //function + void *arg; //arg + free_routine free_func; //free function + struct timeb requestTime; //time of request + int priority; //priority of request + int jobId; //id +} ThreadPoolJob; + +/**************************************************************************** + * Name: ThreadPoolStats + * + * Description: + * Structure to hold statistics + *****************************************************************************/ + +STATSONLY( + +typedef struct TPOOLSTATS +{ + double totalTimeHQ; //total time spent by all jobs in high priority Q + int totalJobsHQ; //total jobs in HQ run so far + double avgWaitHQ; //average wait in HQ + double totalTimeMQ; //total time spent by all jobs in med priority Q + int totalJobsMQ; //total jobs in MQ run so far + double avgWaitMQ; //average wait in MQ + double totalTimeLQ; //total time spent by all jobs in low priority Q + int totalJobsLQ; //total jobs in LQ run so far + double avgWaitLQ; //average wait in LQ + double totalWorkTime; //total time spent working for all threads + double totalIdleTime; //total time spent idle for all threads + int workerThreads; //number of current workerThreads + int idleThreads; //number of current idle threads + int persistentThreads; //number of persistent threads + int totalThreads; //total number of current threads + int maxThreads; //max threads so far + int currentJobsHQ; // current jobs in Q + int currentJobsLQ; //current jobs in Q + int currentJobsMQ; //current jobs in Q +}ThreadPoolStats; + +) + + +/**************************************************************************** + * Name: ThreadPool + * + * Description: + * A thread pool similar to the thread pool in the UPnP SDK. + * Allows jobs to be scheduled for running by threads in a + * thread pool. The thread pool is initialized with a + * minimum and maximum thread number as well as a + * max idle time + * and a jobs per thread ratio. If a worker thread waits the whole + * max idle time without receiving a job and the thread pool + * currently has more threads running than the minimum + * then the worker thread will exit. If when + * scheduling a job the current job to thread ratio + * becomes greater than the set ratio and the thread pool currently has + * less than the maximum threads then a new thread will + * be created. + * + *****************************************************************************/ + +typedef struct THREADPOOL +{ + ithread_mutex_t mutex; //mutex to protect job qs + ithread_cond_t condition; //condition variable to signal Q + ithread_cond_t start_and_shutdown; //condition variable for start + //and stop + int lastJobId; //ids for jobs + int shutdown; //whether or not we are shutting down + int totalThreads; //total number of threads + int persistentThreads; //number of persistent threads + FreeList jobFreeList; //free list of jobs + LinkedList lowJobQ; //low priority job Q + LinkedList medJobQ; //med priority job Q + LinkedList highJobQ; //high priority job Q + ThreadPoolJob *persistentJob; //persistent job + + ThreadPoolAttr attr; //thread pool attributes + + //statistics + STATSONLY(ThreadPoolStats stats;) + +} ThreadPool; + + + +/**************************************************************************** + * Function: ThreadPoolInit + * + * Description: + * Initializes and starts ThreadPool. Must be called first. + * And only once for ThreadPool. + * Parameters: + * tp - must be valid, non null, pointer to ThreadPool. + * attr - can be null + * + * if not null then attr contains the following fields: + * + * minWorkerThreads - minimum number of worker threads + * thread pool will never have less than this + * number of threads. + * maxWorkerThreads - maximum number of worker threads + * thread pool will never have more than this + * number of threads. + * maxIdleTime - maximum time that a worker thread will spend + * idle. If a worker is idle longer than this + * time and there are more than the min + * number of workers running, than the + * worker thread exits. + * jobsPerThread - ratio of jobs to thread to try and maintain + * if a job is scheduled and the number of jobs per + * thread is greater than this number,and + * if less than the maximum number of + * workers are running then a new thread is + * started to help out with efficiency. + * schedPolicy - scheduling policy to try and set (OS dependent) + * Returns: + * 0 on success, nonzero on failure. + * EAGAIN if not enough system resources to create minimum threads. + * INVALID_POLICY if schedPolicy can't be set + * EMAXTHREADS if minimum threads is greater than maximum threads + *****************************************************************************/ +int ThreadPoolInit(ThreadPool *tp, + ThreadPoolAttr *attr); + +/**************************************************************************** + * Function: ThreadPoolAddPersistent + * + * Description: + * Adds a persistent job to the thread pool. + * Job will be run as soon as possible. + * Call will block until job is scheduled. + * Parameters: + * tp - valid thread pool pointer + * ThreadPoolJob - valid thread pool job with the following fields: + * + * func - ThreadFunction to run + * arg - argument to function. + * priority - priority of job. + * + * Returns: + * 0 on success, nonzero on failure + * EOUTOFMEM not enough memory to add job. + * EMAXTHREADS not enough threads to add persistent job. + *****************************************************************************/ +int ThreadPoolAddPersistent (ThreadPool*tp, + ThreadPoolJob *job, + int *jobId); + +/**************************************************************************** + * Function: ThreadPoolGetAttr + * + * Description: + * Gets the current set of attributes + * associated with the thread pool. + * Parameters: + * tp - valid thread pool pointer + * out - non null pointer to store attributes + * Returns: + * 0 on success, nonzero on failure + * Always returns 0. + *****************************************************************************/ +int ThreadPoolGetAttr(ThreadPool *tp, + ThreadPoolAttr *out); +/**************************************************************************** + * Function: ThreadPoolSetAttr + * + * Description: + * Sets the attributes for the thread pool. + * Only affects future calculations. + * Parameters: + * tp - valid thread pool pointer + * attr - pointer to attributes, null sets attributes to default. + * Returns: + * 0 on success, nonzero on failure + * Returns INVALID_POLICY if policy can not be set. + *****************************************************************************/ +int ThreadPoolSetAttr(ThreadPool *tp, + ThreadPoolAttr *attr); + +/**************************************************************************** + * Function: ThreadPoolAdd + * + * Description: + * Adds a job to the thread pool. + * Job will be run as soon as possible. + * Parameters: + * tp - valid thread pool pointer + * func - ThreadFunction to run + * arg - argument to function. + * priority - priority of job. + * poolid - id of job + * free_function - function to use when freeing argument + * Returns: + * 0 on success, nonzero on failure + * EOUTOFMEM if not enough memory to add job. + *****************************************************************************/ +int ThreadPoolAdd (ThreadPool*tp, + ThreadPoolJob *job, + int *jobId); + +/**************************************************************************** + * Function: ThreadPoolRemove + * + * Description: + * Removes a job from the thread pool. + * Can only remove jobs which are not + * currently running. + * Parameters: + * tp - valid thread pool pointer + * jobid - id of job + * out - space for removed job. + * Returns: + * 0 on success, nonzero on failure. + * INVALID_JOB_ID if job not found. + *****************************************************************************/ +int ThreadPoolRemove(ThreadPool *tp, + int jobId, ThreadPoolJob *out); + + + +/**************************************************************************** + * Function: ThreadPoolShutdown + * + * Description: + * Shuts the thread pool down. + * Waits for all threads to finish. + * May block indefinitely if jobs do not + * exit. + * Parameters: + * tp - must be valid tp + * Returns: + * 0 on success, nonzero on failure + * Always returns 0. + *****************************************************************************/ +int ThreadPoolShutdown(ThreadPool *tp); + + +/**************************************************************************** + * Function: TPJobInit + * + * Description: + * Initializes thread pool job. + * Sets the priority to default defined in ThreadPool.h. + * Sets the free_routine to default defined in ThreadPool.h + * Parameters: + * ThreadPoolJob *job - must be valid thread pool attributes. + * start_routine func - function to run, must be valid + * void * arg - argument to pass to function. + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPJobInit(ThreadPoolJob *job, start_routine func, void *arg); + +/**************************************************************************** + * Function: TPJobSetPriority + * + * Description: + * Sets the max threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * maxThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPJobSetPriority(ThreadPoolJob *job, ThreadPriority priority); + +/**************************************************************************** + * Function: TPJobSetFreeFunction + * + * Description: + * Sets the max threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * maxThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPJobSetFreeFunction(ThreadPoolJob *job, free_routine func); + + +/**************************************************************************** + * Function: TPAttrInit + * + * Description: + * Initializes thread pool attributes. + * Sets values to defaults defined in ThreadPool.h. + * Parameters: + * attr - must be valid thread pool attributes. + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrInit(ThreadPoolAttr *attr); + +/**************************************************************************** + * Function: TPAttrSetMaxThreads + * + * Description: + * Sets the max threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * maxThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrSetMaxThreads(ThreadPoolAttr *attr, int maxThreads); + +/**************************************************************************** + * Function: TPAttrSetMinThreads + * + * Description: + * Sets the min threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * minThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrSetMinThreads(ThreadPoolAttr *attr, int minThreads); + +/**************************************************************************** + * Function: TPAttrSetIdleTime + * + * Description: + * Sets the idle time for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrSetIdleTime(ThreadPoolAttr *attr, int idleTime); + +/**************************************************************************** + * Function: TPAttrSetJobsPerThread + * + * Description: + * Sets the jobs per thread ratio + * Parameters: + * attr - must be valid thread pool attributes. + * jobsPerThread - number of jobs per thread to maintain + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrSetJobsPerThread(ThreadPoolAttr *attr, int jobsPerThread); + +/**************************************************************************** + * Function: TPAttrSetStarvationTime + * + * Description: + * Sets the starvation time for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * int starvationTime - milliseconds + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrSetStarvationTime(ThreadPoolAttr *attr, int starvationTime); + +/**************************************************************************** + * Function: TPAttrSetSchedPolicy + * + * Description: + * Sets the scheduling policy for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * PolicyType schedPolicy - must be a valid policy type. + * Returns: + * Always returns 0. + *****************************************************************************/ +int TPAttrSetSchedPolicy(ThreadPoolAttr *attr, PolicyType schedPolicy); + + +/**************************************************************************** + * Function: ThreadPoolGetStats + * + * Description: + * Returns various statistics about the + * thread pool. + * Only valid if STATS has been defined. + * Parameters: + * ThreadPool *tp - valid initialized threadpool + * ThreadPoolStats *stats - valid stats, out parameter + * Returns: + * Always returns 0. + *****************************************************************************/ +STATSONLY( EXPORT int ThreadPoolGetStats(ThreadPool *tp, ThreadPoolStats *stats);); + +STATSONLY(EXPORT void ThreadPoolPrintStats(ThreadPoolStats *stats);); + +#ifdef __cplusplus +} +#endif + +#endif //ThreadPool diff --git a/libupnp/threadutil/inc/TimerThread.h b/libupnp/threadutil/inc/TimerThread.h new file mode 100644 index 0000000..1659de8 --- /dev/null +++ b/libupnp/threadutil/inc/TimerThread.h @@ -0,0 +1,191 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef TIMERTHREAD_H +#define TIMERTHREAD_H + +#include "ithread.h" +#include "LinkedList.h" +#include "FreeList.h" +#include "ThreadPool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define INVALID_EVENT_ID (-10 & 1<<29) + +//Timeout Types +//absolute means in seconds from Jan 1, 1970 +//relative means in seconds from current time +typedef enum timeoutType {ABS_SEC,REL_SEC} TimeoutType; + + +/**************************************************************************** + * Name: TimerThread + * + * Description: + * A timer thread similar to the one in the Upnp SDK that allows + * the scheduling of a job to run at a specified time in the future + * Because the timer thread uses the thread pool there is no + * gurantee of timing, only approximate timing. + * Uses ThreadPool, Mutex, Condition, Thread + * + * + *****************************************************************************/ +typedef struct TIMERTHREAD +{ + ithread_mutex_t mutex; //mutex to protect eventQ + ithread_cond_t condition; //condition variable + int lastEventId; //last event id + LinkedList eventQ; //event q + int shutdown; //whether or not we are shutdown + FreeList freeEvents; //FreeList for events + ThreadPool *tp; //ThreadPool to use +} TimerThread; + + +/**************************************************************************** + * Name: TimerEvent + * + * Description: + * + * Struct to contain information for a timer event. + * Internal to the TimerThread + * + *****************************************************************************/ +typedef struct TIMEREVENT +{ + ThreadPoolJob job; + time_t eventTime; //absolute time for event in seconds since Jan 1, 1970 + Duration persistent; //long term or short term job + int id; //id of job +} TimerEvent; + + + + +/************************************************************************ + * Function: TimerThreadInit + * + * Description: + * Initializes and starts timer thread. + * + * Parameters: + * timer - valid timer thread pointer. + * tp - valid thread pool to use. Must be + * started. Must be valid for lifetime + * of timer. Timer must be shutdown + * BEFORE thread pool. + * Return: + * 0 on success, nonzero on failure + * Returns error from ThreadPoolAddPersistent on failure. + * + ************************************************************************/ +int TimerThreadInit(TimerThread *timer, + ThreadPool *tp); + + +/************************************************************************ + * Function: TimerThreadSchedule + * + * Description: + * Schedules an event to run at a specified time. + * + * Parameters: + * timer - valid timer thread pointer. + * time_t - time of event. + * either in absolute seconds, + * or relative seconds in the future. + * timeoutType - either ABS_SEC, or REL_SEC. + * if REL_SEC, then the event + * will be scheduled at the + * current time + REL_SEC. + * job-> valid Thread pool job with following fields + * func - function to schedule + * arg - argument to function + * priority - priority of job. + * + * id - id of timer event. (out, can be null) + * Return: + * 0 on success, nonzero on failure + * EOUTOFMEM if not enough memory to schedule job. + * + ************************************************************************/ +int TimerThreadSchedule(TimerThread* timer, + time_t time, + TimeoutType type, + ThreadPoolJob *job, + Duration duration, + int *id); + +/************************************************************************ + * Function: TimerThreadRemove + * + * Description: + * Removes an event from the timer Q. + * Events can only be removed + * before they have been placed in the + * thread pool. + * + * Parameters: + * timer - valid timer thread pointer. + * id - id of event to remove. + * ThreadPoolJob *out - space for thread pool job. + * Return: + * 0 on success, + * INVALID_EVENT_ID on failure + * + ************************************************************************/ +int TimerThreadRemove(TimerThread *timer, + int id, + ThreadPoolJob *out); + +/************************************************************************ + * Function: TimerThreadShutdown + * + * Description: + * Shutdown the timer thread + * Events scheduled in the future will NOT be run. + * Timer thread should be shutdown BEFORE it's associated + * thread pool. + * Returns: + * returns 0 if succesfull, + * nonzero otherwise. + * Always returns 0. + ***********************************************************************/ +int TimerThreadShutdown(TimerThread *timer); + +#ifdef __cplusplus +} +#endif + +#endif //TIMER_THREAD_H diff --git a/libupnp/threadutil/inc/iasnprintf.h b/libupnp/threadutil/inc/iasnprintf.h new file mode 100644 index 0000000..b4f1331 --- /dev/null +++ b/libupnp/threadutil/inc/iasnprintf.h @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include + +#define EXPORT + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Allocates enough memory for the + * Formatted string, up to max + * specified. + * With max set to -1, allocates + * as much size as needed. + * Memory must be freed using free. + */ + +EXPORT int iasnprintf(char **ret, + int incr, + int max, + const char * fmt, ...) +#ifndef SPARC_SOLARIS + #if (__GNUC__ >= 3) + __attribute__((format (__printf__, 4, 5))); + #else + ; + #endif + EXPORT void iasnprintfFree(char *); +#else +; +#endif + +#ifdef __cplusplus +} +#endif diff --git a/libupnp/threadutil/inc/ithread.h b/libupnp/threadutil/inc/ithread.h new file mode 100644 index 0000000..b785f63 --- /dev/null +++ b/libupnp/threadutil/inc/ithread.h @@ -0,0 +1,536 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef ITHREADH +#define ITHREADH +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef DEBUG +#define DEBUG_ONLY(x) x +#else +#define DEBUG_ONLY(x) +#endif + +#include +#include + +#define ITHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_FAST_NP +#define ITHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE_NP +#define ITHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK_NP +#define ITHREAD_CANCELED PTHREAD_CANCELED + + + /*************************************************************************** + * Name: ithread_t + * + * Description: + * Thread handle. + * typedef to pthread_t. + * Internal Use Only. + ***************************************************************************/ + typedef pthread_t ithread_t; + + /**************************************************************************** + * Name: ithread_attr_t + * + * Description: + * Thread attribute. + * typedef to pthread_attr_t + * Internal Use Only + ***************************************************************************/ + typedef pthread_attr_t ithread_attr_t; + + + /**************************************************************************** + * Name: start_routine + * + * Description: + * Thread start routine + * Internal Use Only. + ***************************************************************************/ + typedef void * (*start_routine) (void *arg); + + + /**************************************************************************** + * Name: ithread_cond_t + * + * Description: + * condition variable. + * typedef to pthread_cond_t + * Internal Use Only. + ***************************************************************************/ + typedef pthread_cond_t ithread_cond_t; + + + /**************************************************************************** + * Name: ithread_mutexattr_t + * + * Description: + * Mutex attribute. + * typedef to pthread_mutexattr_t + * Internal Use Only + ***************************************************************************/ + typedef pthread_mutexattr_t ithread_mutexattr_t; + + + /**************************************************************************** + * Name: ithread_mutex_t + * + * Description: + * Mutex. + * typedef to pthread_mutex_t + * Internal Use Only. + ***************************************************************************/ + typedef pthread_mutex_t ithread_mutex_t; + + + /**************************************************************************** + * Name: ithread_condattr_t + * + * Description: + * Condition attribute. + * typedef to pthread_condattr_t + * NOT USED + * Internal Use Only + ***************************************************************************/ + typedef pthread_condattr_t ithread_condattr_t; + + /**************************************************************************** + * Function: ithread_mutexattr_init + * + * Description: + * Initializes a mutex attribute variable. + * Used to set the type of the mutex. + * Parameters: + * ithread_mutexattr_init * attr (must be valid non NULL pointer to + * pthread_mutexattr_t) + * Returns: + * 0 on success, Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutexattr_init + ***************************************************************************/ + +#define ithread_mutexattr_init pthread_mutexattr_init + + /**************************************************************************** + * Function: ithread_mutexattr_destroy + * + * Description: + * Releases any resources held by the mutex attribute. + * Currently there are no resources associated with the attribute + * Parameters: + * ithread_mutexattr_t * attr (must be valid non NULL pointer to + * pthread_mutexattr_t) + * Returns: + * 0 on success, Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutexattr_destroy + ***************************************************************************/ +#define ithread_mutexattr_destroy pthread_mutexattr_destroy + + +/**************************************************************************** + * Function: ithread_mutexattr_setkind_np + * + * Description: + * Sets the mutex type in the attribute. + * Valid types are: ITHREAD_MUTEX_FAST_NP + * ITHREAD_MUTEX_RECURSIVE_NP + * ITHREAD_MUTEX_ERRORCHECK_NP + * + * Parameters: + * ithread_mutexattr_t * mutex (must be valid non NULL pointer to + * ithread_mutexattr_t) + * int kind (one of ITHREAD_MUTEX_FAST_NP or ITHREAD_MUTEX_RECURSIVE_NP + * or ITHREAD_MUTEX_ERRORCHECK_NP) + * Returns: + * 0 on success. Nonzero on failure. + * Returns EINVAL if the kind is not supported. + * See man page for pthread_mutexattr_setkind_np + *****************************************************************************/ +#define ithread_mutexattr_setkind_np pthread_mutexattr_setkind_np + + +/**************************************************************************** + * Function: ithread_mutexattr_getkind_np + * + * Description: + * Gets the mutex type in the attribute. + * Valid types are: ITHREAD_MUTEX_FAST_NP + * ITHREAD_MUTEX_RECURSIVE_NP + * ITHREAD_MUTEX_ERRORCHECK_NP + * + * Parameters: + * ithread_mutexattr_t * mutex (must be valid non NULL pointer to + * pthread_mutexattr_t) + * int *kind (one of ITHREAD_MUTEX_FAST_NP or ITHREAD_MUTEX_RECURSIVE_NP + * or ITHREAD_MUTEX_ERRORCHECK_NP) + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutexattr_getkind_np + *****************************************************************************/ +#define ithread_mutexattr_getkind_np pthread_mutexattr_getkind_np + + +/**************************************************************************** + * Function: ithread_mutex_init + * + * Description: + * Initializes mutex. + * Must be called before use. + * + * Parameters: + * ithread_mutex_t * mutex (must be valid non NULL pointer to pthread_mutex_t) + * const ithread_mutexattr_t * mutex_attr + * Returns: + * 0 on success, Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutex_init + *****************************************************************************/ +#define ithread_mutex_init pthread_mutex_init + +/**************************************************************************** + * Function: ithread_mutex_lock + * + * Description: + * Locks mutex. + * Parameters: + * ithread_mutex_t * mutex (must be valid non NULL pointer to pthread_mutex_t) + * mutex must be initialized. + * + * Returns: + * 0 on success, Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutex_lock + *****************************************************************************/ +#define ithread_mutex_lock pthread_mutex_lock + + +/**************************************************************************** + * Function: ithread_mutex_unlock + * + * Description: + * Unlocks mutex. + * + * Parameters: + * ithread_mutex_t * mutex (must be valid non NULL pointer to pthread_mutex_t) + * mutex must be initialized. + * + * Returns: + * 0 on success, Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutex_unlock + *****************************************************************************/ +#define ithread_mutex_unlock pthread_mutex_unlock + + +/**************************************************************************** + * Function: ithread_mutex_destroy + * + * Description: + * Releases any resources held by the mutex. + * Mutex can no longer be used after this call. + * Mutex is only destroyed when there are no longer any threads waiting on it. + * Mutex cannot be destroyed if it is locked. + * Parameters: + * ithread_mutex_t * mutex (must be valid non NULL pointer to pthread_mutex_t) + * mutex must be initialized. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + * See man page for pthread_mutex_destroy + *****************************************************************************/ +#define ithread_mutex_destroy pthread_mutex_destroy + + +/**************************************************************************** + * Function: ithread_cond_init + * + * Description: + * Initializes condition variable. + * Must be called before use. + * Parameters: + * ithread_cond_t * cond (must be valid non NULL pointer to pthread_cond_t) + * const ithread_condattr_t * cond_attr (ignored) + * Returns: + * 0 on success, Nonzero on failure. + * See man page for pthread_cond_init + *****************************************************************************/ +#define ithread_cond_init pthread_cond_init + + + +/**************************************************************************** + * Function: ithread_cond_signal + * + * Description: + * Wakes up exactly one thread waiting on condition. + * Associated mutex MUST be locked by thread before entering this call. + * Parameters: + * ithread_cond_t * cond (must be valid non NULL pointer to + * ithread_cond_t) + * cond must be initialized + * Returns: + * 0 on success, Nonzero on failure. + * See man page for pthread_cond_signal + *****************************************************************************/ +#define ithread_cond_signal pthread_cond_signal + + +/**************************************************************************** + * Function: ithread_cond_broadcast + * + * Description: + * Wakes up all threads waiting on condition. + * Associated mutex MUST be locked by thread before entering this call. + * Parameters: + * ithread_cond_t * cond (must be valid non NULL pointer to + * ithread_cond_t) + * cond must be initialized + * Returns: + * 0 on success, Nonzero on failure. + * See man page for pthread_cond_broadcast + *****************************************************************************/ +#define ithread_cond_broadcast pthread_cond_broadcast + + +/**************************************************************************** + * Function: ithread_cond_wait + * + * Description: + * Atomically releases mutex and waits on condition. + * Associated mutex MUST be locked by thread before entering this call. + * Mutex is reacquired when call returns. + * Parameters: + * ithread_cond_t * cond (must be valid non NULL pointer to + * ithread_cond_t) + * cond must be initialized + * ithread_mutex_t *mutex (must be valid non NULL pointer to + * ithread_mutex_t) + * Mutex must be locked. + * Returns: + * 0 on success, Nonzero on failure. + * See man page for pthread_cond_wait + *****************************************************************************/ +#define ithread_cond_wait pthread_cond_wait + + + /**************************************************************************** + * Function: pthread_cond_timedwait + * + * Description: + * Atomically releases the associated mutex and waits on the condition. + * If the condition is not signaled in the specified time + * than the + * call times out and returns. + * Associated mutex MUST be locked by thread before entering + * this call. + * Mutex is reacquired when call returns. + * Parameters: + * ithread_cond_t * cond (must be valid non NULL pointer to + * ithread_cond_t) + * cond must be initialized + * ithread_mutex_t *mutex (must be valid non NULL pointer to + * ithread_mutex_t) + * Mutex must be locked. + * const struct timespec *abstime (absolute time, measured + * from Jan 1, 1970) + * Returns: + * 0 on success. ETIMEDOUT on timeout. Nonzero on failure. + * See man page for pthread_cond_timedwait + ***************************************************************************/ + +#define ithread_cond_timedwait pthread_cond_timedwait + + + /**************************************************************************** + * Function: ithread_cond_destroy + * + * Description: + * Releases any resources held by the condition variable. + * Condition variable can no longer be used after this call. + * Parameters: + * ithread_cond_t * cond (must be valid non NULL pointer to + * ithread_cond_t) + * cond must be initialized. + * Returns: + * 0 on success. Nonzero on failure. + * See man page for pthread_cond_destroy + ***************************************************************************/ +#define ithread_cond_destroy pthread_cond_destroy + + + /**************************************************************************** + * Function: ithread_create + * + * Description: + * Creates a thread with the given start routine + * and argument. + * Parameters: + * ithread_t * thread (must be valid non NULL pointer to pthread_t) + * ithread_attr_t *attr, IGNORED + * void * (start_routine) (void *arg) (start routine) + * void * arg - argument. + * Returns: + * 0 on success. Nonzero on failure. + * Returns EAGAIN if a new thread can not be created. + * Returns EINVAL if there is a problem with the arguments. + * See man page fore pthread_create + ***************************************************************************/ +#define ithread_create pthread_create + + + /**************************************************************************** + * Function: ithread_cancel + * + * Description: + * Cancels a thread. + * Parameters: + * ithread_t * thread (must be valid non NULL pointer to ithread_t) + * Returns: + * 0 on success. Nonzero on failure. + * See man page for pthread_cancel + ***************************************************************************/ +#define ithread_cancel pthread_cancel + + + /**************************************************************************** + * Function: ithread_exit + * + * Description: + * Returns a return code from a thread. + * Implicitly called when the start routine returns. + * Parameters: + * void * return_code return code to return + * See man page for pthread_exit + ***************************************************************************/ +#define ithread_exit pthread_exit + +/**************************************************************************** + * Function: ithread_get_current_thread_id + * + * Description: + * Returns the handle of the currently running thread. + * Returns: + * The handle of the currently running thread. + * See man page for pthread_self + ***************************************************************************/ +#define ithread_get_current_thread_id pthread_self + + + /**************************************************************************** + * Function: ithread_self + * + * Description: + * Returns the handle of the currently running thread. + * Returns: + * The handle of the currently running thread. + * See man page for pthread_self + ***************************************************************************/ +#define ithread_self pthread_self + + /**************************************************************************** + * Function: ithread_detach + * + * Description: + * Makes a thread's resources reclaimed immediately + * after it finishes + * execution. + * Returns: + * 0 on success, Nonzero on failure. + * See man page for pthread_detach + ***************************************************************************/ +#define ithread_detach pthread_detach + + /**************************************************************************** + * Function: ithread_join + * + * Description: + * Suspends the currently running thread until the + * specified thread + * has finished. + * Returns the return code of the thread, or ITHREAD_CANCELED + * if the thread has been canceled. + * Parameters: + * ithread_t *thread (valid non null thread identifier) + * void ** return (space for return code) + * Returns: + * 0 on success, Nonzero on failure. + * See man page for pthread_join + ***************************************************************************/ +#define ithread_join pthread_join + + + +/**************************************************************************** + * Function: isleep + * + * Description: + * Suspends the currently running thread for the specified number + * of seconds + * Always returns 0. + * Parameters: + * unsigned int seconds - number of seconds to sleep. + * Returns: + * 0 on success, Nonzero on failure. + * See man page for sleep (man 3 sleep) + *****************************************************************************/ +#define isleep sleep + +/**************************************************************************** + * Function: isleep + * + * Description: + * Suspends the currently running thread for the specified number + * of milliseconds + * Always returns 0. + * Parameters: + * unsigned int milliseconds - number of milliseconds to sleep. + * Returns: + * 0 on success, Nonzero on failure. + * See man page for sleep (man 3 sleep) + *****************************************************************************/ +#define imillisleep(x) usleep(1000*x) + + + +//NK: Added for satisfying the gcc compiler +int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind); + +#ifdef __cplusplus +} +#endif + +#endif //ITHREADH diff --git a/libupnp/threadutil/src/FreeList.c b/libupnp/threadutil/src/FreeList.c new file mode 100644 index 0000000..80c3ac9 --- /dev/null +++ b/libupnp/threadutil/src/FreeList.c @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "FreeList.h" + +#include +#include + +/**************************************************************************** + * Function: FreeListInit + * + * Description: + * Initializes Free List. Must be called first. + * And only once for FreeList. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * size_t - size of elements to store in free list + * maxFreeListSize - max size that the free list can grow to + * before returning memory to O.S. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + *****************************************************************************/ +int +FreeListInit( FreeList * free_list, + size_t elementSize, + int maxFreeListLength ) +{ + assert( free_list != NULL ); + + if( free_list == NULL ) + return EINVAL; + + free_list->element_size = elementSize; + free_list->maxFreeListLength = maxFreeListLength; + free_list->head = NULL; + free_list->freeListLength = 0; + return 0; +} + +/**************************************************************************** + * Function: FreeListAlloc + * + * Description: + * Allocates chunk of set size. + * If a free item is available in the list, returnes the stored item. + * Otherwise calls the O.S. to allocate memory. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * Returns: + * Non NULL on success. NULL on failure. + *****************************************************************************/ +void * +FreeListAlloc( FreeList * free_list ) +{ + FreeListNode *ret = NULL; + + assert( free_list != NULL ); + + if( free_list == NULL ) + return NULL; + + if( free_list->head ) { + ret = free_list->head; + free_list->head = free_list->head->next; + free_list->freeListLength--; + } else { + ret = malloc( free_list->element_size ); + } + + return ret; +} + +/**************************************************************************** + * Function: FreeListFree + * + * Description: + * Returns an item to the Free List. + * If the free list is smaller than the max size than + * adds the item to the free list. + * Otherwise returns the item to the O.S. + * Parameters: + * free_list - must be valid, non null, pointer to a free list. + * element - must be a pointer allocated by FreeListAlloc + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + *****************************************************************************/ +int +FreeListFree( FreeList * free_list, + void *element ) +{ + + FreeListNode *temp = NULL; + + assert( free_list != NULL ); + + if( free_list == NULL ) + return EINVAL; + + if( ( element != NULL ) && + ( ( free_list->freeListLength + 1 ) < + free_list->maxFreeListLength ) ) { + free_list->freeListLength++; + temp = ( FreeListNode * ) element; + temp->next = free_list->head; + free_list->head = temp; + + } else { + + free( element ); + } + + return 0; +} + +/**************************************************************************** + * Function: FreeListDestroy + * + * Description: + * Releases the resources stored with the free list. + * Parameters: + * free_list - must be valid, non null, pointer to a linked list. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + *****************************************************************************/ +int +FreeListDestroy( FreeList * free_list ) +{ + FreeListNode *temp = NULL; + int i = 0; + + assert( free_list != NULL ); + + if( free_list == NULL ) + return EINVAL; + + while( free_list->head ) { + i++; + temp = free_list->head->next; + free( free_list->head ); + free_list->head = temp; + } + + free_list->freeListLength = 0; + + return 0; +} diff --git a/libupnp/threadutil/src/LinkedList.c b/libupnp/threadutil/src/LinkedList.c new file mode 100644 index 0000000..c1b2950 --- /dev/null +++ b/libupnp/threadutil/src/LinkedList.c @@ -0,0 +1,529 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "LinkedList.h" +#include +#include + +static int +freeListNode( ListNode * node, + LinkedList * list ) +{ + assert( list != NULL ); + + return FreeListFree( &list->freeNodeList, node ); +} + +/**************************************************************************** + * Function: CreateListNode + * + * Description: + * Creates a list node. Dynamically. + * + * Parameters: + * void * item - the item to store + * Returns: + * The new node, NULL on failure. + *****************************************************************************/ +static ListNode * +CreateListNode( void *item, + LinkedList * list ) +{ + + ListNode *temp = NULL; + + assert( list != NULL ); + + temp = ( ListNode * ) FreeListAlloc( &list->freeNodeList ); + if( temp ) { + temp->prev = NULL; + temp->next = NULL; + temp->item = item; + } + return temp; +} + +/**************************************************************************** + * Function: ListInit + * + * Description: + * Initializes LinkedList. Must be called first. + * And only once for List. + * Parameters: + * list - must be valid, non null, pointer to a linked list. + * cmp_func - function used to compare items. (May be NULL) + * free_func - function used to free items. (May be NULL) + * Returns: + * 0 on success, EOUTOFMEM on failure. + *****************************************************************************/ +int +ListInit( LinkedList * list, + cmp_routine cmp_func, + free_function free_func ) +{ + + int retCode = 0; + + assert( list != NULL ); + + if( list == NULL ) + return EINVAL; + + list->size = 0; + list->cmp_func = cmp_func; + list->free_func = free_func; + + retCode = + FreeListInit( &list->freeNodeList, sizeof( ListNode ), + FREELISTSIZE ); + + assert( retCode == 0 ); + + list->head.item = NULL; + list->head.next = &list->tail; + list->head.prev = NULL; + + list->tail.item = NULL; + list->tail.prev = &list->head; + list->tail.next = NULL; + + return 0; +} + +/**************************************************************************** + * Function: ListAddHead + * + * Description: + * Adds a node to the head of the list. + * Node gets immediately after list.head. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * void * item - item to be added + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListAddHead( LinkedList * list, + void *item ) +{ + assert( list != NULL ); + + if( list == NULL ) + return NULL; + + return ListAddAfter( list, item, &list->head ); +} + +/**************************************************************************** + * Function: ListAddTail + * + * Description: + * Adds a node to the tail of the list. + * Node gets added immediately before list.tail. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * void * item - item to be added + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListAddTail( LinkedList * list, + void *item ) +{ + assert( list != NULL ); + + if( list == NULL ) + return NULL; + + return ListAddBefore( list, item, &list->tail ); +} + +/**************************************************************************** + * Function: ListAddAfter + * + * Description: + * Adds a node after the specified node. + * Node gets added immediately after bnode. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * void * item - item to be added + * ListNode * bnode - node to add after + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListAddAfter( LinkedList * list, + void *item, + ListNode * bnode ) +{ + ListNode *newNode = NULL; + + assert( list != NULL ); + + if( ( list == NULL ) || ( bnode == NULL ) ) + return NULL; + + newNode = CreateListNode( item, list ); + if( newNode ) { + ListNode *temp = bnode->next; + + bnode->next = newNode; + newNode->prev = bnode; + newNode->next = temp; + temp->prev = newNode; + list->size++; + return newNode; + } + return NULL; +} + +/**************************************************************************** + * Function: ListAddBefore + * + * Description: + * Adds a node before the specified node. + * Node gets added immediately before anode. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * ListNode * anode - node to add the in front of. + * void * item - item to be added + * Returns: + * The pointer to the ListNode on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListAddBefore( LinkedList * list, + void *item, + ListNode * anode ) +{ + ListNode *newNode = NULL; + + assert( list != NULL ); + + if( ( list == NULL ) || ( anode == NULL ) ) + return NULL; + + newNode = CreateListNode( item, list ); + + if( newNode ) { + ListNode *temp = anode->prev; + + anode->prev = newNode; + newNode->next = anode; + newNode->prev = temp; + temp->next = newNode; + list->size++; + return newNode; + } + return NULL; +} + +/**************************************************************************** + * Function: ListDelNode + * + * Description: + * Removes a node from the list + * The memory for the node is freed but the + * the memory for the items are not. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * ListNode *dnode - done to delete. + * Returns: + * The pointer to the item stored in node on success, NULL on failure. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +void * +ListDelNode( LinkedList * list, + ListNode * dnode, + int freeItem ) +{ + void *temp; + + assert( list != NULL ); + assert( dnode != &list->head ); + assert( dnode != &list->tail ); + + if( ( list == NULL ) || + ( dnode == &list->head ) || + ( dnode == &list->tail ) || ( dnode == NULL ) ) { + return NULL; + } + + temp = dnode->item; + dnode->prev->next = dnode->next; + dnode->next->prev = dnode->prev; + + freeListNode( dnode, list ); + list->size--; + + if( freeItem && list->free_func ) { + list->free_func( temp ); + temp = NULL; + } + + return temp; +} + +/**************************************************************************** + * Function: ListDestroy + * + * Description: + * Removes all memory associated with list nodes. + * Does not free LinkedList *list. + * Items stored in the list are not freed, only nodes are. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * Returns: + * 0 on success. Nonzero on failure. + * Always returns 0. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +int +ListDestroy( LinkedList * list, + int freeItem ) +{ + ListNode *dnode = NULL; + ListNode *temp = NULL; + + if( list == NULL ) + return EINVAL; + + for( dnode = list->head.next; dnode != &list->tail; ) { + temp = dnode->next; + ListDelNode( list, dnode, freeItem ); + dnode = temp; + } + + list->size = 0; + FreeListDestroy( &list->freeNodeList ); + return 0; +} + +/**************************************************************************** + * Function: ListHead + * + * Description: + * Returns the head of the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The head of the list. NULL if list is empty. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListHead( LinkedList * list ) +{ + assert( list != NULL ); + + if( list == NULL ) + return NULL; + + if( list->size == 0 ) + return NULL; + else + return list->head.next; +} + +/**************************************************************************** + * Function: ListTail + * + * Description: + * Returns the tail of the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The tail of the list. NULL if list is empty. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListTail( LinkedList * list ) +{ + assert( list != NULL ); + + if( list == NULL ) + return NULL; + + if( list->size == 0 ) + return NULL; + else + return list->tail.prev; +} + +/**************************************************************************** + * Function: ListNext + * + * Description: + * Returns the next item in the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The next item in the list. NULL if there are no more items in list. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListNext( LinkedList * list, + ListNode * node ) +{ + assert( list != NULL ); + assert( node != NULL ); + + if( ( list == NULL ) || ( node == NULL ) ) + return NULL; + + if( node->next == &list->tail ) + return NULL; + else + return node->next; +} + +/**************************************************************************** + * Function: ListPrev + * + * Description: + * Returns the previous item in the list. + * + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * + * Returns: + * The previous item in the list. NULL if there are no more items in list. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListPrev( LinkedList * list, + ListNode * node ) +{ + assert( list != NULL ); + assert( node != NULL ); + + if( ( list == NULL ) || ( node == NULL ) ) + return NULL; + + if( node->prev == &list->head ) + return NULL; + else + return node->prev; +} + +/**************************************************************************** + * Function: ListFind + * + * Description: + * Finds the specified item in the list. + * Uses the compare function specified in ListInit. If compare function + * is NULL then compares items as pointers. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + * ListNode *start - the node to start from, NULL if to start from + * beginning. + * void * item - the item to search for. + * Returns: + * The node containing the item. NULL if no node contains the item. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +ListNode * +ListFind( LinkedList * list, + ListNode * start, + void *item ) +{ + + ListNode *finger = NULL; + + if( list == NULL ) + return NULL; + + if( start == NULL ) + start = &list->head; + + assert( start ); + + finger = start->next; + + assert( finger ); + + while( finger != &list->tail ) { + if( list->cmp_func ) { + if( list->cmp_func( item, finger->item ) ) + return finger; + } else { + if( item == finger->item ) + return finger; + } + finger = finger->next; + } + + return NULL; + +} + +/**************************************************************************** + * Function: ListSize + * + * Description: + * Returns the size of the list. + * Parameters: + * LinkedList *list - must be valid, non null, pointer to a linked list. + + * Returns: + * The number of items in the list. + * Precondition: + * The list has been initialized. + *****************************************************************************/ +int +ListSize( LinkedList * list ) +{ + assert( list != NULL ); + + if( list == NULL ) + return EINVAL; + + return list->size; +} diff --git a/libupnp/threadutil/src/ThreadPool.c b/libupnp/threadutil/src/ThreadPool.c new file mode 100644 index 0000000..f4d0211 --- /dev/null +++ b/libupnp/threadutil/src/ThreadPool.c @@ -0,0 +1,1531 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "ThreadPool.h" +#include "FreeList.h" +#include +#include + +#ifdef STATS +#include +#endif + +/**************************************************************************** + * Function: CmpThreadPoolJob + * + * Description: + * Compares thread pool jobs. + * Parameters: + * void * - job A + * void * - job B + *****************************************************************************/ +static int +CmpThreadPoolJob( void *jobA, + void *jobB ) +{ + ThreadPoolJob *a = ( ThreadPoolJob * ) jobA; + ThreadPoolJob *b = ( ThreadPoolJob * ) jobB; + + assert( jobA != NULL ); + assert( jobB != NULL ); + return ( a->jobId == b->jobId ); +} + +/**************************************************************************** + * Function: FreeThreadPoolJob + * + * Description: + * Deallocates a dynamically allocated ThreadPoolJob. + * Parameters: + * ThreadPoolJob *tpj - must be allocated with CreateThreadPoolJob + *****************************************************************************/ +static void +FreeThreadPoolJob( ThreadPool * tp, + ThreadPoolJob * tpj ) +{ + assert( tp != NULL ); + + FreeListFree( &tp->jobFreeList, tpj ); +} + +/**************************************************************************** + * Function: SetPolicyType + * + * Description: + * Sets the scheduling policy of the current process. + * Internal only. + * Parameters: + * PolocyType in + * Returns: + * 0 on success, nonzero on failure + * Returns result of GetLastError() on failure. + * + *****************************************************************************/ +static int +SetPolicyType( PolicyType in ) +{ + struct sched_param current; + + sched_getparam( 0, ¤t ); + current.sched_priority = DEFAULT_SCHED_PARAM; + return sched_setscheduler( 0, in, ¤t ); +} + +/**************************************************************************** + * Function: SetPriority + * + * Description: + * Sets the priority of the currently running thread. + * Internal only. + * Parameters: + * ThreadPriority priority + * Returns: + * 0 on success, nonzero on failure + * EINVAL invalid priority + * Returns result of GerLastError on failure. + * + *****************************************************************************/ +static int +SetPriority( ThreadPriority priority ) +{ + + int currentPolicy; + int minPriority = 0; + int maxPriority = 0; + int actPriority = 0; + int midPriority = 0; + struct sched_param newPriority; + + pthread_getschedparam( ithread_self( ), ¤tPolicy, + &newPriority ); + minPriority = sched_get_priority_min( currentPolicy ); + maxPriority = sched_get_priority_max( currentPolicy ); + midPriority = ( maxPriority - minPriority ) / 2; + switch ( priority ) { + + case LOW_PRIORITY: + actPriority = minPriority; + break; + case MED_PRIORITY: + actPriority = midPriority; + break; + case HIGH_PRIORITY: + actPriority = maxPriority; + break; + default: + return EINVAL; + }; + + newPriority.sched_priority = actPriority; + + return pthread_setschedparam( ithread_self( ), currentPolicy, + &newPriority ); + +} + +/**************************************************************************** + * Function: DiffMillis + * + * Description: + * Returns the difference in milliseconds between two + * timeb structures. + * Internal Only. + * Parameters: + * struct timeb *time1, + * struct timeb *time2, + * Returns: + * the difference in milliseconds, time1-time2. + *****************************************************************************/ +static double +DiffMillis( struct timeb *time1, + struct timeb *time2 ) +{ + double temp = 0; + + assert( time1 != NULL ); + assert( time2 != NULL ); + + temp = ( ( double )( time1->time ) - time2->time ); + temp = temp * 1000; + temp += ( time1->millitm - time2->millitm ); + return temp; +} + +/**************************************************************************** + * Function: BumpPriority + * + * Description: + * Determines whether any jobs + * need to be bumped to a higher priority Q and bumps them. + * + * tp->mutex must be locked. + * Internal Only. + * Parameters: + * ThreadPool *tp + *****************************************************************************/ +static void +BumpPriority( ThreadPool * tp ) +{ + int done = 0; + struct timeb now; + double diffTime = 0; + ThreadPoolJob *tempJob = NULL; + + assert( tp != NULL ); + + ftime( &now ); + + while( !done ) { + if( tp->medJobQ.size ) { + tempJob = ( ThreadPoolJob * ) tp->medJobQ.head.next->item; + diffTime = DiffMillis( &now, &tempJob->requestTime ); + + if( diffTime >= ( tp->attr.starvationTime ) ) { + //If job has waited longer than the + //starvation time + //bump priority (add to higher priority Q) + + STATSONLY( tp->stats.totalJobsMQ++; + ); + STATSONLY( tp->stats.totalTimeMQ += diffTime; + ); + + ListDelNode( &tp->medJobQ, tp->medJobQ.head.next, 0 ); + ListAddTail( &tp->highJobQ, tempJob ); + continue; + } + } + + if( tp->lowJobQ.size ) { + tempJob = ( ThreadPoolJob * ) tp->lowJobQ.head.next->item; + + diffTime = DiffMillis( &now, &tempJob->requestTime ); + + if( diffTime >= ( tp->attr.maxIdleTime ) ) { + //If job has waited longer than the + //starvation time + //bump priority (add to higher priority Q) + + STATSONLY( tp->stats.totalJobsLQ++; + ); + STATSONLY( tp->stats.totalTimeLQ += diffTime; + ); + + ListDelNode( &tp->lowJobQ, tp->lowJobQ.head.next, 0 ); + ListAddTail( &tp->medJobQ, tempJob ); + continue; + } + } + + done = 1; + } +} + +/**************************************************************************** + * Function: SetRelTimeout + * + * Description: + * Sets the fields of the + * passed in timespec to be relMillis milliseconds in the future. + * Internal Only. + * Parameters: + * struct timespec *time + * int relMillis - milliseconds in the future + *****************************************************************************/ +static void +SetRelTimeout( struct timespec *time, + int relMillis ) +{ + struct timeb now; + int sec = relMillis / 1000; + int milliSeconds = relMillis % 1000; + + assert( time != NULL ); + + ftime( &now ); + + time->tv_sec = now.time + sec; + time->tv_nsec = ( now.millitm + milliSeconds ) * 1000000; +} + +/**************************************************************************** + * Function: StatsInit + * + * Description: + * Initializes the statistics structure. + * Internal Only. + * Parameters: + * ThreadPoolStats *stats must be valid non null stats structure + *****************************************************************************/ +STATSONLY( static void StatsInit( ThreadPoolStats * stats ) { + assert( stats != NULL ); stats->totalIdleTime = 0; stats->totalJobsHQ = 0; stats->totalJobsLQ = 0; stats->totalJobsMQ = 0; stats->totalTimeHQ = 0; stats->totalTimeMQ = 0; stats->totalTimeLQ = 0; stats->totalWorkTime = 0; stats->totalIdleTime = 0; stats->avgWaitHQ = 0; //average wait in HQ + stats->avgWaitMQ = 0; //average wait in MQ + stats->avgWaitLQ = 0; + stats->workerThreads = 0; + stats->idleThreads = 0; + stats->persistentThreads = 0; + stats->maxThreads = 0; stats->totalThreads = 0;} + ) + +/**************************************************************************** + * Function: CalcWaitTime + * + * Description: + * Calculates the time the job has been waiting at the specified + * priority. Adds to the totalTime and totalJobs kept in the + * thread pool statistics structure. + * Internal Only. + * + * Parameters: + * ThreadPool *tp + * ThreadPriority p + * ThreadPoolJob *job + *****************************************************************************/ +STATSONLY( static void CalcWaitTime( ThreadPool * tp, + ThreadPriority p, + ThreadPoolJob * job ) { + struct timeb now; + double diff; + assert( tp != NULL ); + assert( job != NULL ); + ftime( &now ); + diff = DiffMillis( &now, &job->requestTime ); switch ( p ) { +case HIGH_PRIORITY: +tp->stats.totalJobsHQ++; tp->stats.totalTimeHQ += diff; break; case MED_PRIORITY: +tp->stats.totalJobsMQ++; tp->stats.totalTimeMQ += diff; break; case LOW_PRIORITY: +tp->stats.totalJobsLQ++; tp->stats.totalTimeLQ += diff; break; default: + assert( 0 );} + } + + ) + +/**************************************************************************** + * Function: SetSeed + * + * Description: + * Sets seed for random number generator. + * Each thread sets the seed random number generator. + * Internal Only. + * Parameters: + * + *****************************************************************************/ + static void SetSeed( ) { + struct timeb t; + + ftime( &t ); + srand( ( unsigned int )t.millitm + ithread_get_current_thread_id( ) ); + } + +/**************************************************************************** + * Function: WorkerThread + * + * Description: + * Implements a thread pool worker. + * Worker waits for a job to become available. + * Worker picks up persistent jobs first, high priority, med priority, + * then low priority. + * If worker remains idle for more than specified max, the worker + * is released. + * Internal Only. + * Parameters: + * void * arg -> is cast to ThreadPool * + *****************************************************************************/ + static void *WorkerThread( void *arg ) { + + STATSONLY( time_t start = 0; + ) + + ThreadPoolJob *job = NULL; + ListNode *head = NULL; + + struct timespec timeout; + int retCode = 0; + int persistent = -1; + ThreadPool *tp = ( ThreadPool * ) arg; + + assert( tp != NULL ); + + //Increment total thread count + ithread_mutex_lock( &tp->mutex ); + tp->totalThreads++; + ithread_cond_broadcast( &tp->start_and_shutdown ); + ithread_mutex_unlock( &tp->mutex ); + + SetSeed( ); + + STATSONLY( time( &start ); + ); + + while( 1 ) { + + ithread_mutex_lock( &tp->mutex ); + + if( job ) { + + FreeThreadPoolJob( tp, job ); + job = NULL; + + } + + retCode = 0; + + STATSONLY( tp->stats.idleThreads++; + ); + STATSONLY( tp->stats.totalWorkTime += ( time( NULL ) - start ); + ); //work time + STATSONLY( time( &start ); + ); //idle time + + if( persistent == 1 ) { + //Persistent thread + //becomes a regular thread + tp->persistentThreads--; + } + + STATSONLY( if( persistent == 0 ) + tp->stats.workerThreads--; ); + + //Check for a job or shutdown + while( ( tp->lowJobQ.size == 0 ) + && ( tp->medJobQ.size == 0 ) + && ( tp->highJobQ.size == 0 ) + && ( !tp->persistentJob ) + && ( !tp->shutdown ) ) { + + //If wait timed out + //and we currently have more than the + //min threads, or if we have more than the max threads + // (only possible if the attributes have been reset) + //let this thread die. + + if( ( ( retCode == ETIMEDOUT ) + && ( ( tp->totalThreads ) > tp->attr.minThreads ) ) + || ( ( tp->attr.maxThreads != -1 ) + && ( ( tp->totalThreads ) > + tp->attr.maxThreads ) ) ) { + + STATSONLY( tp->stats.idleThreads-- ); + + tp->totalThreads--; + ithread_cond_broadcast( &tp->start_and_shutdown ); + ithread_mutex_unlock( &tp->mutex ); + + return NULL; + } + + SetRelTimeout( &timeout, tp->attr.maxIdleTime ); + + //wait for a job up to the specified max time + retCode = ithread_cond_timedwait( &tp->condition, + &tp->mutex, &timeout ); + + } + + STATSONLY( tp->stats.idleThreads--; + ); + STATSONLY( tp->stats.totalIdleTime += ( time( NULL ) - start ); + ); //idle time + STATSONLY( time( &start ); + ); //work time + + //bump priority of starved jobs + BumpPriority( tp ); + + //if shutdown then stop + if( tp->shutdown ) { + tp->totalThreads--; + + ithread_cond_broadcast( &tp->start_and_shutdown ); + + ithread_mutex_unlock( &tp->mutex ); + + return NULL; + } else { + + //Pick up persistent job if available + if( tp->persistentJob ) { + + job = tp->persistentJob; + tp->persistentJob = NULL; + tp->persistentThreads++; + persistent = 1; + ithread_cond_broadcast( &tp->start_and_shutdown ); + + } else { + STATSONLY( tp->stats.workerThreads++ ); + persistent = 0; + + //Pick the highest priority job + if( tp->highJobQ.size > 0 ) { + head = ListHead( &tp->highJobQ ); + job = ( ThreadPoolJob * ) head->item; + STATSONLY( CalcWaitTime + ( tp, HIGH_PRIORITY, job ) ); + ListDelNode( &tp->highJobQ, head, 0 ); + + } else if( tp->medJobQ.size > 0 ) { + head = ListHead( &tp->medJobQ ); + job = ( ThreadPoolJob * ) head->item; + STATSONLY( CalcWaitTime( tp, MED_PRIORITY, job ) ); + ListDelNode( &tp->medJobQ, head, 0 ); + + } else if( tp->lowJobQ.size > 0 ) { + head = ListHead( &tp->lowJobQ ); + job = ( ThreadPoolJob * ) head->item; + STATSONLY( CalcWaitTime( tp, LOW_PRIORITY, job ) ); + ListDelNode( &tp->lowJobQ, head, 0 ); + + } else { + + // Should never get here + assert( 0 ); + STATSONLY( tp->stats.workerThreads-- ); + tp->totalThreads--; + ithread_cond_broadcast( &tp->start_and_shutdown ); + ithread_mutex_unlock( &tp->mutex ); + + return NULL; + } + + } + } + + ithread_mutex_unlock( &tp->mutex ); + + if( SetPriority( job->priority ) != 0 ) { + // In the future can log + // info + } else { + // In the future can log + // info + } + + //run the job + + job->func( job->arg ); + + //return to Normal + SetPriority( DEFAULT_PRIORITY ); + + } + } + +/**************************************************************************** + * Function: CreateThreadPoolJob + * + * Description: + * Creates a Thread Pool Job. (Dynamically allocated) + * Internal to thread pool. + * Parameters: + * ThreadPoolJob * job - job is copied + * id - id of job + * + * Returns: + * ThreadPoolJob * on success, NULL on failure. + *****************************************************************************/ + static ThreadPoolJob *CreateThreadPoolJob( ThreadPoolJob * job, + int id, + ThreadPool * tp ) { + + ThreadPoolJob *newJob = NULL; + + assert( job != NULL ); + assert( tp != NULL ); + + newJob = ( ThreadPoolJob * ) FreeListAlloc( &tp->jobFreeList ); + + if( newJob ) { + ( *newJob ) = ( *job ); + newJob->jobId = id; + ftime( &newJob->requestTime ); + } + return newJob; + } + +/**************************************************************************** + * Function: CreateWorker + * + * Description: + * Creates a worker thread, if the thread pool + * does not already have max threads. + * Internal to thread pool. + * Parameters: + * ThreadPool *tp + * + * Returns: + * 0 on success, <0 on failure + * EMAXTHREADS if already max threads reached + * EAGAIN if system can not create thread + * + *****************************************************************************/ + static int CreateWorker( ThreadPool * tp ) { + ithread_t temp; + int rc = 0; + int currentThreads = tp->totalThreads + 1; + + assert( tp != NULL ); + + if( ( tp->attr.maxThreads != INFINITE_THREADS ) + && ( currentThreads > tp->attr.maxThreads ) ) { + return EMAXTHREADS; + } + + rc = ithread_create( &temp, NULL, WorkerThread, tp ); + + if( rc == 0 ) { + + rc = ithread_detach( temp ); + + while( tp->totalThreads < currentThreads ) { + + ithread_cond_wait( &tp->start_and_shutdown, &tp->mutex ); + + } + + } + + STATSONLY( if( tp->stats.maxThreads < tp->totalThreads ) { + tp->stats.maxThreads = tp->totalThreads;} + ) + + return rc; + } + +/**************************************************************************** + * Function: AddWorker + * + * Description: + * Determines whether or not a thread should be added + * based on the jobsPerThread ratio. + * Adds a thread if appropriate. + * Internal to Thread Pool. + * Parameters: + * ThreadPool* tp + * + *****************************************************************************/ + static void AddWorker( ThreadPool * tp ) { + int jobs = 0; + int threads = 0; + + assert( tp != NULL ); + + jobs = tp->highJobQ.size + tp->lowJobQ.size + tp->medJobQ.size; + + threads = tp->totalThreads - tp->persistentThreads; + + while( ( threads == 0 ) + || ( ( jobs / threads ) > tp->attr.jobsPerThread ) ) { + + if( CreateWorker( tp ) != 0 ) + return; + threads++; + } + + } + +/**************************************************************************** + * Function: ThreadPoolInit + * + * Description: + * Initializes and starts ThreadPool. Must be called first. + * And only once for ThreadPool. + * Parameters: + * tp - must be valid, non null, pointer to ThreadPool. + * minWorkerThreads - minimum number of worker threads + * thread pool will never have less than this + * number of threads. + * maxWorkerThreads - maximum number of worker threads + * thread pool will never have more than this + * number of threads. + * maxIdleTime - maximum time that a worker thread will spend + * idle. If a worker is idle longer than this + * time and there are more than the min + * number of workers running, than the + * worker thread exits. + * jobsPerThread - ratio of jobs to thread to try and maintain + * if a job is scheduled and the number of jobs per + * thread is greater than this number,and + * if less than the maximum number of + * workers are running then a new thread is + * started to help out with efficiency. + * schedPolicy - scheduling policy to try and set (OS dependent) + * Returns: + * 0 on success, nonzero on failure. + * EAGAIN if not enough system resources to create minimum threads. + * INVALID_POLICY if schedPolicy can't be set + * EMAXTHREADS if minimum threads is greater than maximum threads + *****************************************************************************/ + int ThreadPoolInit( ThreadPool * tp, + ThreadPoolAttr * attr ) { + int retCode = 0; + int i = 0; + + assert( tp != NULL ); + + if( tp == NULL ) { + return EINVAL; + } + + retCode += ithread_mutex_init( &tp->mutex, NULL ); + assert( retCode == 0 ); + + retCode += ithread_mutex_lock( &tp->mutex ); + assert( retCode == 0 ); + + retCode += ithread_cond_init( &tp->condition, NULL ); + assert( retCode == 0 ); + + retCode += ithread_cond_init( &tp->start_and_shutdown, NULL ); + assert( retCode == 0 ); + + if( retCode != 0 ) { + return EAGAIN; + } + + if( attr ) { + tp->attr = ( *attr ); + } else { + TPAttrInit( &tp->attr ); + } + + if( SetPolicyType( tp->attr.schedPolicy ) != 0 ) { + ithread_mutex_unlock( &tp->mutex ); + ithread_mutex_destroy( &tp->mutex ); + ithread_cond_destroy( &tp->condition ); + ithread_cond_destroy( &tp->start_and_shutdown ); + return INVALID_POLICY; + } + + retCode += FreeListInit( &tp->jobFreeList, sizeof( ThreadPoolJob ), + JOBFREELISTSIZE ); + assert( retCode == 0 ); + + STATSONLY( StatsInit( &tp->stats ) ); + + retCode += ListInit( &tp->highJobQ, CmpThreadPoolJob, NULL ); + assert( retCode == 0 ); + + retCode += ListInit( &tp->medJobQ, CmpThreadPoolJob, NULL ); + assert( retCode == 0 ); + + retCode += ListInit( &tp->lowJobQ, CmpThreadPoolJob, NULL ); + assert( retCode == 0 ); + + if( retCode != 0 ) { + retCode = EAGAIN; + } else { + + tp->persistentJob = NULL; + tp->lastJobId = 0; + tp->shutdown = 0; + tp->totalThreads = 0; + tp->persistentThreads = 0; + + for( i = 0; i < tp->attr.minThreads; i++ ) { + + if( ( retCode = CreateWorker( tp ) ) != 0 ) { + break; + } + } + } + + ithread_mutex_unlock( &tp->mutex ); + + if( retCode != 0 ) { + //clean up if the min threads could + //not be created + ThreadPoolShutdown( tp ); + } + + return retCode; + } + +/**************************************************************************** + * Function: ThreadPoolAddPersistent + * + * Description: + * Adds a long term job to the thread pool. + * Job will be run as soon as possible. + * Call will block until job is scheduled. + * Parameters: + * tp - valid thread pool pointer + * job-> valid ThreadPoolJob pointer with following fields + * func - ThreadFunction to run + * arg - argument to function. + * priority - priority of job. + * free_function - function to use when freeing argument + * Returns: + * 0 on success, nonzero on failure + * EOUTOFMEM not enough memory to add job. + * EMAXTHREADS not enough threads to add persistent job. + *****************************************************************************/ + int ThreadPoolAddPersistent( ThreadPool * tp, + ThreadPoolJob * job, + int *jobId ) { + int tempId = -1; + + ThreadPoolJob *temp = NULL; + + assert( tp != NULL ); + assert( job != NULL ); + + if( ( tp == NULL ) || ( job == NULL ) ) { + return EINVAL; + } + + if( jobId == NULL ) + jobId = &tempId; + + ( *jobId ) = INVALID_JOB_ID; + + ithread_mutex_lock( &tp->mutex ); + + assert( ( job->priority == LOW_PRIORITY ) + || ( job->priority == MED_PRIORITY ) + || ( job->priority == HIGH_PRIORITY ) ); + + //Create A worker if less than max threads running + if( tp->totalThreads < tp->attr.maxThreads ) { + CreateWorker( tp ); + } else { + //if there is more than one worker thread + //available then schedule job, otherwise fail + if( ( tp->totalThreads - tp->persistentThreads ) - 1 == 0 ) { + ithread_mutex_unlock( &tp->mutex ); + return EMAXTHREADS; + } + } + + temp = CreateThreadPoolJob( job, tp->lastJobId, tp ); + + if( temp == NULL ) { + ithread_mutex_unlock( &tp->mutex ); + return EOUTOFMEM; + } + + tp->persistentJob = temp; + + //Notify a waiting thread + + ithread_cond_signal( &tp->condition ); + + //wait until long job has been picked up + while( tp->persistentJob != NULL ) { + ithread_cond_wait( &tp->start_and_shutdown, &tp->mutex ); + } + + ( *jobId ) = tp->lastJobId++; + ithread_mutex_unlock( &tp->mutex ); + return 0; + } + +/**************************************************************************** + * Function: ThreadPoolAdd + * + * Description: + * Adds a job to the thread pool. + * Job will be run as soon as possible. + * Parameters: + * tp - valid thread pool pointer + * func - ThreadFunction to run + * arg - argument to function. + * priority - priority of job. + * jobId - id of job + * duration - whether or not this is a persistent thread + * free_function - function to use when freeing argument + * Returns: + * 0 on success, nonzero on failure + * EOUTOFMEM if not enough memory to add job. + *****************************************************************************/ + int ThreadPoolAdd( ThreadPool * tp, + ThreadPoolJob * job, + int *jobId ) { + int rc = EOUTOFMEM; + + int tempId = -1; + + ThreadPoolJob *temp = NULL; + + assert( tp != NULL ); + assert( job != NULL ); + + if( ( tp == NULL ) || ( job == NULL ) ) { + return EINVAL; + } + + ithread_mutex_lock( &tp->mutex ); + + assert( ( job->priority == LOW_PRIORITY ) + || ( job->priority == MED_PRIORITY ) + || ( job->priority == HIGH_PRIORITY ) ); + + if( jobId == NULL ) + jobId = &tempId; + + ( *jobId ) = INVALID_JOB_ID; + + temp = CreateThreadPoolJob( job, tp->lastJobId, tp ); + + if( temp == NULL ) { + ithread_mutex_unlock( &tp->mutex ); + return rc; + } + + if( job->priority == HIGH_PRIORITY ) { + if( ListAddTail( &tp->highJobQ, temp ) ) + rc = 0; + } else if( job->priority == MED_PRIORITY ) { + if( ListAddTail( &tp->medJobQ, temp ) ) + rc = 0; + } else { + if( ListAddTail( &tp->lowJobQ, temp ) ) + rc = 0; + } + + //AddWorker if appropriate + AddWorker( tp ); + + //Notify a waiting thread + if( rc == 0 ) { + ithread_cond_signal( &tp->condition ); + + } else { + FreeThreadPoolJob( tp, temp ); + } + + ( *jobId ) = tp->lastJobId++; + + ithread_mutex_unlock( &tp->mutex ); + return rc; + } + +/**************************************************************************** + * Function: ThreadPoolRemove + * + * Description: + * Removes a job from the thread pool. + * Can only remove jobs which are not + * currently running. + * Parameters: + * tp - valid thread pool pointer + * jobId - id of job + * ThreadPoolJob *out - space for removed job. + * Can be null if not needed. + * + * Returns: + * 0 on success. INVALID_JOB_ID on failure. + *****************************************************************************/ + int ThreadPoolRemove( ThreadPool * tp, + int jobId, + ThreadPoolJob * out ) { + ThreadPoolJob *temp = NULL; + int ret = INVALID_JOB_ID; + ListNode *tempNode = NULL; + ThreadPoolJob dummy; + + assert( tp != NULL ); + + if( tp == NULL ) { + return EINVAL; + } + + if( out == NULL ) { + out = &dummy; + } + + dummy.jobId = jobId; + + ithread_mutex_lock( &tp->mutex ); + + tempNode = ListFind( &tp->highJobQ, NULL, &dummy ); + + if( tempNode ) { + temp = ( ThreadPoolJob * ) tempNode->item; + ( *out ) = ( *temp ); + ListDelNode( &tp->highJobQ, tempNode, 0 ); + FreeThreadPoolJob( tp, temp ); + ithread_mutex_unlock( &tp->mutex ); + return 0; + } + + tempNode = ListFind( &tp->medJobQ, NULL, &dummy ); + + if( tempNode ) { + temp = ( ThreadPoolJob * ) tempNode->item; + ( *out ) = ( *temp ); + ListDelNode( &tp->medJobQ, tempNode, 0 ); + FreeThreadPoolJob( tp, temp ); + ithread_mutex_unlock( &tp->mutex ); + return 0; + } + + tempNode = ListFind( &tp->lowJobQ, NULL, &dummy ); + + if( tempNode ) { + temp = ( ThreadPoolJob * ) tempNode->item; + ( *out ) = ( *temp ); + ListDelNode( &tp->lowJobQ, tempNode, 0 ); + FreeThreadPoolJob( tp, temp ); + ithread_mutex_unlock( &tp->mutex ); + return 0; + } + + if( ( tp->persistentJob ) + && ( tp->persistentJob->jobId == jobId ) ) { + ( *out ) = ( *tp->persistentJob ); + FreeThreadPoolJob( tp, tp->persistentJob ); + tp->persistentJob = NULL; + ithread_mutex_unlock( &tp->mutex ); + return 0; + } + + ithread_mutex_unlock( &tp->mutex ); + return ret; + } + +/**************************************************************************** + * Function: ThreadPoolGetAttr + * + * Description: + * Gets the current set of attributes + * associated with the thread pool. + * Parameters: + * tp - valid thread pool pointer + * out - non null pointer to store attributes + * Returns: + * 0 on success, nonzero on failure + * Always returns 0. + *****************************************************************************/ + int ThreadPoolGetAttr( ThreadPool * tp, + ThreadPoolAttr * out ) { + assert( tp != NULL ); + + assert( out != NULL ); + + if( ( tp == NULL ) || ( out == NULL ) ) { + return EINVAL; + } + + if( !tp->shutdown ) { + ithread_mutex_lock( &tp->mutex ); + } + + ( *out ) = tp->attr; + + if( !tp->shutdown ) { + ithread_mutex_unlock( &tp->mutex ); + } + + return 0; + } + +/**************************************************************************** + * Function: ThreadPoolSetAttr + * + * Description: + * Sets the attributes for the thread pool. + * Only affects future calculations. + * Parameters: + * tp - valid thread pool pointer + * attr - pointer to attributes, null sets attributes to default. + * Returns: + * 0 on success, nonzero on failure + * Returns INVALID_POLICY if policy can not be set. + *****************************************************************************/ + int ThreadPoolSetAttr( ThreadPool * tp, + ThreadPoolAttr * attr ) { + int retCode = 0; + ThreadPoolAttr temp; + int i = 0; + + assert( tp != NULL ); + + if( tp == NULL ) { + return EINVAL; + } + ithread_mutex_lock( &tp->mutex ); + + if( attr != NULL ) { + temp = ( *attr ); + } else { + TPAttrInit( &temp ); + } + + if( SetPolicyType( temp.schedPolicy ) != 0 ) { + ithread_mutex_unlock( &tp->mutex ); + return INVALID_POLICY; + } + + tp->attr = ( temp ); + + if( tp->totalThreads < tp->attr.minThreads ) //add threads + { + for( i = tp->totalThreads; i < tp->attr.minThreads; i++ ) { + + if( ( retCode = CreateWorker( tp ) ) != 0 ) { + break; + } + } + } + + ithread_cond_signal( &tp->condition ); //signal changes + + ithread_mutex_unlock( &tp->mutex ); + + if( retCode != 0 ) { + //clean up if the min threads could + //not be created + ThreadPoolShutdown( tp ); + } + + return retCode; + } + +/**************************************************************************** + * Function: ThreadPoolShutdown + * + * Description: + * Shuts the thread pool down. + * Waits for all threads to finish. + * May block indefinitely if jobs do not + * exit. + * Parameters: + * tp - must be valid tp + * Returns: + * 0 on success, nonzero on failure + * Always returns 0. + *****************************************************************************/ + int ThreadPoolShutdown( ThreadPool * tp ) { + + ListNode *head = NULL; + ThreadPoolJob *temp = NULL; + + assert( tp != NULL ); + + if( tp == NULL ) { + return EINVAL; + } + + ithread_mutex_lock( &tp->mutex ); + + //clean up high priority jobs + while( tp->highJobQ.size ) { + head = ListHead( &tp->highJobQ ); + temp = ( ThreadPoolJob * ) head->item; + if( temp->free_func ) + temp->free_func( temp->arg ); + FreeThreadPoolJob( tp, temp ); + ListDelNode( &tp->highJobQ, head, 0 ); + } + + ListDestroy( &tp->highJobQ, 0 ); + + //clean up med priority jobs + while( tp->medJobQ.size ) { + head = ListHead( &tp->medJobQ ); + temp = ( ThreadPoolJob * ) head->item; + if( temp->free_func ) + temp->free_func( temp->arg ); + FreeThreadPoolJob( tp, temp ); + ListDelNode( &tp->medJobQ, head, 0 ); + } + + ListDestroy( &tp->medJobQ, 0 ); + + //clean up low priority jobs + while( tp->lowJobQ.size ) { + head = ListHead( &tp->lowJobQ ); + temp = ( ThreadPoolJob * ) head->item; + if( temp->free_func ) + temp->free_func( temp->arg ); + FreeThreadPoolJob( tp, temp ); + ListDelNode( &tp->lowJobQ, head, 0 ); + } + + ListDestroy( &tp->lowJobQ, 0 ); + + //clean up long term job + if( tp->persistentJob ) { + temp = tp->persistentJob; + if( temp->free_func ) + temp->free_func( temp->arg ); + FreeThreadPoolJob( tp, temp ); + tp->persistentJob = NULL; + } + + tp->shutdown = 1; + ithread_cond_broadcast( &tp->condition ); //signal shutdown + + //wait for all threads to finish + while( tp->totalThreads > 0 ) { + + ithread_cond_wait( &tp->start_and_shutdown, &tp->mutex ); + } + + //destroy condition + while( ithread_cond_destroy( &tp->condition ) != 0 ) { + } + + while( ithread_cond_destroy( &tp->start_and_shutdown ) != 0 ) { + } + + FreeListDestroy( &tp->jobFreeList ); + + ithread_mutex_unlock( &tp->mutex ); + + //destroy mutex + while( ithread_mutex_destroy( &tp->mutex ) != 0 ) { + } + + return 0; + } + +/**************************************************************************** + * Function: TPAttrInit + * + * Description: + * Initializes thread pool attributes. + * Sets values to defaults defined in ThreadPool.h. + * Parameters: + * attr - must be valid thread pool attributes. + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrInit( ThreadPoolAttr * attr ) { + assert( attr != NULL ); + + if( attr == NULL ) { + return EINVAL; + } + + attr->jobsPerThread = DEFAULT_JOBS_PER_THREAD; + attr->maxIdleTime = DEFAULT_IDLE_TIME; + attr->maxThreads = DEFAULT_MAX_THREADS; + attr->minThreads = DEFAULT_MIN_THREADS; + attr->schedPolicy = DEFAULT_POLICY; + attr->starvationTime = DEFAULT_STARVATION_TIME; + return 0; + } + +/**************************************************************************** + * Function: TPJobInit + * + * Description: + * Initializes thread pool job. + * Sets the priority to default defined in ThreadPool.h. + * Sets the free_routine to default defined in ThreadPool.h + * Parameters: + * ThreadPoolJob *job - must be valid thread pool attributes. + * start_routine func - function to run, must be valid + * void * arg - argument to pass to function. + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPJobInit( ThreadPoolJob * job, + start_routine func, + void *arg ) { + assert( job != NULL ); + assert( func != NULL ); + + if( ( job == NULL ) || ( func == NULL ) ) { + return EINVAL; + } + + job->func = func; + job->arg = arg; + job->priority = DEFAULT_PRIORITY; + job->free_func = DEFAULT_FREE_ROUTINE; + return 0; + } + +/**************************************************************************** + * Function: TPJobSetPriority + * + * Description: + * Sets the max threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * maxThreads - value to set + * Returns: + * Returns 0 on success nonzero on failure. + * Returns EINVAL if invalid priority. + *****************************************************************************/ + int TPJobSetPriority( ThreadPoolJob * job, + ThreadPriority priority ) { + assert( job != NULL ); + + if( job == NULL ) { + return EINVAL; + } + + if( priority == LOW_PRIORITY || priority == MED_PRIORITY || + priority == HIGH_PRIORITY ) { + job->priority = priority; + return 0; + } else { + return EINVAL; + } + } + +/**************************************************************************** + * Function: TPJobSetFreeFunction + * + * Description: + * Sets the max threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * maxThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPJobSetFreeFunction( ThreadPoolJob * job, + free_routine func ) { + assert( job != NULL ); + + if( job == NULL ) { + return EINVAL; + } + + job->free_func = func; + return 0; + } + +/**************************************************************************** + * Function: TPAttrSetMaxThreads + * + * Description: + * Sets the max threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * maxThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrSetMaxThreads( ThreadPoolAttr * attr, + int maxThreads ) { + assert( attr != NULL ); + + if( attr == NULL ) { + return EINVAL; + } + + attr->maxThreads = maxThreads; + return 0; + } + +/**************************************************************************** + * Function: TPAttrSetMinThreads + * + * Description: + * Sets the min threads for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * minThreads - value to set + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrSetMinThreads( ThreadPoolAttr * attr, + int minThreads ) { + assert( attr != NULL ); + + if( attr == NULL ) { + return EINVAL; + } + + attr->minThreads = minThreads; + return 0; + } + +/**************************************************************************** + * Function: TPAttrSetIdleTime + * + * Description: + * Sets the idle time for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrSetIdleTime( ThreadPoolAttr * attr, + int idleTime ) { + assert( attr != NULL ); + + if( attr == NULL ) { + return EINVAL; + } + + attr->maxIdleTime = idleTime; + return 0; + } + +/**************************************************************************** + * Function: TPAttrSetJobsPerThread + * + * Description: + * Sets the max thre + * Parameters: + * attr - must be valid thread pool attributes. + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrSetJobsPerThread( ThreadPoolAttr * attr, + int jobsPerThread ) { + assert( attr != NULL ); + + if( attr == NULL ) { + return EINVAL; + } + + attr->jobsPerThread = jobsPerThread; + return 0; + } + +/**************************************************************************** + * Function: TPAttrSetStarvationTime + * + * Description: + * Sets the starvation time for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrSetStarvationTime( ThreadPoolAttr * attr, + int starvationTime ) { + assert( attr != NULL ); + + if( attr == NULL ) { + return EINVAL; + } + + attr->starvationTime = starvationTime; + return 0; + } + +/**************************************************************************** + * Function: TPAttrSetSchedPolicy + * + * Description: + * Sets the scheduling policy for the thread pool attributes. + * Parameters: + * attr - must be valid thread pool attributes. + * PolicyType schedPolicy - must be a valid policy type. + * Returns: + * Always returns 0. + *****************************************************************************/ + int TPAttrSetSchedPolicy( ThreadPoolAttr * attr, + PolicyType schedPolicy ) { + assert( attr != NULL ); + if( attr == NULL ) { + return EINVAL; + } + attr->schedPolicy = schedPolicy; + return 0; + } + + STATSONLY( void ThreadPoolPrintStats( ThreadPoolStats * stats ) { + assert( stats != NULL ); if( stats == NULL ) { + return;} + + printf( "ThreadPoolStats at Time: %ld\n", time( NULL ) ); + printf + ( "Average Wait in High Priority Q in milliseconds: %lf\n", + stats->avgWaitHQ ); + printf + ( "Average Wait in Med Priority Q in milliseconds: %lf\n", + stats->avgWaitMQ ); + printf + ( "Averate Wait in Low Priority Q in milliseconds: %lf\n", + stats->avgWaitLQ ); + printf( "Max Threads Active: %d\n", stats->maxThreads ); + printf( "Current Worker Threads: %d\n", + stats->workerThreads ); + printf( "Current Persistent Threads: %d\n", + stats->persistentThreads ); + printf( "Current Idle Threads: %d\n", stats->idleThreads ); + printf( "Total Threads : %d\n", stats->totalThreads ); + printf( "Total Time spent Working in seconds: %lf\n", + stats->totalWorkTime ); + printf( "Total Time spent Idle in seconds : %lf\n", + stats->totalIdleTime );} + ) + +/**************************************************************************** + * Function: ThreadPoolGetStats + * + * Description: + * Returns various statistics about the + * thread pool. + * Only valid if STATS has been defined. + * Parameters: + * ThreadPool *tp - valid initialized threadpool + * ThreadPoolStats *stats - valid stats, out parameter + * Returns: + * Always returns 0. + *****************************************************************************/ + STATSONLY( int + ThreadPoolGetStats( ThreadPool * tp, + ThreadPoolStats * stats ) { + + assert( tp != NULL ); + assert( stats != NULL ); + if( ( tp == NULL ) || ( stats == NULL ) ) { + return EINVAL;} + + //if not shutdown then acquire mutex + if( !tp->shutdown ) { + ithread_mutex_lock( &tp->mutex );} + + ( *stats ) = tp->stats; if( stats->totalJobsHQ > 0 ) + stats->avgWaitHQ = + stats->totalTimeHQ / stats->totalJobsHQ; + else + stats->avgWaitHQ = 0; if( stats->totalJobsMQ > 0 ) + stats->avgWaitMQ = + stats->totalTimeMQ / stats->totalJobsMQ; + else + stats->avgWaitMQ = 0; if( stats->totalJobsLQ > 0 ) + stats->avgWaitLQ = + stats->totalTimeLQ / stats->totalJobsLQ; + else + stats->avgWaitLQ = 0; + stats->totalThreads = tp->totalThreads; + stats->persistentThreads = tp->persistentThreads; + stats->currentJobsHQ = ListSize( &tp->highJobQ ); + stats->currentJobsLQ = ListSize( &tp->lowJobQ ); + stats->currentJobsMQ = ListSize( &tp->medJobQ ); + //if not shutdown then release mutex + if( !tp->shutdown ) { + ithread_mutex_unlock( &tp->mutex );} + + return 0;} + + ) diff --git a/libupnp/threadutil/src/TimerThread.c b/libupnp/threadutil/src/TimerThread.c new file mode 100644 index 0000000..d213e97 --- /dev/null +++ b/libupnp/threadutil/src/TimerThread.c @@ -0,0 +1,519 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "TimerThread.h" +#include + +/**************************************************************************** + * Function: FreeTimerEvent + * + * Description: + * Deallocates a dynamically allocated TimerEvent. + * Parameters: + * TimerEvent *event - must be allocated with CreateTimerEvent + *****************************************************************************/ +static void +FreeTimerEvent( TimerThread * timer, + TimerEvent * event ) +{ + + assert( timer != NULL ); + + FreeListFree( &timer->freeEvents, event ); +} + +/**************************************************************************** + * Function: TimerThreadWorker + * + * Description: + * Implements timer thread. + * Waits for next event to occur and schedules + * associated job into threadpool. + * Internal Only. + * Parameters: + * void * arg -> is cast to TimerThread * + *****************************************************************************/ +static void * +TimerThreadWorker( void *arg ) +{ + TimerThread *timer = ( TimerThread * ) arg; + ListNode *head = NULL; + + TimerEvent *nextEvent = NULL; + + time_t currentTime = 0; + time_t nextEventTime = 0; + struct timespec timeToWait; + + int tempId; + + assert( timer != NULL ); + + ithread_mutex_lock( &timer->mutex ); + + while( 1 ) + { + + //mutex should always be locked at top of loop + + //Check for shutdown + + if( timer->shutdown ) + { + + timer->shutdown = 0; + ithread_cond_signal( &timer->condition ); + ithread_mutex_unlock( &timer->mutex ); + return NULL; + + } + + nextEvent = NULL; + + //Get the next event if possible + if( timer->eventQ.size > 0 ) + { + head = ListHead( &timer->eventQ ); + + nextEvent = ( TimerEvent * ) head->item; + nextEventTime = nextEvent->eventTime; + } + + currentTime = time( NULL ); + + //If time has elapsed, schedule job + + if( ( nextEvent != NULL ) && ( currentTime >= nextEventTime ) ) + { + + if( nextEvent->persistent ) { + + ThreadPoolAddPersistent( timer->tp, &nextEvent->job, + &tempId ); + } else { + + ThreadPoolAdd( timer->tp, &nextEvent->job, &tempId ); + } + + ListDelNode( &timer->eventQ, head, 0 ); + FreeTimerEvent( timer, nextEvent ); + + continue; + + } + + if( nextEvent != NULL ) { + timeToWait.tv_nsec = 0; + timeToWait.tv_sec = nextEvent->eventTime; + + ithread_cond_timedwait( &timer->condition, &timer->mutex, + &timeToWait ); + + } else { + ithread_cond_wait( &timer->condition, &timer->mutex ); + } + + } +} + +/**************************************************************************** + * Function: CalculateEventTime + * + * Description: + * Calculates the appropriate timeout in absolute seconds since + * Jan 1, 1970 + * Internal Only. + * Parameters: + * time_t *timeout - timeout + * + *****************************************************************************/ +static int +CalculateEventTime( time_t * timeout, + TimeoutType type ) +{ + time_t now; + + assert( timeout != NULL ); + + if( type == ABS_SEC ) + return 0; + else if( type == REL_SEC ) { + time( &now ); + ( *timeout ) += now; + return 0; + } + + return -1; + +} + +/**************************************************************************** + * Function: CreateTimerEvent + * + * Description: + * Creates a Timer Event. (Dynamically allocated) + * Internal to timer thread. + * Parameters: + * func - thread function to run. + * arg - argument to function. + * priority - priority of job. + * eventTime - the absoule time of the event + * in seconds from Jan, 1970 + * id - id of job + * + * Returns: + * TimerEvent * on success, NULL on failure. + ****************************************************************************/ +static TimerEvent * +CreateTimerEvent( TimerThread * timer, + ThreadPoolJob * job, + Duration persistent, + time_t eventTime, + int id ) +{ + TimerEvent *temp = NULL; + + assert( timer != NULL ); + assert( job != NULL ); + + temp = ( TimerEvent * ) FreeListAlloc( &timer->freeEvents ); + if( temp == NULL ) + return temp; + temp->job = ( *job ); + temp->persistent = persistent; + temp->eventTime = eventTime; + temp->id = id; + + return temp; +} + +/************************************************************************ + * Function: TimerThreadInit + * + * Description: + * Initializes and starts timer thread. + * + * Parameters: + * timer - valid timer thread pointer. + * tp - valid thread pool to use. Must be + * started. Must be valid for lifetime + * of timer. Timer must be shutdown + * BEFORE thread pool. + * Return: + * 0 on success, nonzero on failure + * Returns error from ThreadPoolAddPersistent if failure. + ************************************************************************/ +int +TimerThreadInit( TimerThread * timer, + ThreadPool * tp ) +{ + + int rc = 0; + + ThreadPoolJob timerThreadWorker; + + assert( timer != NULL ); + assert( tp != NULL ); + + if( ( timer == NULL ) || ( tp == NULL ) ) { + return EINVAL; + } + + rc += ithread_mutex_init( &timer->mutex, NULL ); + + assert( rc == 0 ); + + rc += ithread_mutex_lock( &timer->mutex ); + assert( rc == 0 ); + + rc += ithread_cond_init( &timer->condition, NULL ); + assert( rc == 0 ); + + rc += FreeListInit( &timer->freeEvents, sizeof( TimerEvent ), 100 ); + assert( rc == 0 ); + + timer->shutdown = 0; + timer->tp = tp; + timer->lastEventId = 0; + rc += ListInit( &timer->eventQ, NULL, NULL ); + + assert( rc == 0 ); + + if( rc != 0 ) { + rc = EAGAIN; + } else { + + TPJobInit( &timerThreadWorker, TimerThreadWorker, timer ); + TPJobSetPriority( &timerThreadWorker, HIGH_PRIORITY ); + + rc = ThreadPoolAddPersistent( tp, &timerThreadWorker, NULL ); + } + + ithread_mutex_unlock( &timer->mutex ); + + if( rc != 0 ) { + ithread_cond_destroy( &timer->condition ); + ithread_mutex_destroy( &timer->mutex ); + FreeListDestroy( &timer->freeEvents ); + ListDestroy( &timer->eventQ, 0 ); + } + + return rc; + +} + +/************************************************************************ + * Function: TimerThreadSchedule + * + * Description: + * Schedules an event to run at a specified time. + * + * Parameters: + * timer - valid timer thread pointer. + * time_t - time of event. + * either in absolute seconds, + * or relative seconds in the future. + * timeoutType - either ABS_SEC, or REL_SEC. + * if REL_SEC, then the event + * will be scheduled at the + * current time + REL_SEC. + * + * func - function to schedule + * arg - argument to function + * priority - priority of job. + * id - id of timer event. (out) + * Return: + * 0 on success, nonzero on failure + * EOUTOFMEM if not enough memory to schedule job + ************************************************************************/ +int +TimerThreadSchedule( TimerThread * timer, + time_t timeout, + TimeoutType type, + ThreadPoolJob * job, + Duration duration, + int *id ) +{ + + int rc = EOUTOFMEM; + int found = 0; + int tempId = 0; + + ListNode *tempNode = NULL; + TimerEvent *temp = NULL; + TimerEvent *newEvent = NULL; + + assert( timer != NULL ); + assert( job != NULL ); + + if( ( timer == NULL ) || ( job == NULL ) ) { + return EINVAL; + } + + CalculateEventTime( &timeout, type ); + ithread_mutex_lock( &timer->mutex ); + + if( id == NULL ) + id = &tempId; + + ( *id ) = INVALID_EVENT_ID; + + newEvent = CreateTimerEvent( timer, job, duration, timeout, + timer->lastEventId ); + + if( newEvent == NULL ) { + ithread_mutex_unlock( &timer->mutex ); + return rc; + } + + tempNode = ListHead( &timer->eventQ ); + //add job to Q + //Q is ordered by eventTime + //with the head of the Q being the next event + + while( tempNode != NULL ) { + temp = ( TimerEvent * ) tempNode->item; + if( temp->eventTime >= timeout ) + { + + if( ListAddBefore( &timer->eventQ, newEvent, tempNode ) != + NULL ) + rc = 0; + found = 1; + break; + + } + tempNode = ListNext( &timer->eventQ, tempNode ); + } + + //add to the end of Q + if( !found ) { + + if( ListAddTail( &timer->eventQ, newEvent ) != NULL ) + rc = 0; + + } + //signal change in Q + if( rc == 0 ) { + + ithread_cond_signal( &timer->condition ); + } else { + FreeTimerEvent( timer, newEvent ); + } + ( *id ) = timer->lastEventId++; + ithread_mutex_unlock( &timer->mutex ); + + return rc; +} + +/************************************************************************ + * Function: TimerThreadRemove + * + * Description: + * Removes an event from the timer Q. + * Events can only be removed + * before they have been placed in the + * thread pool. + * + * Parameters: + * timer - valid timer thread pointer. + * id - id of event to remove. + * out - space for returned job (Can be NULL) + * Return: + * 0 on success. + * INVALID_EVENT_ID on error. + * + ************************************************************************/ +int +TimerThreadRemove( TimerThread * timer, + int id, + ThreadPoolJob * out ) +{ + int rc = INVALID_EVENT_ID; + ListNode *tempNode = NULL; + TimerEvent *temp = NULL; + + assert( timer != NULL ); + + if( timer == NULL ) { + return EINVAL; + } + + ithread_mutex_lock( &timer->mutex ); + + tempNode = ListHead( &timer->eventQ ); + + while( tempNode != NULL ) { + temp = ( TimerEvent * ) tempNode->item; + if( temp->id == id ) + { + + ListDelNode( &timer->eventQ, tempNode, 0 ); + if( out != NULL ) + ( *out ) = temp->job; + FreeTimerEvent( timer, temp ); + rc = 0; + break; + } + tempNode = ListNext( &timer->eventQ, tempNode ); + } + + ithread_mutex_unlock( &timer->mutex ); + return rc; +} + +/************************************************************************ + * Function: TimerThreadShutdown + * + * Description: + * Shutdown the timer thread + * Events scheduled in the future will NOT be run. + * Timer thread should be shutdown BEFORE it's associated + * thread pool. + * Returns: + * returns 0 if succesfull, + * nonzero otherwise. + * Always returns 0. + ***********************************************************************/ +int +TimerThreadShutdown( TimerThread * timer ) +{ + ListNode *tempNode2 = NULL; + ListNode *tempNode = NULL; + + assert( timer != NULL ); + + if( timer == NULL ) { + return EINVAL; + } + + ithread_mutex_lock( &timer->mutex ); + + timer->shutdown = 1; + tempNode = ListHead( &timer->eventQ ); + + //Delete nodes in Q + //call registered free function + //on argument + while( tempNode != NULL ) { + TimerEvent *temp = ( TimerEvent * ) tempNode->item; + + tempNode2 = ListNext( &timer->eventQ, tempNode ); + ListDelNode( &timer->eventQ, tempNode, 0 ); + if( temp->job.free_func ) { + temp->job.free_func( temp->job.arg ); + } + FreeTimerEvent( timer, temp ); + tempNode = tempNode2; + } + + ListDestroy( &timer->eventQ, 0 ); + FreeListDestroy( &timer->freeEvents ); + + ithread_cond_broadcast( &timer->condition ); + + while( timer->shutdown ) //wait for timer thread to shutdown + { + ithread_cond_wait( &timer->condition, &timer->mutex ); + } + + ithread_mutex_unlock( &timer->mutex ); + + //destroy condition + while( ithread_cond_destroy( &timer->condition ) != 0 ) { + } + + //destroy mutex + while( ithread_mutex_destroy( &timer->mutex ) != 0 ) { + } + + return 0; +} diff --git a/libupnp/threadutil/src/iasnprintf.c b/libupnp/threadutil/src/iasnprintf.c new file mode 100644 index 0000000..dd2bd16 --- /dev/null +++ b/libupnp/threadutil/src/iasnprintf.c @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "iasnprintf.h" + +#ifndef NULL +#define NULL 0 +#endif + +/** + * Allocates enough memory for the + * Formatted string, up to max + * specified. + * With max set to -1, it allocates as + * much size as needed. + * Memory must be freed using free. + */ + +int +iasnprintf( char **ret, + int incr, + int max, + const char *fmt, + ... ) +{ + int size = incr; + int retc = 0; + va_list ap; + char *temp = NULL; + + assert( ret ); + assert( fmt ); + ( *ret ) = ( char * )malloc( incr ); + + while( 1 ) { + va_start( ap, fmt ); + retc = vsnprintf( ( *ret ), size, fmt, ap ); + va_end( ap ); + + if( retc < 0 ) { + //size not big enough + //and vsnprintf does NOT return the + //necessary number of bytes + if( ( max != -1 ) && ( size == max ) ) //max reached + { + break; + } + + incr *= 2; //increase increment + //increase size and try again + if( ( max != -1 ) && ( ( size + incr ) > max ) ) { + incr = ( max - size ); + } + + temp = ( char * )realloc( ( *ret ), size + incr ); + if( temp == NULL ) { + break; + } + size += incr; + ( *ret ) = temp; + + } else { + if( ( retc + 1 ) > size ) { + //size not big enough + //and vsnprintf + //returns the necessary + //number of bytes + if( ( max != -1 ) && ( retc + 1 > max ) ) { + retc = -1; + break; + } + + temp = ( char * )realloc( ( *ret ), retc + 1 ); + if( temp == NULL ) { + retc = -1; + break; + } + size = retc + 1; + ( *ret ) = temp; //size increased try again + } else if( ( retc + 1 ) < size ) { + //size is bigger than needed + //try and reallocate smaller + + temp = ( char * )realloc( ( *ret ), retc + 1 ); + if( temp != NULL ) { + ( *ret ) = temp; + } + break; + } else //size is just right, exit + { + break; + } + + } + } + + if( retc < 0 ) { + free( ( *ret ) ); + ( *ret ) = NULL; + } + + return retc; +} + +void +iasnprintfFree( char *fChar ) +{ + free( fChar ); + fChar = NULL; +} diff --git a/libupnp/upnp/LICENSE b/libupnp/upnp/LICENSE new file mode 100644 index 0000000..d0d55c3 --- /dev/null +++ b/libupnp/upnp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2000-2003 Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/libupnp/upnp/Makefile.am b/libupnp/upnp/Makefile.am new file mode 100644 index 0000000..3949aa3 --- /dev/null +++ b/libupnp/upnp/Makefile.am @@ -0,0 +1,161 @@ +# $Id: Makefile.am,v 1.10 2006/04/08 15:22:22 r3mi Exp $ +# +# "Makefile.am" for "libupnp/upnp" +# +# Copyright (C) 2005 Rémi Turboult +# + +SUBDIRS = doc + +AM_CPPFLAGS = -I$(srcdir)/inc \ + -I$(top_srcdir)/threadutil/inc \ + -I$(top_srcdir)/ixml/inc + +AM_CFLAGS = $(PTHREAD_CFLAGS) + +AM_LDFLAGS = $(PTHREAD_LIBS) + +LDADD = libupnp.la \ + $(top_builddir)/threadutil/libthreadutil.la \ + $(top_builddir)/ixml/libixml.la + + +upnpincludedir = $(includedir)/upnp +upnpinclude_HEADERS = inc/upnp.h +nodist_upnpinclude_HEADERS = inc/upnpconfig.h +if ENABLE_TOOLS +upnpinclude_HEADERS += inc/upnptools.h +endif +if ENABLE_DEBUG +upnpinclude_HEADERS += inc/upnpdebug.h +endif + +lib_LTLIBRARIES = libupnp.la + +libupnp_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/src/inc +libupnp_la_LDFLAGS = -version-info $(LT_VERSION_UPNP) \ + -export-symbols-regex '^Upnp.*' \ + $(top_builddir)/threadutil/libthreadutil.la \ + $(top_builddir)/ixml/libixml.la + +libupnp_la_SOURCES = \ + src/inc/config.h \ + src/inc/client_table.h src/inc/global.h \ + src/inc/md5.h src/inc/ssdplib.h \ + src/inc/unixutil.h src/inc/urlconfig.h \ + src/inc/gmtdate.h src/inc/membuffer.h \ + src/inc/server.h src/inc/statcodes.h \ + src/inc/upnpapi.h src/inc/utilall.h \ + src/inc/gena_ctrlpt.h src/inc/http_client.h \ + src/inc/miniserver.h src/inc/service_table.h \ + src/inc/statuscodes.h src/inc/upnpclosesocket.h \ + src/inc/util.h src/inc/gena_device.h \ + src/inc/httpparser.h src/inc/netall.h \ + src/inc/soaplib.h src/inc/strintmap.h \ + src/inc/upnp_timeout.h src/inc/uuid.h \ + src/inc/gena.h src/inc/httpreadwrite.h \ + src/inc/parsetools.h src/inc/sock.h \ + src/inc/sysdep.h src/inc/uri.h \ + src/inc/webserver.h + +# ssdp +libupnp_la_SOURCES += src/ssdp/ssdp_device.c src/ssdp/ssdp_ctrlpt.c \ + src/ssdp/ssdp_server.c + +# soap +libupnp_la_SOURCES += src/soap/soap_device.c src/soap/soap_ctrlpt.c \ + src/soap/soap_common.c + +# genlib +libupnp_la_SOURCES += \ + src/genlib/miniserver/miniserver.c \ + src/genlib/service_table/service_table.c \ + src/genlib/util/membuffer.c \ + src/genlib/util/strintmap.c \ + src/genlib/util/upnp_timeout.c \ + src/genlib/util/util.c \ + src/genlib/client_table/client_table.c \ + src/genlib/net/sock.c \ + src/genlib/net/http/httpparser.c \ + src/genlib/net/http/httpreadwrite.c \ + src/genlib/net/http/statcodes.c \ + src/genlib/net/http/webserver.c \ + src/genlib/net/http/parsetools.c \ + src/genlib/net/uri/uri.c + +# gena +libupnp_la_SOURCES += src/gena/gena_device.c src/gena/gena_ctrlpt.c \ + src/gena/gena_callback2.c + +# api +libupnp_la_SOURCES += src/api/upnpapi.c +if ENABLE_TOOLS + libupnp_la_SOURCES += src/api/upnptools.c +endif +if ENABLE_DEBUG + libupnp_la_SOURCES += src/api/upnpdebug.c +endif + +# uuid +libupnp_la_SOURCES += src/uuid/md5.c src/uuid/sysdep.c src/uuid/uuid.c + +# urlconfig +libupnp_la_SOURCES += src/urlconfig/urlconfig.c + + +# samples +noinst_PROGRAMS = +if ENABLE_SAMPLES +if ENABLE_CLIENT + noinst_PROGRAMS += upnp_tv_ctrlpt + upnp_tv_ctrlpt_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(srcdir)/sample/common/ \ + -I$(srcdir)/sample/tvctrlpt +endif +if ENABLE_DEVICE + noinst_PROGRAMS += upnp_tv_device + upnp_tv_device_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(srcdir)/sample/common/ \ + -I$(srcdir)/sample/tvdevice +endif +endif +upnp_tv_device_SOURCES = \ + sample/common/sample_util.c \ + sample/common/sample_util.h \ + sample/tvdevice/upnp_tv_device.c \ + sample/tvdevice/upnp_tv_device.h \ + sample/tvdevice/linux/upnp_tv_device_main.c +upnp_tv_ctrlpt_SOURCES = \ + sample/common/sample_util.c \ + sample/common/sample_util.h \ + sample/tvctrlpt/upnp_tv_ctrlpt.c \ + sample/tvctrlpt/upnp_tv_ctrlpt.h \ + sample/tvctrlpt/linux/upnp_tv_ctrlpt_main.c + +if WITH_DOCDIR + docdir = @DOCDIR@ + examplesdir = $(docdir)/examples + examples_DATA = $(upnp_tv_ctrlpt_SOURCES) $(upnp_tv_device_SOURCES) +endif + + +# check / distcheck tests +check_PROGRAMS = test_init +TESTS = test_init + +test_init_SOURCES = test/test_init.c + + +EXTRA_DIST = LICENSE \ + sample/tvdevice/web/tvcontrolSCPD.xml \ + sample/tvdevice/web/tvdevicedesc.xml \ + sample/tvdevice/web/tvdevicepres.html \ + sample/tvdevice/web/tvpictureSCPD.xml + + + +CLEANFILES = IUpnpErrFile.txt IUpnpInfoFile.txt + + + + diff --git a/libupnp/upnp/doc/Makefile.am b/libupnp/upnp/doc/Makefile.am new file mode 100644 index 0000000..2959268 --- /dev/null +++ b/libupnp/upnp/doc/Makefile.am @@ -0,0 +1,65 @@ +# $Id: Makefile.am,v 1.2 2006/03/28 21:07:01 r3mi Exp $ +# +# "Makefile.am" for "libunp/upnp/doc" +# +# (C) Copyright 2005 Rémi Turboult +# +########################################################################## +# +# Copyright (c) 2000-2003 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +########################################################################## + +EXTRA_DIST = UPnP_Programming_Guide.pdf \ + callback.dxx docxx.sty intro.dxx \ + license.dxx upnpsdk.dxx + +if WITH_DOCDIR + docdir = @DOCDIR@ + doc_DATA = UPnP_Programming_Guide.pdf +endif + + +html-local: + @if [ -d html ]; then rm -rf html; fi + @doc++ -nd -S -w -j -d html $(srcdir)/upnpsdk.dxx + +pdf-local: + @doc++ -nd -S -w -j -t --package a4wide -o upnpsdk.tex \ + $(srcdir)/upnpsdk.dxx + @-pdflatex "\scrollmode\input upnpsdk.tex" > latex.log + @-pdflatex "\scrollmode\input upnpsdk.tex" >> latex.log + @-pdflatex "\scrollmode\input upnpsdk.tex" >> latex.log + +clean-local: + -rm -rf html + -rm -f upnpsdk.tex upnpsdk.dvi upnpsdk.ps upnpsdk.log upnpsdk.aux + -rm -f latex.log + + + diff --git a/libupnp/upnp/doc/UPnP_Programming_Guide.pdf b/libupnp/upnp/doc/UPnP_Programming_Guide.pdf new file mode 100644 index 0000000..d81de2a Binary files /dev/null and b/libupnp/upnp/doc/UPnP_Programming_Guide.pdf differ diff --git a/libupnp/upnp/doc/callback.dxx b/libupnp/upnp/doc/callback.dxx new file mode 100644 index 0000000..7823a16 --- /dev/null +++ b/libupnp/upnp/doc/callback.dxx @@ -0,0 +1,13 @@ +/**@name About Callbacks + * + * The Linux SDK for UPnP Devices contains functions that generate + * asynchronous callbacks. To simplify the application callback functions, + * these callbacks are executed on a thread owned by the SDK itself. The + * SDK executes the application's callback function in a thread + * context so the application can allocate memory and preserve the information + * it needs. The application can also use standard thread synchronization + * methods to ensure data integrity. Due to the possibility of deadlock, the + * application cannot call back into the SDK during these callbacks + * unless explicitly noted. There is no restriction in calling into the + * operating system or any other application interfaces. + */ diff --git a/libupnp/upnp/doc/docxx.sty b/libupnp/upnp/doc/docxx.sty new file mode 100644 index 0000000..f876b51 --- /dev/null +++ b/libupnp/upnp/doc/docxx.sty @@ -0,0 +1,1082 @@ +% docxx.sty +% +% Copyright (c) 1996 Roland Wunderling, Malte Zoeckler +% Copyright (c) 1999-2001 Dragos Acostachioaie +% +% This file is part of DOC++. +% +% DOC++ is free software; you can redistribute it and/or +% modify it under the terms of the GNU General Public +% License as published by the Free Software Foundation; either +% version 2 of the license, or (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +% General Public License for more details. +% +% You should have received a copy of the GNU General Public +% License along with this program; if not, write to the Free +% Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +% +% +% Switch off special characters except {}\ for the rest of the text. +% +\def\cxxtilde{{\tt\~\relax}} +\addtolength{\parskip}{6pt} + +\catcode`\,=\active% +\def,{\char`\,\penalty-8\ } +%\def,{++ } +\catcode`\,=12 + +\def\<{{\tt <}} +\def\>{{\tt >}} +\def\TEX{} +\def\cxxExceptionsStr{} +\def\cxxParameterStr{} +\def\cxxReturnStr{} +\def\cxxInvariantsStr{} +\def\cxxPreconditionsStr{} +\def\cxxPostconditionsStr{} +\def\cxxSeeStr{} +\def\cxxAuthorStr{} +\def\cxxVersionStr{} +\def\cxxDeprecatedStr{} +\def\cxxSinceStr{} +\def\cxxFileStr{} +\def\cxxExceptions#1{\def\cxxExceptionsStr{#1}} +\def\cxxParameter#1{\def\cxxParameterStr{#1}} +\def\cxxReturn#1{\def\cxxReturnStr{#1}} +\def\cxxInvariants#1{\def\cxxInvariantsStr{#1}} +\def\cxxPreconditions#1{\def\cxxPreconditionsStr{#1}} +\def\cxxPostconditions#1{\def\cxxPostconditionsStr{#1}} +\def\cxxSee#1{\def\cxxSeeStr{#1}} +\def\cxxAuthor#1{\def\cxxAuthorStr{#1}} +\def\cxxVersion#1{\def\cxxVersionStr{#1}} +\def\cxxDeprecated#1{\def\cxxDeprecatedStr{#1}} +\def\cxxSince#1{\def\cxxSinceStr{#1}} +\def\cxxFile#1{\def\cxxFileStr{#1}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Define ... to do verbatim listing +% +\catcode`\=\active +\catcode`\=\active +{\obeyspaces\gdef {\ }} + +\def\ccverbatim{\strut\begingroup + \catcode`\\=12 \catcode`\{=12 + \catcode`\}=12 \catcode`\$=12 + \catcode`\&=12 \catcode`\#=12 + \catcode`\%=12 \catcode`\~=12 + \catcode`\_=12 \catcode`\^=12 + \catcode`\|=12 \catcode`\/=12 + \obeyspaces\tt} +\def{\let\par=\endgraf \ccverbatim \parskip=0pt \ccfinish} +{\catcode`\=0 catcode`\=12 +gdefccfinish#1{#1endgroup}} + +% +% Definition of structuring comands. +% +\newcommand{\Section}[1]{\section{#1}} +\newcommand{\SubSection}[1]{\subsection{#1}} +\newcommand{\SubSubSection}[1]{\subsubsection{#1}} +\newcommand{\Paragraph}[1]{\paragraph{#1}} + +\newcommand{\Ref}[1]{{\bf #1} ($\rightarrow$ \ref{#1})} +\newcommand{\URL}[2][]{% + \def\name{#1}% + \def\empty{}% + \ifx\name\empty% + {\tt #2}% + \else% + #1 ({\tt #2})% + \fi% +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% printing line #1 of code #2 +% +\newdimen\cxxcodewidth +\cxxcodewidth=\textwidth +\advance\cxxcodewidth by -21pt + +\def\cxxCodeLine#1#2{% +{\hbox to 20pt{\tiny\hss#1}\parbox[t]{\cxxcodewidth}{\small#2}}% +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for listing manual entries +% +\newdimen\cxxIdWidth +\newdimen\cxxTypeWidth +\newdimen\cxxProtoWidth +\newdimen\cxxMemoWidth +\newdimen\cxxPageWidth + +\cxxIdWidth=0.1\textwidth +\cxxTypeWidth=0.15\textwidth +\cxxProtoWidth=0.25\textwidth +\cxxMemoWidth=0.43\textwidth + +\cxxPageWidth=\textwidth +\advance\cxxPageWidth by-\cxxIdWidth +\advance\cxxPageWidth by-\cxxTypeWidth +\advance\cxxPageWidth by-\cxxProtoWidth +\advance\cxxPageWidth by-\cxxMemoWidth + +\newdimen\cxxProtoMemoWidth +\cxxProtoMemoWidth=\cxxProtoWidth +\advance\cxxProtoMemoWidth by\cxxMemoWidth + +\def\cxxStrut{\vrule width0pt height0pt depth9pt} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 1: type +% 2: name +% 3: args +% 4: memo +% 5: id +\def\cxxitem#1#2#3#4#5{\noindent{% + \setbox5\hbox{#5 }% + \ifdim\wd5>\cxxIdWidth% + \setbox5\hbox{\hbox to \cxxIdWidth{\hss#5}}% + \else + \setbox5\hbox{\hbox to \cxxIdWidth{#5\hss}}% + \fi% + \setbox1\hbox{{% + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}#1% + }}% + \setbox2\hbox{{% + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}\textbf{#2} #3% + }}% + \setbox4\hbox{\parbox[t]{\cxxMemoWidth}{{% + \raggedright\sloppy% + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}{\em #4} + \cxxStrut% + \def\page{#5}% + \ifx\page\empty% + \hss% + \else% + \ \dotfill% + \hbox to 0pt{\hbox to \cxxPageWidth{\hss% + \pageref{cxx.#5}% + }\hss}% + \fi% + }}}% + % + \ifdim\wd1>\cxxTypeWidth% + \hbox to \hsize{\unhbox5\hbox to \cxxTypeWidth{\unhbox1\hss}\hss}\\\nopagebreak% + \setbox5\hbox{\hskip\cxxIdWidth}% + \setbox1\hbox{\hskip\cxxTypeWidth}% + \else% + \setbox1\hbox{\hbox to \cxxTypeWidth{\unhbox1\hss}}% + \fi% + \ifdim\wd2>\cxxProtoWidth% + \ifdim\wd2<\cxxProtoMemoWidth% + \def\tmp{#4}% + \ifx\tmp\empty% + \def\tmp{#5}% + \ifx\tmp\empty% + \hbox to \hsize{\unhbox5\unhbox1\unhbox2\hss\cxxStrut}\\% + \else% + \hbox to \hsize{\unhbox5\unhbox1\unhbox2 + \dotfill\hbox to \cxxPageWidth{\hss\pageref{cxx.#5}}% + \cxxStrut}\\% + \fi% + \else% + \hbox to \hsize{\unhbox5\unhbox1\unhbox2\hss}\\\nopagebreak% + \hbox to \hsize{% + \hskip\cxxIdWidth% + \hskip\cxxTypeWidth% + \hskip\cxxProtoWidth% + \unhbox4\hss% + }\\% + \fi% + \else% + \hbox to \hsize{% + \unhbox5% + \unhbox1% + \parbox[t]{\cxxProtoMemoWidth}{% + \setbox255\hbox{\textbf{#2} (}% + \hangindent=\wd255\hangafter=1% + \raggedright\sloppy% + {\catcode`\&=4\catcode`\_=8% + \def{\ccverbatim \ccfinish}\textbf{#2} #3\strut}% + }\hss% + }\\\nopagebreak% + \hbox to \hsize{% + \hskip\cxxIdWidth% + \hskip\cxxTypeWidth% + \hskip\cxxProtoWidth% + \unhbox4\hss% + }\\% + \fi% + \else% + \hbox to \hsize{% + \unhbox5% + \unhbox1% + \hbox to \cxxProtoWidth{\unhbox2\hss}% + \unhbox4\hss% + }\\% + \fi% +}} + + +\newdimen\cxxtypestart +\cxxtypestart=0.05\textwidth +%\advance\cxxtypestart by \labelsep + +\newdimen\cxxnamestart +\cxxnamestart=\cxxtypestart +\advance\cxxnamestart by 0.25\textwidth + +\newdimen\cxxargsstart +\cxxargsstart=\cxxnamestart +\advance\cxxargsstart by 0.2\textwidth +\newdimen\cxxargswidth +\cxxargswidth=\textwidth +\advance\cxxargswidth by -\cxxargsstart +\advance\cxxargswidth by -\spaceskip + +\newdimen\cxxmemostart +\cxxmemostart=\cxxargsstart +\advance\cxxmemostart by 0.12\textwidth + +\newenvironment{cxxlist}[1]{ +\begingroup +\catcode`\,=\active% +\paragraph{#1}\strut\smallskip\\ +}{ +\endgroup +} + +\newenvironment{cxxnames}{\begin{cxxlist}{Names}}{\end{cxxlist}} +\newenvironment{cxxpublic}{\begin{cxxlist}{Public Members}}{\end{cxxlist}} +\newenvironment{cxxprivate}{\begin{cxxlist}{Private Members}}{\end{cxxlist}} +\newenvironment{cxxprotected}{\begin{cxxlist}{Protected Members}}{\end{cxxlist}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Numbered Frame box +% +\newlength{\cxxBoxLen}% +\newlength{\cxxBoxHt}% +\newlength{\cxxBoxDp}% +\newlength{\cxxSideHt}% +\newlength{\cxxSideLen}% +\newlength{\cxxTitleLen}% +\def\empty{} +\def\idPos{1cm} + +% +% box with id and name [optional width] +% +\newcommand{\aBox}[3][1pt]{{% +\small% +\def\width{#1}% +\def\num{#2}% +\def\name{#3}% +\setbox0\hbox{\hskip\width{ \strut\name\strut} \hskip\width}% +% +\setlength{\cxxBoxLen}{\wd0}% +\addtolength{\cxxBoxLen}{\width}% +\addtolength{\cxxBoxLen}{\width}% +\setbox2\hbox{\normalsize\strut\rule{\cxxBoxLen}{\width}}% +% +\ifx\num\empty% + \setbox1\hbox{\strut}% +\else% + \setbox1\hbox{\rule{\idPos}{\width} { \sf\bf\strut #2 } }% +\fi% +\addtolength{\cxxBoxLen}{-\wd1}% +\setbox1\hbox{\unhbox1\rule{\cxxBoxLen}{\width}}% +% +\setlength{\cxxSideHt}{\dp0}% +\addtolength{\cxxSideHt}{\width}% +\setlength{\cxxSideLen}{\dp0}% +\addtolength{\cxxSideLen}{\ht0}% +\addtolength{\cxxSideLen}{\dp1}% +\addtolength{\cxxSideLen}{\width}% +\addtolength{\cxxSideLen}{\width}% +\setbox3\hbox{\hbox to 0pt{\hss\rule[-\cxxSideHt]{\width}{\cxxSideLen}}}% +\setbox4\hbox{\hbox to 0pt{\rule[-\cxxSideHt]{\width}{\cxxSideLen}\hss}}% +% +\setlength{\cxxBoxHt}{\dp1}% +\addtolength{\cxxBoxHt}{1pt}% +\setlength{\cxxBoxDp}{\ht2}% +\addtolength{\cxxBoxDp}{-\width}% +\addtolength{\cxxBoxDp}{1pt}% +% +\setlength{\cxxBoxLen}{\ht3}% +\addtolength{\cxxBoxLen}{\ht1}% +% +\vbox to \cxxBoxLen{% +\hbox{\unhbox1}% +\vskip-\cxxBoxHt% +\hbox{\hskip\width\unhbox3\unhbox0\unhbox4}% +%\vskip-\ht2% +\vskip-\cxxBoxDp% +\hbox{\unhbox2}% +\vss% +}% +}} + +% +% box with id and name [optional width] of size #4 +% +\newcommand{\sizeBox}[4][3pt]{{% + \setbox0\hbox{ }% + \setlength{\cxxSideLen}{#4}% + \addtolength{\cxxSideLen}{-\wd0}% + \addtolength{\cxxSideLen}{-\wd0}% + \addtolength{\cxxSideLen}{-#1}% + \addtolength{\cxxSideLen}{-#1}% + \addtolength{\cxxSideLen}{-#1}% + \addtolength{\cxxSideLen}{-#1}% + \aBox[#1]{#2}{\hbox to \cxxSideLen{#3}}% +}} + +% +% centered box with id and name [optional width] of size #4 +% +\newcommand{\cBox}[4][3pt]{{% +\setbox0\hbox{\aBox[#1]{#2}{#3}}% +\ifdim\wd0<#4% + \sizeBox[#1]{#2}{\hss#3\hss}{#4}% +\else% + \setlength{\cxxSideLen}{\wd0}% + \advance\cxxSideLen by -#4% + \hskip -0.5\cxxSideLen% + \unhbox0% +\fi% +}} + +% +% right expanding box with id and name [optional width] of size at least #4 +% +\newcommand{\rBox}[4][3pt]{{% +\setbox0\hbox{\aBox[#1]{#2}{#3}\hss}% +\ifdim\wd0>#4% + \unhbox0% +\else% + \sizeBox[#1]{#2}{\hss#3\hss}{#4}% +\fi% +}} + +% +% left expanding box with id and name [optional width] of size at least #4 +% +\newcommand{\lBox}[4][3pt]{{% +\setbox0\hbox{\aBox[#1]{#2}{#3}\hss}% +\ifdim\wd0>#4% + \setlength{\cxxSideLen}{\wd0}% + \advance\cxxSideLen by -#4% + \hskip -\cxxSideLen% + \unhbox0% +\else% + \sizeBox[#1]{#2}{\hss#3\hss}{#4}% +\fi% +}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Pagestyle for documentation. +% +\newsavebox{\cxxHeadName} +\newcommand{\ps@docxx}{% + \renewcommand{\@oddhead}{\headlinetext}% + \renewcommand{\@evenhead}{\headlinetext}% + \renewcommand{\@oddfoot}{\footlinetext}% + \renewcommand{\@evenfoot}{\footlinetext}% +} +\newcommand{\makeHeadLine}[2]{ + \global\sbox{\cxxHeadName}{\vbox to 0pt{\vss% + \hbox to \textwidth{% + \hbox to 0pt{\strut\hbox to 1cm{\hss}\quad#1\hss}% + \hfil#2\hfil% + }% + \vskip 1pt% + \hbox to \textwidth{\hrulefill}% + }}% +} +\newcommand{\headlinetext}{\usebox{\cxxHeadName}} +\providecommand{\cxxCopyright}{% + \vtop{% + \hbox to \textwidth{{ + \tiny\sf This page was generated with the help of DOC++ + \hss}}% + \vskip -20pt + \hbox to \textwidth{ + \hbox{{\tiny\sf http://docpp.sourceforge.net}} + \hss}% + }% +} +\def\footlinetext{\hbox to \textwidth{ + \vtop{% + \hbox to \textwidth{\hrulefill}% + \vskip -20pt% + \cxxCopyright% + }% + \hss\vtop{\vskip 10pt\hbox{\today\hspace*{3cm}\textrm{\thepage} }} +}} +\pagestyle{docxx} + +\def\cxxTitle#1#2#3#4#5{\noindent{% + \thispagestyle{empty} + \vfill + \begin{center} + \Huge\bf + \catcode`\&=4% + \catcode`\_=8% + \def{\ccverbatim \ccfinish}#1% + \strut\\ + \def{\ccverbatim \ccfinish}#2% + \strut\\ + \def{\ccverbatim \ccfinish}#3% + \strut\\ + \end{center} + \if\cxxVersionStr\empty% + \else% + \begin{center} + \small\sf + --- Version \cxxVersionStr\ --- + \end{center} + \global\def\cxxVersionStr{} + \fi + \vfill + \large + \begin{center} + \Large\em + \def{\ccverbatim \ccfinish}#4% + \end{center} + \vfill + \if\cxxAuthorStr\empty% + \else% + \begin{center} + \sf\cxxAuthorStr + \end{center} + \global\def\cxxAuthorStr{} + \vfill + \fi + \pagebreak + \makeHeadLine{}{#2} +}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for contents +% +\newcounter{cxxContentsDepth} +\setcounter{cxxContentsDepth}{0} +\newdimen\cxxContentsLengthIncr +\cxxContentsLengthIncr=18pt +\newdimen\cxxContentsLength +\cxxContentsLength=\textwidth +\advance\cxxContentsLength by -\cxxContentsLengthIncr + +\makeHeadLine{}{Contents} + +\newenvironment{cxxContents}{ + \ifcase \value{cxxContentsDepth} + \vskip 40pt + \hbox to \hsize{\hskip 8pt\hskip\cxxContentsLengthIncr\Huge\bf Contents\hss} + \vskip 40pt + \bf + \else % >1 + \rm + \fi + \begingroup + \addtocounter{cxxContentsDepth}{1} + \advance\cxxContentsLengthIncr by 8pt + \advance\cxxContentsLength by -\cxxContentsLengthIncr +}{ + \addtocounter{cxxContentsDepth}{-1} + \advance\cxxContentsLength by \cxxContentsLengthIncr + \advance\cxxContentsLengthIncr by -8pt + \ifcase \value{cxxContentsDepth} + \vskip 12pt + \or + \vskip 9pt + \else + \vskip 3pt + \fi + \endgroup +} + +\newcommand{\cxxContentsEntry}[3]{{ + \def\emtpty{} + \def\memo{#3} + \ifx\memo\empty + \setbox0\hbox{\parbox[t]{\cxxContentsLength}{\strut#2 \dotfill }} + \else + \setbox0\hbox{\parbox[t]{\cxxContentsLength}{\strut#2 --- {\em #3} \dotfill }} + \fi + \setbox1\hbox{\vtop{\vskip\dp0\vskip-\ht0\vskip-1.5pt\hbox to 20pt{\hss\rm \pageref{cxx.#1}}}} + \hbox to \textwidth{% + \hss\hbox to \cxxContentsLengthIncr{#1\hss}% + \unhbox0\unhbox1% + } +}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for class graph +% +\newdimen\cxxClassGraphShift +\newdimen\cxxClassGraphLength +\newdimen\cxxClassGraphHeight +\newdimen\cxxClassGraphDepth +\newdimen\cxxClassGraphTotV +\setlength{\cxxClassGraphLength}{0.23\hsize} +\setlength{\cxxClassGraphShift}{30pt} +\setbox0\vbox{\aBox[5pt]{\strut}{\strut}} +\setlength{\cxxClassGraphHeight}{\ht0} +\setlength{\cxxClassGraphDepth}{\dp0} +\addtolength{\cxxClassGraphDepth}{10pt} +\setlength{\cxxClassGraphTotV}{\dp0} +\addtolength{\cxxClassGraphTotV}{\ht0} + +\newenvironment{cxxInheritance}{ + \par\medskip + \begingroup + \newcommand{\cxxCGSpace}[1]{% + \vtop to \cxxClassGraphTotV{\hbox to \cxxClassGraphShift{% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##1}}% + } + \newcommand{\cxxSlashHline}{% + \vrule width 0.1\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.1\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.1\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.1\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.1\cxxClassGraphShift height 3.5pt depth -3pt% + } + \newcommand{\cxxDotHline}{% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + \vrule width 0.05\cxxClassGraphShift height 0pt depth 0pt% + \vrule width 0.06\cxxClassGraphShift height 3.5pt depth -3pt% + } + \newcommand{\cxxHline}{% + \vrule width 0.5\cxxClassGraphShift height 3.5pt depth -3pt% + } + \newcommand{\cxxVup}{\hbox to 0pt{\hss% + \vrule width 0.5pt height \cxxClassGraphHeight depth -3pt% + \hss}} + \newcommand{\cxxVlow}{\hbox to 0pt{\hss% + \vtop to 0pt{\vskip-3pt% + \hbox{\vrule width 0.5pt height 13pt depth \cxxClassGraphDepth}% + \vss}% + \hss}} + \newcommand{\cxxLinkUp}{\hbox to 0pt{\hss\hskip 0.5\cxxClassGraphShift% + \raise0.7\cxxClassGraphHeight\hbox to 0pt{\hss\textbf{\symbol{94}}\hss}}}% + \newcommand{\cxxLinkDown}{\hbox to 0pt{\hss\hskip 0.5\cxxClassGraphShift% + \raise-0.6\cxxClassGraphHeight\vbox to 0pt{% + \hbox to 0pt{\hss\textbf{$\lor$}\hss}\vss}}}% + \newcommand{\cxxLinkLeft}{\hbox to 0pt{\hss\hskip 0.0\cxxClassGraphShift% + \raise0.0\cxxClassGraphHeight\hbox to 0pt{\textbf{\tt\<}\hss}}}% + \newcommand{\cxxLinkRight}{\hbox to 0pt{\hss\hskip 1.0\cxxClassGraphShift% + \raise0.0\cxxClassGraphHeight\hbox to 0pt{\hss\textbf{\tt\>}}}}% + \newcommand{\cxxInheritanceEntry}[5][]{ + \hbox to \hsize{\hss% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##2% + \def\tmp{##1}% + \ifx\tmp\empty% + \def\tmp{##5}% + \ifx\tmp\empty% + \rBox[1pt]{##3}{##4}{0.24\hsize}% + \else% + \lBox[1pt]{##3}{##4}{0.24\hsize}% + \fi% + \else% + \cBox[2pt]{##3}{##4}{0.24\hsize}% + \fi% + ##5\hfill% + }\vskip-1pt + } + \newcommand{\cxxNone}{\cxxCGSpace{\hss}} + \newcommand{\cxxLong}{\cxxCGSpace{\hss\cxxVup\cxxVlow\hss}} +% + \newcommand{\cxxPubLeft}{\cxxCGSpace{\cxxHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxProLeft}{\cxxCGSpace{\cxxSlashHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxPriLeft}{\cxxCGSpace{\cxxDotHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxPubleft}{\cxxCGSpace{\cxxHline\cxxVlow\hss}} + \newcommand{\cxxProleft}{\cxxCGSpace{\cxxSlashHline\cxxVlow\hss}} + \newcommand{\cxxPrileft}{\cxxCGSpace{\cxxDotHline\cxxVlow\hss}} + \newcommand{\cxxLastPubLeft}{\cxxCGSpace{\cxxHline% + \cxxVup\cxxLinkDown\cxxVlow\hss}} + \newcommand{\cxxLastProLeft}{\cxxCGSpace{\cxxSlashHline% + \cxxVup\cxxLinkDown\cxxVlow\hss}} + \newcommand{\cxxLastPriLeft}{\cxxCGSpace{\cxxDotHline% + \cxxVup\cxxLinkDown\cxxVlow\hss}} + \newcommand{\cxxLastPubleft}{\cxxCGSpace{\cxxHline% + \cxxVlow\cxxLinkDown\hss}} + \newcommand{\cxxLastProleft}{\cxxCGSpace{\cxxSlashHline% + \cxxVlow\cxxLinkDown\hss}} + \newcommand{\cxxLastPrileft}{\cxxCGSpace{\cxxDotHline% + \cxxVlow\cxxLinkDown\hss}} + \newcommand{\cxxLinkPubLeft}{\cxxCGSpace{\cxxLinkLeft\cxxHline\cxxVup\cxxVlow\hss}} + \newcommand{\cxxLinkProLeft}{\cxxCGSpace{\cxxLinkLeft\cxxSlashHline% + \cxxVup\cxxVlow\hss}} + \newcommand{\cxxLinkPriLeft}{\cxxCGSpace{\cxxLinkLeft\cxxDotHline% + \cxxVup\cxxVlow\hss}} + \newcommand{\cxxLinkPubleft}{\cxxCGSpace{\cxxLinkLeft\cxxHline\cxxVlow\hss}} + \newcommand{\cxxLinkProleft}{\cxxCGSpace{\cxxLinkLeft\cxxSlashHline\cxxVlow\hss}} + \newcommand{\cxxLinkPrileft}{\cxxCGSpace{\cxxLinkLeft\cxxDotHline\cxxVlow\hss}} +% + \newcommand{\cxxPubRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxHline}} + \newcommand{\cxxProRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxSlashHline}} + \newcommand{\cxxPriRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxDotHline}} + \newcommand{\cxxPubright}{\cxxCGSpace{\hss\cxxVup\cxxHline}} + \newcommand{\cxxProright}{\cxxCGSpace{\hss\cxxVup\cxxSlashHline}} + \newcommand{\cxxPriright}{\cxxCGSpace{\hss\cxxVup\cxxDotHline}} + + \newcommand{\cxxLinkPubRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow\cxxHline\cxxLinkRight}} + \newcommand{\cxxLinkProRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow% + \cxxSlashHline\cxxLinkRight}} + \newcommand{\cxxLinkPriRight}{\cxxCGSpace{\hss\cxxVup\cxxVlow% + \cxxDotHline\cxxLinkRight}} + \newcommand{\cxxLinkPubright}{\cxxCGSpace{\hss\cxxVup\cxxHline\cxxLinkRight}} + \newcommand{\cxxLinkProright}{\cxxCGSpace{\hss\cxxVup\cxxSlashHline\cxxLinkRight}} + \newcommand{\cxxLinkPriright}{\cxxCGSpace{\hss\cxxVup\cxxDotHline\cxxLinkRight}} + + \newcommand{\cxxFirstPubRight}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxVlow\cxxHline}} + \newcommand{\cxxFirstProRight}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxVlow% + \cxxSlashHline}} + \newcommand{\cxxFirstPriRight}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxVlow\cxxDotHline}} + \newcommand{\cxxFirstPubright}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxHline}} + \newcommand{\cxxFirstProright}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxSlashHline}} + \newcommand{\cxxFirstPriright}{\cxxCGSpace{\hss\cxxVup\cxxLinkUp\cxxDotHline}} +}{ + \endgroup +} + +\newenvironment{cxxClassGraph}{ + \begin{cxxInheritance} + \newcommand{\cxxClassGraphEntry}[4]{ + \hbox to \hsize{\hss% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##1% + \def\tmp{##4}% + \rBox[1pt]{##2}{##3}{0.24\hsize}% + ##4% + \hskip\cxxClassGraphShift\dotfill% + \hbox to \cxxClassGraphShift{\hss\pageref{cxx.##2}}% + }\vskip-1pt + } + \newcommand{\cxxClassGraphEntryUnknownPackage}[3]{ + \hbox to \hsize{\hss% + \vrule width0pt height \cxxClassGraphHeight depth \cxxClassGraphDepth% + ##1% + \def\tmp{##3}% + \rBox[1pt]{}{##2}{0.24\hsize}% + ##3% + \hskip\cxxClassGraphShift\hfill% + }\vskip-1pt + } + \clearpage + \pagebreak\strut + \makeHeadLine{}{Class Graph} + \vskip 20pt + \hbox to \hsize{\Huge\bf \quad Class Graph\hss} + \vskip 40pt +} +{ + \end{cxxInheritance} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for generic manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newdimen\cxxgenericstart +\cxxgenericstart=\labelwidth +\advance\cxxgenericstart by \labelsep +\newdimen\cxxgenericwidth +\cxxgenericwidth=\textwidth +\advance\cxxgenericwidth by -\cxxgenericstart + +\newcounter{cxxDepth} +\setcounter{cxxDepth}{0} + +\newlength{\cxxSize} +\newenvironment{cxxgeneric}[5]{ +% +% some local definitions +% + \def\empty{} + \def\type{#1} + \def\args{#3} + \def\memo{#4} + \def\id {#5} +% +% pagebreak ? +% +\ifcase \value{cxxDepth} + \clearpage % 0 + \pagebreak + \makeHeadLine{#5}{#2} + \setlength{\cxxSize}{2pt} +\or % 1 + \strut\bigskip\bigskip\goodbreak% + \setlength{\cxxSize}{1pt} +\else % >2 + \strut\bigskip\bigskip\goodbreak% + \setlength{\cxxSize}{0.5pt} +\fi +\addtocounter{cxxDepth}{1} +% +% write synopsis +% +\setbox0\hbox{ }% +\setbox1\hbox{\strut\large #1 {\bf#2} }% +\setbox3\hbox{\strut\large #1 {\bf#2} #3}% +% +% box with id and name [optional width] over entire page width +% +\setlength{\cxxSideLen}{\hsize}% +\addtolength{\cxxSideLen}{-4\cxxSize}% +\addtolength{\cxxSideLen}{-2\wd0}% +\setlength{\cxxTitleLen}{\cxxSideLen}% +\addtolength{\cxxTitleLen}{-8\wd0}% +\hbox{\aBox[\cxxSize]{\id}{\vbox{\vskip 1.5\parskip% + \hbox to \cxxSideLen{\strut% + \hbox to 4\wd0{}% + \ifdim\wd3<\cxxTitleLen% + \parbox[b]{\cxxTitleLen}{% + \begin{raggedright} + \noindent\large #1 {\bf#2} #3 + \end{raggedright} + } + \else% + \ifdim\wd1>0.7\cxxTitleLen% + \parbox[b]{\cxxTitleLen}{% + \begin{raggedright} + \noindent\large #1 {\bf#2} #3 + \end{raggedright} + } + \else% + \addtolength{\cxxTitleLen}{-\wd1}% + \unhbox1% + \parbox[t]{\cxxTitleLen}{% + \advance\lineskip 7pt% + \begin{raggedright} + \noindent\large\strut #3 + \end{raggedright} + } + \fi% + \fi% + \hss\strut% + }\vskip\parskip}% +}} +% \parbox[b]{\cxxSideLen}{\begingroup +% \catcode`\&=12% +% \catcode`\_=12% +% \begin{flushleft} +% \quad\large% +% \ifx\type\empty +% \ifx\args\empty +% \strut{\bf #2}\\ +% \else +% \strut{\bf #2}\ \args +% \fi +% \else +% \strut\type\ {\bf #2}\ \args +% \fi +% \end{flushleft} +% \endgroup}}}% +\ifx\memo\empty\else + \vskip 10pt + \begin{flushright} + \it\memo + \end{flushright} +\fi +\label{cxx.\id} +\begingroup +\def\cxxExceptionsStr{} +\def\cxxParameterStr{} +\def\cxxReturnStr{} +\def\cxxInvariantsStr{} +\def\cxxPreconditionsStr{} +\def\cxxPostconditionsStr{} +\def\cxxSeeStr{} +\def\cxxAuthorStr{} +\def\cxxVersionStr{} +\def\cxxDeprecatedStr{} +\def\cxxSinceStr{} +\def\cxxFileStr{} +\def\cxxExceptions##1{\def\cxxExceptionsStr{##1}} +\def\cxxParameter##1{\def\cxxParameterStr{##1}} +\def\cxxReturn##1{\def\cxxReturnStr{##1}} +\def\cxxInvariants##1{\def\cxxInvariantsStr{##1}} +\def\cxxPreconditions##1{\def\cxxPreconditionsStr{##1}} +\def\cxxPostconditions##1{\def\cxxPostconditionsStr{##1}} +\def\cxxSee##1{\def\cxxSeeStr{##1}} +\def\cxxAuthor##1{\def\cxxAuthorStr{##1}} +\def\cxxVersion##1{\def\cxxVersionStr{##1}} +\def\cxxDeprecated##1{\def\cxxDeprecatedStr{##1}} +\def\cxxSince##1{\def\cxxSinceStr{##1}} +\def\cxxFile##1{\def\cxxFileStr{##1}} +}{ +\endgroup +\addtocounter{cxxDepth}{-1} +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for function manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxfunction}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Arguments}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxentry}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for union manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxunion}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Members}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for typedef manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxtypedef}[5]{ +\begin{cxxgeneric}{#1}{#2}{}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Members}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for macro manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxmacro}[5]{ +\begin{cxxgeneric}{\#define}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for class manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxclass}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for namespace manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxnamespace}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for interface manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxinterface}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5}}{\end{cxxgeneric}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for variable manual entries +% arguments are: +% #1 type +% #2 name +% #3 args +% #4 memo +% #5 number +% +\newenvironment{cxxvariable}[5]{ +\begin{cxxgeneric}{#1}{#2}{#3}{#4}{#5} +\renewenvironment{cxxnames}{\begin{cxxlist}{Names}}{\end{cxxlist}} +}{ +\end{cxxgeneric} +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for documentation +% +\newenvironment{cxxdocumentation}{ +% +% switch on special characters for documentation section +% +\begingroup +\catcode`\&=4 +\catcode`\_=8 +}{ +\endgroup +} + +\newenvironment{cxxdoc}{ +% +% switch on special characters for documentation section +% +\begin{cxxdocumentation} +\strut\\\noindent% +}{ +\smallskip +\def\empty{}% +\ifx\cxxReturnStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Return Value:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxReturnStr}\\ +\fi +\ifx\cxxParameterStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Parameters:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxParameterStr}\\ +\fi +\ifx\cxxExceptionsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Exceptions:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxExceptionsStr}\\ +\fi +\ifx\cxxInvariantsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Invariants:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxInvariantsStr}\\ +\fi +\ifx\cxxPreconditionsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Preconditions:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxPreconditionsStr}\\ +\fi +\ifx\cxxPostconditionsStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Postconditions:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxPostconditionsStr}\\ +\fi +\ifx\cxxSeeStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf See Also:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxSeeStr}\\ +\fi +\ifx\cxxAuthorStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Author:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxAuthorStr}\\ +\fi +\ifx\cxxVersionStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Version:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxVersionStr}\\ +\fi +\ifx\cxxDeprecatedStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf\it Deprecated:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxDeprecatedStr}\\ +\fi +\ifx\cxxSinceStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf Since:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxSinceStr}\\ +\fi +\ifx\cxxFileStr\empty\else + \noindent\hbox to 0.3\textwidth{{\bf File:}\hss}% + \parbox[t]{0.7\textwidth}{\cxxFileStr}\\ +\fi +\end{cxxdocumentation} +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% environment for commented listing +% arguments are: +% #1 Section title +% +\newenvironment{cxximplementation}[1]{ +\goodbreak +\begin{cxxdocumentation} +}{ +\end{cxxdocumentation} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\def\cxxCodeLine#1{ +\strut\hbox to 20pt{\tiny\hss #1}\small +%\advance\leftmargin by 20pt +%\advance\textwidth by -20pt +\ccverbatim \parskip=0pt \cxxCodeFinish} +{\catcode`\=0 catcode`\=12 +gdefcxxCodeFinish#1{hbox{#1}endgroup}} diff --git a/libupnp/upnp/doc/intro.dxx b/libupnp/upnp/doc/intro.dxx new file mode 100644 index 0000000..7c1240b --- /dev/null +++ b/libupnp/upnp/doc/intro.dxx @@ -0,0 +1,17 @@ +/**@name Introduction + * This document gives a brief description of the Linux SDK for UPnP + * Devices API. Section 1 covers the license under which the SDK is + * distributed. Section 2 talks about the callback functions used + * in many parts of the API. Finally, section 3 details the structures and + * functions that comprise the API. + * + * The Linux SDK for UPnP Devices version 1.2 supports the following + * platforms: + * \begin{itemize} + * \item Linux* running on an Intel Architecture processor + * \item Linux running on an Intel StrongARM or XScale processor + * \end{itemize} + * + * {\bf *} Other brands and names are the property of their respective + * owners. + */ diff --git a/libupnp/upnp/doc/license.dxx b/libupnp/upnp/doc/license.dxx new file mode 100644 index 0000000..88b21cf --- /dev/null +++ b/libupnp/upnp/doc/license.dxx @@ -0,0 +1,32 @@ +/**@name License + * + * \begin{center} + * Copyright (c) 2000-2003 Intel Corporation + * All rights reserved. + * \end{center} + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * \begin{itemize} + * \item Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * \item Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * \item Neither name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * \end{itemize} + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ diff --git a/libupnp/upnp/doc/upnpsdk.dxx b/libupnp/upnp/doc/upnpsdk.dxx new file mode 100644 index 0000000..cb46c57 --- /dev/null +++ b/libupnp/upnp/doc/upnpsdk.dxx @@ -0,0 +1,20 @@ +/**@name Linux SDK for UPnP Devices v1.2 + * + *\begin{center} + * {\bf Linux SDK for UPnP Devices Version 1.2} + * + * Copyright (C) 2000-2003 Intel Corporation ALL RIGHTS RESERVED + * + * Revision 1.2.1 (\Date) + *\end{center} + */ +//@{ + + //@Include: intro.dxx + //@Include: license.dxx + //@Include: callback.dxx + //@Include: ../inc/upnp.h + //@Include: ../inc/upnptools.h + //@Include: ../inc/config.h + +//@} diff --git a/libupnp/upnp/inc/upnp.h b/libupnp/upnp/inc/upnp.h new file mode 100644 index 0000000..0b71940 --- /dev/null +++ b/libupnp/upnp/inc/upnp.h @@ -0,0 +1,2761 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UPNP_H +#define UPNP_H + +/** @name The API */ + +//@{ + +#include +#include "ixml.h" +#include "upnpconfig.h" +#if UPNP_HAVE_DEBUG +# include "upnpdebug.h" +#endif + +#define UpnpCloseSocket close +#define UPNP_SOCKETERROR -1 +#define UPNP_INVALID_SOCKET -1 +#define SOCKET int + +#include + +#define LINE_SIZE 180 +#define NAME_SIZE 256 +#define MNFT_NAME_SIZE 64 +#define MODL_NAME_SIZE 32 +#define SERL_NUMR_SIZE 64 +#define MODL_DESC_SIZE 64 +#define UPNP_INFINITE -1 + +#define UPNP_USING_CHUNKED -3 +#define UPNP_UNTIL_CLOSE -4 + + +/** @name Error codes + * The functions in the SDK API can return a variety of error + * codes to describe problems encountered during execution. This section + * lists the error codes and provides a brief description of what each error + * code means. Refer to the documentation for each function for a + * description of what an error code means in that context. + */ +//@{ + +/** @name UPNP_E_SUCCESS [0] + * {\tt UPNP_E_SUCCESS} signifies that the operation completed successfully. + * For asynchronous functions, this only means that the packet generated by + * the operation was successfully transmitted on the network. The result of + * the entire operation comes as part of the callback for that operation. + */ +//@{ +#define UPNP_E_SUCCESS 0 +//@} + +/** @name UPNP_E_INVALID_HANDLE [-100] + * {\tt UPNP_E_INVALID_HANDLE} signifies that the handle passed to a + * function is not a recognized as a valid handle. + */ +//@{ +#define UPNP_E_INVALID_HANDLE -100 +//@} + +/** @name UPNP_E_INVALID_PARAM [-101] + * {\tt UPNP_E_INVALID_PARAM} signifies that one or more of the parameters + * passed to the function is not valid. Refer to the documentation for each + * function for more information on the valid ranges of the parameters. + */ +//@{ +#define UPNP_E_INVALID_PARAM -101 +//@} + +/** @name UPNP_E_OUTOF_HANDLE [-102] + * {\tt UPNP_E_OUTOF_HANDLE} signifies that the SDK does not have any + * more space for additional handles. The SDK allocates space for only + * a few handles in order to conserve memory. + */ +//@{ +#define UPNP_E_OUTOF_HANDLE -102 +//@} + +#define UPNP_E_OUTOF_CONTEXT -103 + +/** @name UPNP_E_OUTOF_MEMORY [-104] + * {\tt UPNP_E_OUTOF_MEMORY} signifies that not enough resources are + * currently available to complete the operation. Most operations require + * some free memory in order to complete their work. + */ +//@{ +#define UPNP_E_OUTOF_MEMORY -104 +//@} + +/** @name UPNP_E_INIT [-105] + * {\tt UPNP_E_INIT} signifies that the SDK has already been + * initialized. The SDK needs to be initialied only once per process. + * Any additional initialization attempts simply return this error with + * no other ill effects. + */ +//@{ +#define UPNP_E_INIT -105 +//@} + +#define UPNP_E_BUFFER_TOO_SMALL -106 + +/** @name UPNP_E_INVALID_DESC [-107] + * {\tt UPNP_E_INVALID_DESC} signifies that the description document passed + * to {\bf UpnpRegisterRootDevice} or {\bf UpnpRegisterRootDevice2} is an + * invalid description document. + */ +//@{ +#define UPNP_E_INVALID_DESC -107 +//@} + +/** @name UPNP_E_INVALID_URL [-108] + * {\tt UPNP_E_INVALID_URL} signifies that a URL passed into the function + * is invalid. The actual cause is function specific, but in general, the + * URL itself might be malformed (e.g. have invalid characters in it) or + * the host might be unreachable. + */ +//@{ +#define UPNP_E_INVALID_URL -108 +//@} + +#define UPNP_E_INVALID_SID -109 +#define UPNP_E_INVALID_DEVICE -110 + +/** @name UPNP_E_INVALID_SERVICE [-111] + * {\tt UPNP_E_INVALID_SERVICE} is returned only by {\bf UpnpNotify}, + * {\bf UpnpNotifyExt}, {\bf UpnpAcceptSubscription}, and + * {\bf UpnpAcceptSubscriptionExt} to signify that the device ID/service + * ID pair does not refer to a valid service. + */ +//@{ +#define UPNP_E_INVALID_SERVICE -111 +//@} + +/** @name UPNP_E_BAD_RESPONSE [-113] + * {\tt UPNP_E_BAD_RESPONSE} signifies that the response received from the + * remote side of a connection is not correct for the protocol. This applies + * to the GENA, SOAP, and HTTP protocols. + */ +//@{ +#define UPNP_E_BAD_RESPONSE -113 +//@} + +#define UPNP_E_BAD_REQUEST -114 + +/** @name UPNP_E_INVALID_ACTION [-115] + * {\tt UPNP_E_INVALID_ACTION} signifies that the SOAP action message is + * invalid. This can be because the DOM document passed to the function was + * malformed or the action message is not correct for the given action. + */ +//@{ +#define UPNP_E_INVALID_ACTION -115 +//@} + +/** @name UPNP_E_FINISH [-116] + * {\tt UPNP_E_FINISH} signifies that {\bf UpnpInit} has not been called, or + * that {\bf UpnpFinish} has already been called. None of the API functions + * operate until {\bf UpnpInit} successfully completes. + */ +//@{ +#define UPNP_E_FINISH -116 +//@} + +/** @name UPNP_E_INIT_FAILED [-117] + * {\tt UPNP_E_INIT_FAILED} signifies that {\bf UpnpInit} cannot complete. + * The typical reason is failure to allocate sufficient resources. + */ +//@{ +#define UPNP_E_INIT_FAILED -117 +//@} + +/** @name UPNP_E_URL_TOO_BIG [-118] + * {\tt UPNP_E_URL_TOO_BIG} signifies that the URL passed into a function + * is too long. The SDK limits URLs to 180 characters in length. + */ +#define UPNP_E_URL_TOO_BIG -118 + +/** @name UPNP_E_BAD_HTTPMSG [-119] + * {\tt UPNP_E_BAD_HTTPMSG} signifies that the HTTP message contains invalid + * message headers. The error always refers to the HTTP message header + * received from the remote host. The main areas where this occurs are in + * SOAP control messages (e.g. {\bf UpnpSendAction}), GENA subscription + * message (e.g. {\bf UpnpSubscribe}), GENA event notifications (e.g. {\bf + * UpnpNotify}), and HTTP transfers (e.g. {\bf UpnpDownloadXmlDoc}). + */ +//@{ +#define UPNP_E_BAD_HTTPMSG -119 +//@} + +/** @name UPNP_E_ALREADY_REGISTERED [-120] + * {\tt UPNP_E_ALREADY_REGISTERED} signifies that a client or a device is + * already registered. The SDK currently has a limit of one registered + * client and one registered device per process. + */ +//@{ +#define UPNP_E_ALREADY_REGISTERED -120 +//@} + +/** @name UPNP_E_NETWORK_ERROR [-200] + * {\tt UPNP_E_NETWORK_ERROR} signifies that a network error occurred. It + * is the generic error code for network problems that are not covered under + * one of the more specific error codes. The typical meaning is the SDK + * failed to read the local IP address or had problems configuring one of + * the sockets. + */ +//@{ +#define UPNP_E_NETWORK_ERROR -200 +//@} + +/** @name UPNP_E_SOCKET_WRITE [-201] + * {\tt UPNP_E_SOCKET_WRITE} signifies an error writing to a socket. This + * occurs in any function that makes network connections, such + * as discovery (e.g. {\bf UpnpSearchAsync} or {\bf UpnpSendAdvertisement}), + * control (e.g. {\bf UpnpSendAction}), eventing (e.g. {\bf UpnpNotify}), + * and HTTP functions (e.g. {\bf UpnpDownloadXmlDoc}). + */ +//@{ +#define UPNP_E_SOCKET_WRITE -201 +//@} + +/** @name UPNP_E_SOCKET_READ [-202] + * {\tt UPNP_E_SOCKET_READ} signifies an error reading from a socket. This + * occurs in any function that makes network connections, such + * as discovery (e.g. {\bf UpnpSearchAsync} or {\bf UpnpSendAdvertisement}), + * control (e.g. {\bf UpnpSendAction}), eventing (e.g. {\bf UpnpNotify}), + * and HTTP functions (e.g. {\bf UpnpDownloadXmlDoc}). + */ +//@{ +#define UPNP_E_SOCKET_READ -202 +//@} + +/** @name UPNP_E_SOCKET_BIND [-203] + * {\tt UPNP_E_SOCKET_BIND} signifies that the SDK had a problem binding + * a socket to a network interface. This occurs in any function that makes + * network connections, such as discovery (e.g. {\bf UpnpSearchAsync} or + * {\bf UpnpSendAdvertisement}), control (e.g. {\bf UpnpSendAction}), eventing + * (e.g. {\bf UpnpNotify}), and HTTP functions (e.g. + * {\bf UpnpDownloadXmlDoc}). + */ +//@{ +#define UPNP_E_SOCKET_BIND -203 +//@} + +/** @name UPNP_E_SOCKET_CONNECT [-204] + * {\tt UPNP_E_SOCKET_CONNECT} signifies that the SDK had a problem + * connecting to a remote host. This occurs in any function that makes + * network connections, such as discovery (e.g. {\bf UpnpSearchAsync} or + * {\bf UpnpSendAdvertisement}), control (e.g. {\bf UpnpSendAction}), eventing + * (e.g. {\bf UpnpNotify}), and HTTP functions (e.g. + * {\bf UpnpDownloadXmlDoc}). + */ +//@{ +#define UPNP_E_SOCKET_CONNECT -204 +//@} + +/** @name UPNP_E_OUTOF_SOCKET [-205] + * {\tt UPNP_E_OUTOF_SOCKET} signifies that the SDK cannot create any + * more sockets. This occurs in any function that makes + * network connections, such as discovery (e.g. {\bf UpnpSearchAsync} or + * {\bf UpnpSendAdvertisement}), control (e.g. {\bf UpnpSendAction}), eventing + * (e.g. {\bf UpnpNotify}), and HTTP functions (e.g. + * {\bf UpnpDownloadXmlDoc}). + */ +//@{ +#define UPNP_E_OUTOF_SOCKET -205 +//@} + +/** @name UPNP_E_LISTEN [-206] + * {\tt UPNP_E_LISTEN} signifies that the SDK had a problem setting the + * socket to listen for incoming connections. This error only happens during + * initialization (i.e. {\bf UpnpInit}). + */ +//@{ +#define UPNP_E_LISTEN -206 +//@} + +/** @name UPNP_E_TIMEDOUT [-207] + * {\tt UPNP_E_TIMEDOUT} signifies that too much time elapsed before the + * required number of bytes were sent or received over a socket. This error + * can be returned by any function that performs network operations. + */ +//@{ +#define UPNP_E_TIMEDOUT -207 +//@} + +/** @name UPNP_E_SOCKET_ERROR [-208] + * {\tt UPNP_E_SOCKET_ERROR} is the generic socket error code for + * conditions not covered by other error codes. This error can be returned + * by any function that performs network operations. + */ +//@{ +#define UPNP_E_SOCKET_ERROR -208 +//@} + +#define UPNP_E_FILE_WRITE_ERROR -209 + +/** @name UPNP_E_CANCELED [-210] + * {\tt UPNP_E_CANCELED} signifies that the operation was canceled. This + * error can be returned by any function that allows for external + * cancelation. + */ +//@{ +#define UPNP_E_CANCELED -210 +//@} + +#define UPNP_E_EVENT_PROTOCOL -300 + +/** @name UPNP_E_SUBSCRIBE_UNACCEPTED [-301] + * {\tt UPNP_E_SUBSCRIBE_UNACCEPTED} signifies that a subscription + * request was rejected from the remote side. + */ +//@{ +#define UPNP_E_SUBSCRIBE_UNACCEPTED -301 +//@} + +/** @name UPNP_E_UNSUBSCRIBE_UNACCAPTED [-302] + * {\tt UPNP_E_UNSUBSCRIBE_UNACCEPTED} signifies that an unsubscribe + * request was rejected from the remote side. + */ +//@{ +#define UPNP_E_UNSUBSCRIBE_UNACCEPTED -302 +//@} + +/** @name UPNP_E_NOTIFY_UNACCEPTED [-303] + * {\tt UPNP_E_NOTIFY_UNACCEPTED} signifies that the remote host did not + * accept the notify sent from the local device. + */ +//@{ +#define UPNP_E_NOTIFY_UNACCEPTED -303 +//@} + +/** @name UPNP_E_INVALID_ARGUMENT [-501] + * {\tt UPNP_E_INVALID_ARGUMENT} signifies that one or more of the parameters + * passed to a function is invalid. Refer to the individual function + * descriptions for the acceptable ranges for parameters. + */ +//@{ +#define UPNP_E_INVALID_ARGUMENT -501 +//@} + +/** @name UPNP_E_FILE_NOT_FOUND [-502] + * {\tt UPNP_E_FILE_NOT_FOUND} signifies that the filename passed + * to one of the device registration functions was not found or was not + * accessible. + */ +//@{ +#define UPNP_E_FILE_NOT_FOUND -502 +//@} + +/** @name UPNP_E_FILE_READ_ERROR [-503] + * {\tt UPNP_E_FILE_READ_ERROR} signifies an error when reading a file. + */ +//@{ +#define UPNP_E_FILE_READ_ERROR -503 +//@} + +/** @name UPNP_E_EXT_NOT_XML [-504] + * {\tt UPNP_E_EXT_NOT_XML} signifies that the file name of the description + * document passed to {\bf UpnpRegisterRootDevice2} does not end in ".xml". + */ +//@{ +#define UPNP_E_EXT_NOT_XML -504 +//@} + +#define UPNP_E_NO_WEB_SERVER -505 +#define UPNP_E_OUTOF_BOUNDS -506 + +/** @name UPNP_E_NOT_FOUND [-507] + * {\tt UPNP_E_NOT_FOUND} signifies that the response to a SOAP request + * did not contain the required XML constructs. + */ +//@{ +#define UPNP_E_NOT_FOUND -507 +//@} + +/** @name UPNP_E_INTERNAL_ERROR [-911] + * {\tt UPNP_E_INTERNAL_ERROR} is the generic error code for internal + * conditions not covered by other error codes. + */ +//@{ +#define UPNP_E_INTERNAL_ERROR -911 +//@} + +// SOAP-related error codes +#define UPNP_SOAP_E_INVALID_ACTION 401 +#define UPNP_SOAP_E_INVALID_ARGS 402 +#define UPNP_SOAP_E_OUT_OF_SYNC 403 +#define UPNP_SOAP_E_INVALID_VAR 404 +#define UPNP_SOAP_E_ACTION_FAILED 501 + +//@} + +#ifndef OUT +#define OUT +#endif + +#ifndef IN +#define IN +#endif + +#ifndef INOUT +#define INOUT +#endif + +enum UpnpOpenFileMode{UPNP_READ, UPNP_WRITE}; + +/// @name Constants, Structures, and Types +//@{ + +/** Returned when a control point application registers with {\bf + * UpnpRegisterClient}. Client handles can only be used with + * functions that operate with a client handle. */ + +typedef int UpnpClient_Handle; + +/** Returned when a device application registers with {\bf + * UpnpRegisterRootDevice} or {\bf UpnpRegisterRootDevice2}. Device handles + * can only be used with functions that operate with a device handle. */ + +typedef int UpnpDevice_Handle; + +/** @name UPnP_EventType + @memo The reason code for an event callback. + @doc The {\bf Event} parameter will be different depending on the + reason for the callback. The descriptions for each event + type describe the contents of the {\bf Event} parameter. + */ + +enum Upnp_EventType_e { + + // + // Control callbacks + // + + /** Received by a device when a control point issues a control + * request. The {\bf Event} parameter contains a pointer to a {\bf + * Upnp_Action_Request} structure containing the action. The application + * stores the results of the action in this structure. */ + + UPNP_CONTROL_ACTION_REQUEST, + + /** A {\bf UpnpSendActionAsync} call completed. The {\bf Event} + * parameter contains a pointer to a {\bf Upnp_Action_Complete} structure + * with the results of the action. */ + + UPNP_CONTROL_ACTION_COMPLETE, + + /** Received by a device when a query for a single service variable + * arrives. The {\bf Event} parameter contains a pointer to a {\bf + * Upnp_State_Var_Request} structure containing the name of the variable + * and value. */ + + UPNP_CONTROL_GET_VAR_REQUEST, + + /** A {\bf UpnpGetServiceVarStatus} call completed. The {\bf Event} + * parameter contains a pointer to a {\bf Upnp_State_Var_Complete} structure + * containing the value for the variable. */ + + UPNP_CONTROL_GET_VAR_COMPLETE, + + // + // Discovery callbacks + // + + /** Received by a control point when a new device or service is available. + * The {\bf Event} parameter contains a pointer to a {\bf + * Upnp_Discovery} structure with the information about the device + * or service. */ + + UPNP_DISCOVERY_ADVERTISEMENT_ALIVE, + + /** Received by a control point when a device or service shuts down. The {\bf + * Event} parameter contains a pointer to a {\bf Upnp_Discovery} + * structure containing the information about the device or + * service. */ + + UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE, + + /** Received by a control point when a matching device or service responds. + * The {\bf Event} parameter contains a pointer to a {\bf + * Upnp_Discovery} structure containing the information about + * the reply to the search request. */ + + UPNP_DISCOVERY_SEARCH_RESULT, + + /** Received by a control point when the search timeout expires. The + * SDK generates no more callbacks for this search after this + * event. The {\bf Event} parameter is {\tt NULL}. */ + + UPNP_DISCOVERY_SEARCH_TIMEOUT, + + // + // Eventing callbacks + // + + /** Received by a device when a subscription arrives. + * The {\bf Event} parameter contains a pointer to a {\bf + * Upnp_Subscription_Request} structure. At this point, the + * subscription has already been accepted. {\bf UpnpAcceptSubscription} + * needs to be called to confirm the subscription and transmit the + * initial state table. This can be done during this callback. The SDK + * generates no events for a subscription unless the device + * application calls {\bf UpnpAcceptSubscription}. + */ + + UPNP_EVENT_SUBSCRIPTION_REQUEST, + + /** Received by a control point when an event arrives. The {\bf + * Event} parameter contains a {\bf Upnp_Event} structure + * with the information about the event. */ + + UPNP_EVENT_RECEIVED, + + /** A {\bf UpnpRenewSubscriptionAsync} call completed. The status of + * the renewal is in the {\bf Event} parameter as a {\bf + * Upnp_Event_Subscription} structure. */ + + UPNP_EVENT_RENEWAL_COMPLETE, + + /** A {\bf UpnpSubscribeAsync} call completed. The status of the + * subscription is in the {\bf Event} parameter as a {\bf + * Upnp_Event_Subscription} structure. */ + + UPNP_EVENT_SUBSCRIBE_COMPLETE, + + /** A {\bf UpnpUnSubscribeAsync} call completed. The status of the + * subscription is in the {\bf Event} parameter as a {\bf + * Upnp_Event_Subscribe} structure. */ + + UPNP_EVENT_UNSUBSCRIBE_COMPLETE, + + /** The auto-renewal of a client subscription failed. + * The {\bf Event} parameter is a {\bf Upnp_Event_Subscribe} structure + * with the error code set appropriately. The subscription is no longer + * valid. */ + + UPNP_EVENT_AUTORENEWAL_FAILED, + + /** A client subscription has expired. This will only occur + * if auto-renewal of subscriptions is disabled. + * The {\bf Event} parameter is a {\bf Upnp_Event_Subscribe} + * structure. The subscription is no longer valid. */ + + UPNP_EVENT_SUBSCRIPTION_EXPIRED + +}; + +typedef enum Upnp_EventType_e Upnp_EventType; + +/** The {\bf Upnp_SID} holds the subscription identifier for a subscription + between a client and a device. The SID is a string representation of + a globally unique id (GUID) and should not be modified. + */ + +typedef char Upnp_SID[44]; + +/** @name Upnp_SType + @memo Represents the different types of searches that + can be performed using the SDK for UPnP Devices API. + @doc By specifying these different values to + {\bf UpnpSearchAsync}, the control point application + can control the scope of the search from all devices + to specific devices or services. + */ + +enum Upnp_SType_e { + + /** Search for all devices and services on the network. */ + UPNP_S_ALL, + + /** Search for all root devices on the network. */ + UPNP_S_ROOT, + + /** Search for a particular device type or a particular device + instance. */ + UPNP_S_DEVICE, + + /** Search for a particular service type, possibly on a particular + * device type or device instance. */ + UPNP_S_SERVICE + +}; + +typedef enum Upnp_SType_e Upnp_SType; + +/** @name Upnp_DescType + @memo Specifies the type of description in + {\bf UpnpRegisterRootDevice2}. + @doc These values control how {\bf UpnpRegisterRootDevice2} + interprets the {\bf description} parameter. + */ +enum Upnp_DescType_e { + + /** The description is the URL to the description document. */ + UPNPREG_URL_DESC, + + /** The description is a file name on the local file system + containing the description of the device. */ + UPNPREG_FILENAME_DESC, + + /** The description is a pointer to a character array containing + the XML description document. */ + UPNPREG_BUF_DESC + +}; + +typedef enum Upnp_DescType_e Upnp_DescType; + +/** Returned as part of a {\bf UPNP_CONTROL_ACTION_COMPLETE} callback. */ + +struct Upnp_Action_Request +{ + /** The result of the operation. */ + int ErrCode; + + /** The socket number of the connection to the requestor. */ + int Socket; + + /** The error string in case of error. */ + char ErrStr[LINE_SIZE]; + + /** The Action Name. */ + char ActionName[NAME_SIZE]; + + /** The unique device ID. */ + char DevUDN[NAME_SIZE]; + + /** The service ID. */ + char ServiceID[NAME_SIZE]; + + /** The DOM document describing the action. */ + IXML_Document *ActionRequest; + + /** The DOM document describing the result of the action. */ + IXML_Document *ActionResult; + + /** IP address of the control point requesting this action. */ + struct in_addr CtrlPtIPAddr; + + /** The DOM document containing the information from the + the SOAP header. */ + IXML_Document *SoapHeader; +}; + +struct Upnp_Action_Complete +{ + /** The result of the operation. */ + int ErrCode; + + /** The control URL for service. */ + char CtrlUrl[NAME_SIZE]; + + /** The DOM document describing the action. */ + IXML_Document *ActionRequest; + + /** The DOM document describing the result of the action. */ + IXML_Document *ActionResult; + +}; + +/** Represents the request for current value of a state variable in a service + * state table. */ + +struct Upnp_State_Var_Request +{ + /** The result of the operation. */ + int ErrCode; + + /** The socket number of the connection to the requestor. */ + int Socket; + + /** The error string in case of error. */ + char ErrStr[LINE_SIZE]; + + /** The unique device ID. */ + char DevUDN[NAME_SIZE]; + + /** The service ID. */ + char ServiceID[NAME_SIZE]; + + /** The name of the variable. */ + char StateVarName[NAME_SIZE]; + + /** IP address of sender requesting the state variable. */ + struct in_addr CtrlPtIPAddr; + + /** The current value of the variable. This needs to be allocated by + * the caller. When finished with it, the SDK frees this {\bf DOMString}. */ + DOMString CurrentVal; +}; + +/** Represents the reply for the current value of a state variable in an + asynchronous call. */ + +struct Upnp_State_Var_Complete +{ + /** The result of the operation. */ + int ErrCode; + + /** The control URL for the service. */ + char CtrlUrl[NAME_SIZE]; + + /** The name of the variable. */ + char StateVarName[NAME_SIZE]; + + /** The current value of the variable or error string in case of error. */ + DOMString CurrentVal; +}; + +/** Returned along with a {\bf UPNP_EVENT_RECEIVED} callback. */ + +struct Upnp_Event +{ + /** The subscription ID for this subscription. */ + Upnp_SID Sid; + + /** The event sequence number. */ + int EventKey; + + /** The DOM tree representing the changes generating the event. */ + IXML_Document *ChangedVariables; + +}; + +// +// This typedef is required by Doc++ to parse the last entry of the +// Upnp_Discovery structure correctly. +// + +typedef struct sockaddr_in SOCKADDRIN; + +/** Returned in a {\bf UPNP_DISCOVERY_RESULT} callback. */ + +struct Upnp_Discovery +{ + + /** The result code of the {\bf UpnpSearchAsync} call. */ + int ErrCode; + + /** The expiration time of the advertisement. */ + int Expires; + + /** The unique device identifier. */ + char DeviceId[LINE_SIZE]; + + /** The device type. */ + char DeviceType[LINE_SIZE]; + + /** The service type. */ + char ServiceType[LINE_SIZE]; + + /** The service version. */ + char ServiceVer[LINE_SIZE]; + + /** The URL to the UPnP description document for the device. */ + char Location[LINE_SIZE]; + + /** The operating system the device is running. */ + char Os[LINE_SIZE]; + + /** Date when the response was generated. */ + char Date[LINE_SIZE]; + + /** Confirmation that the MAN header was understood by the device. */ + char Ext[LINE_SIZE]; + + /** The host address of the device responding to the search. */ + SOCKADDRIN * DestAddr; + +}; + +/** Returned along with a {\bf UPNP_EVENT_SUBSCRIBE_COMPLETE} or {\bf + * UPNP_EVENT_UNSUBSCRIBE_COMPLETE} callback. */ + +struct Upnp_Event_Subscribe { + + /** The SID for this subscription. For subscriptions, this only + * contains a valid SID if the {\bf Upnp_EventSubscribe.result} field + * contains a {\tt UPNP_E_SUCCESS} result code. For unsubscriptions, + * this contains the SID from which the subscription is being + * unsubscribed. */ + + Upnp_SID Sid; + + /** The result of the operation. */ + int ErrCode; + + /** The event URL being subscribed to or removed from. */ + char PublisherUrl[NAME_SIZE]; + + /** The actual subscription time (for subscriptions only). */ + int TimeOut; + +}; + +/** Returned along with a {\bf UPNP_EVENT_SUBSCRIPTION_REQUEST} + * callback. */ + +struct Upnp_Subscription_Request +{ + /** The identifier for the service being subscribed to. */ + char *ServiceId; + + /** Universal device name. */ + char *UDN; + + /** The assigned subscription ID for this subscription. */ + Upnp_SID Sid; + +}; + + +struct File_Info +{ + /** The length of the file. A length less than 0 indicates the size + * is unknown, and data will be sent until 0 bytes are returned from + * a read call. */ + int file_length; + + /** The time at which the contents of the file was modified; + * The time system is always local (not GMT). */ + time_t last_modified; + + /** If the file is a directory, {\bf is_directory} contains + * a non-zero value. For a regular file, it should be 0. */ + int is_directory; + + /** If the file or directory is readable, this contains + * a non-zero value. If unreadable, it should be set to 0. */ + int is_readable; + + /** The content type of the file. This string needs to be allocated + * by the caller using {\bf ixmlCloneDOMString}. When finished + * with it, the SDK frees the {\bf DOMString}. */ + + DOMString content_type; + +}; + +/* The type of handle returned by the web server for open requests. */ + +typedef void *UpnpWebFileHandle; + +/** The {\bf UpnpVirtualDirCallbacks} structure contains the pointers to + * file-related callback functions a device application can register to + * virtualize URLs. + */ +struct UpnpVirtualDirCallbacks +{ + /** Called by the web server to query information on a file. The callback + * should return 0 on success or -1 on an error. */ + int (*get_info) ( + IN const char *filename, /** The name of the file to query. */ + OUT struct File_Info *info /** Pointer to a structure to store the + information on the file. */ + ); + + /** Called by the web server to open a file. The callback should return + * a valid handle if the file can be opened. Otherwise, it should return + * {\tt NULL} to signify an error. */ + UpnpWebFileHandle (*open)( + IN const char *filename, /** The name of the file to open. */ + IN enum UpnpOpenFileMode Mode /** The mode in which to open the file. + Valid values are {\tt UPNP_READ} or + {\tt UPNP_WRITE}. */ + ); + + /** Called by the web server to perform a sequential read from an open + * file. The callback should copy {\bf buflen} bytes from the file into + * the buffer. + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt 0}: The file contains no more data (EOF). + * \item {\tt >0}: A successful read of the number of bytes in the + * return code. + * \item {\tt <0}: An error occurred reading the file. + * \end{itemzie} + */ + int (*read) ( + IN UpnpWebFileHandle fileHnd, /** The handle of the file to read. */ + OUT char *buf, /** The buffer in which to place the + data. */ + IN size_t buflen /** The size of the buffer (i.e. the + number of bytes to read). */ + ); + + /** Called by the web server to perform a sequential write to an open + * file. The callback should write {\bf buflen} bytes into the file from + * the buffer. It should return the actual number of bytes written, + * which might be less than {\bf buflen} in the case of a write error. + */ + int (*write) ( + IN UpnpWebFileHandle fileHnd, /** The handle of the file to write. */ + IN char *buf, /** The buffer with the bytes to write. */ + IN size_t buflen /** The number of bytes to write. */ + ); + + /** Called by the web server to move the file pointer, or offset, into + * an open file. The {\bf origin} parameter determines where to start + * moving the file pointer. A value of {\tt SEEK_CUR} moves the + * file pointer relative to where it is. The {\bf offset} parameter can + * be either positive (move forward) or negative (move backward). + * {\tt SEEK_END} moves relative to the end of the file. A positive + * {\bf offset} extends the file. A negative {\bf offset} moves backward + * in the file. Finally, {\tt SEEK_SET} moves to an absolute position in + * the file. In this case, {\bf offset} must be positive. The callback + * should return 0 on a successful seek or a non-zero value on an error. + */ + int (*seek) ( + IN UpnpWebFileHandle fileHnd, /** The handle of the file to move the + file pointer. */ + IN long offset, /** The number of bytes to move in the + file. Positive values move foward and + negative values move backward. Note + that this must be positive if the + {\bf origin} is {\tt SEEK_SET}. */ + IN int origin /** The position to move relative to. It + can be {\tt SEEK_CUR} to move relative + to the current position, + {\tt SEEK_END} to move relative to + the end of the file, or {\tt + SEEK_SET} to specify an absolute + offset. */ + ); + + /** Called by the web server to close a file opened via the {\bf open} + * callback. It should return 0 on success, or a non-zero value on an + * error. + */ + int (*close) ( + IN UpnpWebFileHandle fileHnd /** The handle of the file to close. */ + ); + +}; + +typedef struct virtual_Dir_List +{ + struct virtual_Dir_List *next; + char dirName[NAME_SIZE]; +} virtualDirList; + +/** All callback functions share the same prototype, documented below. + * Note that any memory passed to the callback function + * is valid only during the callback and should be copied if it + * needs to persist. This callback function needs to be thread + * safe. The context of the callback is always on a valid thread + * context and standard synchronization methods can be used. Note, + * however, because of this the callback cannot call SDK functions + * unless explicitly noted. + * + * \begin{verbatim} + int CallbackFxn( Upnp_EventType EventType, void* Event, void* Cookie ); + \end{verbatim} + * + * where {\bf EventType} is the event that triggered the callback, + * {\bf Event} is a structure that denotes event-specific information for that + * event, and {\bf Cookie} is the user data passed when the callback was + * registered. + * + * See {\bf Upnp_EventType} for more information on the callback values and + * the associated {\bf Event} parameter. + * + * The return value of the callback is currently ignored. It may be used + * in the future to communicate results back to the SDK. + */ + +typedef int (*Upnp_FunPtr) ( + IN Upnp_EventType EventType, + IN void *Event, + IN void *Cookie + ); + +//@} // Constants, Structures, and Types + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +///@name Initialization and Registration +//@{ +/** Initializes the Linux SDK for UPnP Devices. This function must be called + * before any other API function can be called. It should be called + * only once. Subsequent calls to this API return a {\tt UPNP_E_INIT} + * error code. + * + * Optionally, the application can specify a host IP address (in the + * case of a multi-homed configuration) and a port number to use for + * all UPnP operations. Since a port number can be used only by one + * process, multiple processes using the SDK must specify + * different port numbers. + * + * If unspecified, the SDK will use the first adapter's IP address + * and an arbitrary port. + * + * This call is synchronous. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist + * to initialize the SDK. + * \item {\tt UPNP_E_INIT}: The SDK is already initialized. + * \item {\tt UPNP_E_INIT_FAILED}: The SDK initialization + * failed for an unknown reason. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_LISTEN}: An error occurred listening to a socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error ocurred creating a socket. + * \item {\tt UPNP_E_INTERNAL_ERROR}: An internal error ocurred. + * \end{itemize} */ + +int UpnpInit( + IN const char *HostIP, /** The host IP address to use, in + string format, for example "192.168.0.1", + or {\tt NULL} to use the first adapter's + IP address. */ + IN unsigned short DestPort /** The destination port number to use. 0 + will pick an arbitrary free port. */ + ); + +/** Terminates the Linux SDK for UPnP Devices. This function must be the last + * API function called. It should be called only once. Subsequent calls to + * this API return a {\tt UPNP_E_FINISH} error code. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_FINISH}: The SDK is already terminated or + * it is not initialized. + * \end{itemize} */ + +int UpnpFinish(); + +/** If '0' is used as the port number in {\bf UpnpInit}, then this + * function can be used to retrieve the actual port allocated to + * the SDK. If {\bf UpnpInit} has not succeeded then 0 is + * returned. + * + * @return [unsigned short] The port on which an internal server is + * listening for UPnP related requests. + */ +unsigned short UpnpGetServerPort(void); + +/** If {\tt NULL} is used as the IP address in {\bf UpnpInit}, then this + * function can be used to retrieve the actual interface address + * on which device is running. If {\bf UpnpInit} has not succeeded + * then {\tt NULL} is returned. + * + * @return [char*] The IP address on which an internal server is listening + * for UPnP related requests. + */ +char * UpnpGetServerIpAddress(void); + +/** {\bf UpnpRegisterClient} registers a control point application with the + * SDK. A control point application cannot make any other API calls + * until it registers using this function. + * + * {\bf UpnpRegisterClient} is a synchronous call and generates no callbacks. + * Callbacks can occur as soon as this function returns. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_FINISH}: The SDK is already terminated or + * is not initialized. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf Callback} or {\bf Hnd} + * is not a valid pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * register this control point. + * \end{itemize} + */ + +int UpnpRegisterClient( + IN Upnp_FunPtr Callback, /** Pointer to a function for receiving + asynchronous events. */ + IN const void *Cookie, /** Pointer to user data returned with the + callback function when invoked. */ + OUT UpnpClient_Handle *Hnd /** Pointer to a variable to store the + new control point handle. */ + ); + +/** {\bf UpnpRegisterRootDevice} registers a device application with + * the SDK. A device application cannot make any other API + * calls until it registers using this function. Device applications + * can also register as control points (see {\bf UpnpRegisterClient} + * to get a control point handle to perform control point + * functionality). + * + * {\bf UpnpRegisterRootDevice} is synchronous and does not generate + * any callbacks. Callbacks can occur as soon as this function returns. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_FINISH}: The SDK is already terminated or + * is not initialized. + * \item {\tt UPNP_E_INVALID_DESC}: The description document was not + * a valid device description. + * \item {\tt UPNP_E_INVALID_URL}: The URL for the description document + * is not valid. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf Callback} or {\bf Hnd} + * is not a valid pointer or {\bf DescURL} is {\tt NULL}. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting the + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \item {\tt UPNP_E_OUTOF_MEMORY}: There are insufficient resources to + * register this root device. + * \end{itemize} */ + +int UpnpRegisterRootDevice( + IN const char *DescUrl, /** Pointer to a string containing the + description URL for this root device + instance. */ + IN Upnp_FunPtr Callback, /** Pointer to the callback function for + receiving asynchronous events. */ + IN const void *Cookie, /** Pointer to user data returned with the + callback function when invoked. */ + OUT UpnpDevice_Handle *Hnd /** Pointer to a variable to store the + new device handle. */ + ); + +/** {\bf UpnpRegisterRootDevice2} is similar to {\bf UpnpRegisterRootDevice}, + * except that it also allows the description document to be specified as a + * file or a memory buffer. The description can also be configured to have the + * correct IP and port address. + * + * NOTE: For the configuration to be functional, the internal web server + * MUST be present. In addition, the web server MUST be activated + * (using {\bf UpnpSetWebServerRootDir}) before calling this function. + * The only condition where the web server can be absent is if the + * description document is specified as a URL and no configuration is + * required (i.e. {\tt config_baseURL = 0}.) + * + * {\bf UpnpRegisterRootDevice2} is synchronous and does not generate + * any callbacks. Callbacks can occur as soon as this function returns. + * + * Examples of using different types of description documents: + * \begin{verbatim} + 1) Description specified as a URL: + descriptionType == UPNPREG_URL_DESC + description is the URL + bufferLen = 0 (ignored) + 2) Description specified as a file: + descriptionType == UPNPREG_FILENAME_DESC + description is a filename + bufferLen = 0 (ignored) + 3) Description specified as a memory buffer: + descriptionType == UPNPREG_BUF_DESC + description is pointer to a memory buffer + bufferLen == length of memory buffer + \end{verbatim} + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_FINISH}: The SDK is already terminated or + * is not initialized. + * \item {\tt UPNP_E_INVALID_DESC}: The description document is not + * a valid device description. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf Callback} or {\bf Hnd} + * is not a valid pointer or {\bf DescURL} is {\tt NULL}. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting the + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \item {\tt UPNP_E_OUTOF_MEMORY}: There are insufficient resources to + * register this root device. + * \item {\tt UPNP_E_URL_TOO_BIG}: Length of the URL is bigger than the + * internal buffer. + * \item {\tt UPNP_E_FILE_NOT_FOUND}: The description file could not + * be found. + * \item {\tt UPNP_E_FILE_READ_ERROR}: An error occurred reading the + * description file. + * \item {\tt UPNP_E_INVALID_URL}: The URL to the description document + * is invalid. + * \item {\tt UPNP_E_EXT_NOT_XML}: The URL to the description document + * or file should have a {\tt .xml} extension. + * \item {\tt UPNP_E_NO_WEB_SERVER}: The internal web server has been + * compiled out; the SDK cannot configure itself from the + * description document. + * \end{itemize} */ + +int UpnpRegisterRootDevice2( + IN Upnp_DescType descriptionType,/** The type of the description + document. */ + IN const char* description, /** Treated as a URL, file name or + memory buffer depending on + description type. */ + IN size_t bufferLen, /** The length of memory buffer if + passing a description in a buffer, + otherwise it is ignored. */ + IN int config_baseURL, /** If nonzero, {\tt URLBase} of + description document is + configured and the description + is served using the internal + web server. */ + IN Upnp_FunPtr Fun, /** Pointer to the callback function + for receiving asynchronous + events. */ + IN const void* Cookie, /** Pointer to user data returned + with the callback function when + invoked. */ + OUT UpnpDevice_Handle* Hnd /** Pointer to a variable to store + the new device handle. */ + ); + +/** {\bf UpnpUnRegisterClient} unregisters a control point application, + * unsubscribing all active subscriptions. After this call, the + * {\bf UpnpClient_Handle} is no longer valid. + * + * {\bf UpnpUnRegisterClient} is a synchronous call and generates no + * callbacks. The SDK generates no more callbacks after this + * function returns. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \end{itemize} */ + +int UpnpUnRegisterClient( + IN UpnpClient_Handle Hnd /** The handle of the control point instance + to unregister. */ + ); + +/** Unregisters a root device registered with {\bf UpnpRegisterRootDevice} or + * {\bf UpnpRegisterRootDevice2}. After this call, the + * {\bf UpnpDevice_Handle} is no longer valid. For all advertisements that + * have not yet expired, the SDK sends a device unavailable message + * automatically. + * + * {\bf UpnpUnRegisterRootDevice} is a synchronous call and generates no + * callbacks. Once this call returns, the SDK will no longer + * generate callbacks to the application. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid + * device handle. + * \end{itemize} + */ + +int UpnpUnRegisterRootDevice( + IN UpnpDevice_Handle /** The handle of the root device instance to + unregister. */ + ); + + +/** OBSOLETE METHOD : use {\bf UpnpSetMaxContentLength} instead. + * Warning: the Handle argument provided here is not used, so the effect + * of this function is global to the SDK (= same as + * {\bf UpnpSetMaxContentLength} ). + */ +int UpnpSetContentLength( + IN UpnpClient_Handle Hnd, + IN int contentLength + ); + + +/** Sets the maximum content-length that the SDK will process on an incoming + * SOAP requests or responses. This API allows devices that have memory + * constraints to exhibit consistent behaviour if the size of the + * incoming SOAP message exceeds the memory that device can allocate. + * The default maximum content-length is {\tt DEFAULT_SOAP_CONTENT_LENGTH} + * = 16K bytes. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \end{itemize} + */ +int UpnpSetMaxContentLength( + IN size_t contentLength /** The maximum permissible content length + for incoming SOAP actions, in bytes. */ + ); + +//@} // Initialization and Registration + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// // +// D I S C O V E R Y // +// // +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +///@name Discovery +//@{ + +/** {\bf UpnpSearchAsync} searches for devices matching the given + * search target. The function returns immediately and the SDK + * calls the default callback function, registered during the + * {\bf UpnpRegisterClient} call, for each matching root device, + * device, or service. The application specifies the search type by the + * {\bf Target} parameter. + * + * Note that there is no way for the SDK to distinguish which client + * instance issued a particular search. Therefore, the client can get + * search callbacks that do not match the original criteria of the search. + * Also, the application will receive multiple callbacks for each search. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf Target} is {\tt NULL}. + * \end{itemize} */ + +int UpnpSearchAsync( + IN UpnpClient_Handle Hnd, /** The handle of the client performing + the search. */ + IN int Mx, /** The time, in seconds, to wait for + responses. If the time is greater + than {\tt MAX_SEARCH_TIME} then the time is + set to {\tt MAX_SEARCH_TIME}. If the time is + less than {\tt MIN_SEARCH_TIME} then the + time is set to {\tt MIN_SEARCH_TIME}. */ + IN const char *Target, /** The search target as defined in the UPnP + Device Architecture v1.0 specification. */ + IN const void *Cookie /** The user data to pass when the callback + function is invoked. */ + ); + +/** {\bf UpnpSendAdvertisement} sends out the discovery announcements for + * all devices and services for a device. Each announcement is made with + * the same expiration time. + * + * {\bf UpnpSendAdvertisement} is a synchronous call. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid + * device handle. + * \item {\tt UPNP_E_OUTOF_MEMORY}: There are insufficient resources to + * send future advertisements. + * \end{itemize} + */ +int UpnpSendAdvertisement( + IN UpnpDevice_Handle Hnd, /** The device handle for which to send out the + announcements. */ + IN int Exp /** The expiration age, in seconds, of + the announcements. */ + ); + +//@} // Discovery + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// // +// C O N T R O L // +// // +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +///@name Control +//@{ + +/** {\bf UpnpGetServiceVarStatus} queries the state of a state + * variable of a service on another device. This is a synchronous call. + * A positive return value indicates a SOAP error code, whereas a negative + * return code indicates an SDK error code. {\bf Note that the use of this + * function is deprecated by the UPnP Forum}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: {\bf ActionUrl} is not a valid URL. + * \item {\tt UPNP_E_INVALID_DESC}: The XML document was not + * found or it does not contain a valid XML description. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf StVarVal} is not a valid + * pointer or {\bf VarName} or {\bf ActionUrl} is {\tt NULL}. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \item {\tt UPNP_SOAP_E_INVALID_VAR}: The given variable is invalid + * according to the device. + * \end{itemize} + */ + +int UpnpGetServiceVarStatus( + IN UpnpClient_Handle Hnd, /** The handle of the control point. */ + IN const char *ActionURL, /** The URL of the service. */ + IN const char *VarName, /** The name of the variable to query. */ + OUT DOMString *StVarVal /** The pointer to store the value + for {\bf VarName}. The SDK + allocates this string and the caller + needs to free it using + {\bf ixmlFreeDOMString}. */ + ); + +/** {\bf UpnpGetServiceVarStatusAsync} queries the state of a variable of a + * service, generating a callback when the operation is complete. {\bf Note + * that the use of this function is deprecated by the UPnP Forum}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf ActionUrl} is not a valid URL. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf VarName}, {\bf Fun} or + * {\bf ActionUrl} is not a valid pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpGetServiceVarStatusAsync( + IN UpnpClient_Handle Hnd, /** The handle of the control point. */ + IN const char *ActionURL, /** The URL of the service. */ + IN const char *VarName, /** The name of the variable to query. */ + IN Upnp_FunPtr Fun, /** Pointer to a callback function to + be invoked when the operation is complete. */ + IN const void *Cookie /** Pointer to user data to pass to the + callback function when invoked. */ + ); + +/** {\bf UpnpSendAction} sends a message to change a state variable + * in a service. This is a synchronous call that does not return until the + * action is complete. + * + * Note that a positive return value indicates a SOAP-protocol error code. + * In this case, the error description can be retrieved from {\bf RespNode}. + * A negative return value indicates an SDK error. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: {\bf ActionUrl} is not a valid URL. + * \item {\tt UPNP_E_INVALID_ACTION}: This action is not valid. + * \item {\tt UPNP_E_INVALID_DEVICE}: {\bf DevUDN} is not a + * valid device. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf ServiceType}, {\bf Action}, + * {\bf ActionUrl}, or + * {\bf RespNode} is not a valid pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpSendAction( + IN UpnpClient_Handle Hnd, /** The handle of the control point + sending the action. */ + IN const char *ActionURL, /** The action URL of the service. */ + IN const char *ServiceType, /** The type of the service. */ + IN const char *DevUDN, /** This parameter is ignored and must be + {\tt NULL}. */ + IN IXML_Document *Action, /** The DOM document for the action. */ + OUT IXML_Document **RespNode /** The DOM document for the response + to the action. The SDK allocates + this document and the caller needs to free + it. */ + ); + +/** {\bf UpnpSendActionEx} sends a message to change a state variable + * in a service. This is a synchronous call that does not return until the + * action is complete. + * + * Note that a positive return value indicates a SOAP-protocol error code. + * In this case, the error description can be retrieved from {\bf RespNode}. + * A negative return value indicates an SDK error. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: {\bf ActionUrl} is not a valid URL. + * \item {\tt UPNP_E_INVALID_ACTION}: This action is not valid. + * \item {\tt UPNP_E_INVALID_DEVICE}: {\bf DevUDN} is not a + * valid device. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf ServiceType}, {\bf Action}, + * {\bf ActionUrl}, or + * {\bf RespNode} is not a valid pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpSendActionEx( + IN UpnpClient_Handle Hnd, /** The handle of the control point + sending the action. */ + IN const char *ActionURL, /** The action URL of the service. */ + IN const char *ServiceType, /** The type of the service. */ + IN const char *DevUDN, /** This parameter is ignored and must be + {\tt NULL}. */ + IN IXML_Document *Header, /** The DOM document for the SOAP header. + This may be {\tt NULL} if the header is + not required. */ + IN IXML_Document *Action, /** The DOM document for the action. */ + OUT IXML_Document **RespNode /** The DOM document for the response + to the action. The SDK allocates + this document and the caller needs to free + it. */ + ); + +/** {\bf UpnpSendActionAsync} sends a message to change a state variable + * in a service, generating a callback when the operation is complete. + * See {\bf UpnpSendAction} for comments on positive return values. These + * positive return values are sent in the event struct associated with the + * {\tt UPNP_CONTROL_ACTION_COMPLETE} event. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: {\bf ActionUrl} is an invalid URL. + * \item {\tt UPNP_E_INVALID_DEVICE}: {\bf DevUDN} is an invalid device. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf Fun} is not a valid + * callback function or {\bf ServiceType}, {\bf Act}, or + * {\bf ActionUrl} is {\tt NULL}. + * \item {\tt UPNP_E_INVALID_ACTION}: This action is not valid. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpSendActionAsync( + IN UpnpClient_Handle Hnd, /** The handle of the control point + sending the action. */ + IN const char *ActionURL, /** The action URL of the service. */ + IN const char *ServiceType, /** The type of the service. */ + IN const char *DevUDN, /** This parameter is ignored and must be + {\tt NULL}. */ + IN IXML_Document *Action, /** The DOM document for the action to + perform on this device. */ + IN Upnp_FunPtr Fun, /** Pointer to a callback function to + be invoked when the operation + completes. */ + IN const void *Cookie /** Pointer to user data that to be + passed to the callback when invoked. */ + ); + +/** {\bf UpnpSendActionExAsync} sends a message to change a state variable + * in a service, generating a callback when the operation is complete. + * See {\bf UpnpSendAction} for comments on positive return values. These + * positive return values are sent in the event struct associated with the + * {\tt UPNP_CONTROL_ACTION_COMPLETE} event. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: {\bf ActionUrl} is an invalid URL. + * \item {\tt UPNP_E_INVALID_DEVICE}: {\bf DevUDN} is an invalid device. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf Fun} is not a valid + * callback function or {\bf ServiceType}, {\bf Act}, or + * {\bf ActionUrl} is {\tt NULL}. + * \item {\tt UPNP_E_INVALID_ACTION}: This action is not valid. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpSendActionExAsync( + IN UpnpClient_Handle Hnd, /** The handle of the control point + sending the action. */ + IN const char *ActionURL, /** The action URL of the service. */ + IN const char *ServiceType, /** The type of the service. */ + IN const char *DevUDN, /** This parameter is ignored and must be + {\tt NULL}. */ + IN IXML_Document *Header, /** The DOM document for the SOAP header. + This may be {\tt NULL} if the header is + not required. */ + IN IXML_Document *Action, /** The DOM document for the action to + perform on this device. */ + IN Upnp_FunPtr Fun, /** Pointer to a callback function to + be invoked when the operation + completes. */ + IN const void *Cookie /** Pointer to user data that to be + passed to the callback when invoked. */ + ); + +//@} // Control + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// // +// E V E N T I N G // +// // +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +///@name Eventing +//@{ + +/** {\bf UpnpAcceptSubscription} accepts a subscription request and sends + * out the current state of the eventable variables for a service. + * The device application should call this function when it receives a + * {\tt UPNP_EVENT_SUBSCRIPTION_REQUEST} callback. This function is + * synchronous and generates no callbacks. + * + * {\bf UpnpAcceptSubscription} can be called during the execution of + * a callback function. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid device + * handle. + * \item {\tt UPNP_E_INVALID_SERVICE}: The {\bf DevId}/{\bf ServId} + * pair refers to an invalid service. + * \item {\tt UPNP_E_INVALID_SID}: The specified subscription ID is not + * valid. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf VarName}, + * {\bf NewVal}, {\bf DevID}, or {\bf ServID} is not a valid + * pointer or {\bf cVariables} is less than zero. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpAcceptSubscription( + IN UpnpDevice_Handle Hnd, /** The handle of the device. */ + IN const char *DevID, /** The device ID of the subdevice of the + service generating the event. */ + IN const char *ServID, /** The unique service identifier of the service + generating the event. */ + IN const char **VarName, /** Pointer to an array of event variables. */ + IN const char **NewVal, /** Pointer to an array of values for + the event variables. */ + IN int cVariables, /** The number of event variables in + {\bf VarName}. */ + IN Upnp_SID SubsId /** The subscription ID of the newly + registered control point. */ + ); + +/** {\bf UpnpAcceptSubscriptionExt} is similar to {\bf UpnpAcceptSubscription} + * except that it takes a DOM document for the variables to event rather + * than an array of strings. This function is sychronous + * and generates no callbacks. + * + * {\bf UpnpAcceptSubscriptionExt} can be called during the execution of + * a callback function. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid device + * handle. + * \item {\tt UPNP_E_INVALID_SERVICE}: The {\bf DevId}/{\bf ServId} + * pair refers to an invalid service. + * \item {\tt UPNP_E_INVALID_SID}: The specified subscription ID is not + * valid. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf VarName}, + * {\bf NewVal}, {\bf DevID}, {\bf ServID}, or {\bf PropSet} + * is not a valid pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpAcceptSubscriptionExt( + IN UpnpDevice_Handle Hnd, /** The handle of the device. */ + IN const char *DevID, /** The device ID of the subdevice of the + service generating the event. */ + IN const char *ServID, /** The unique service identifier of the service + generating the event. */ + IN IXML_Document *PropSet, /** The DOM document for the property set. + Property set documents must conform to + the XML schema defined in section 4.3 of the + Universal Plug and Play Device Architecture + specification. */ + IN Upnp_SID SubsId /** The subscription ID of the newly + registered control point. */ + ); + +/** {\bf UpnpNotify} sends out an event change notification to all + * control points subscribed to a particular service. This function is + * synchronous and generates no callbacks. + * + * {\bf UpnpNotify} may be called during a callback function to send out + * a notification. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid device + * handle. + * \item {\tt UPNP_E_INVALID_SERVICE}: The {\bf DevId}/{\bf ServId} + * pair refers to an invalid service. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf VarName}, {\bf NewVal}, + * {\bf DevID}, or {\bf ServID} is not a valid pointer or + * {\bf cVariables} is less than zero. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpNotify( + IN UpnpDevice_Handle, /** The handle to the device sending the event. */ + IN const char *DevID, /** The device ID of the subdevice of the service + generating the event. */ + IN const char *ServID, /** The unique identifier of the service + generating the event. */ + IN const char **VarName,/** Pointer to an array of variables that + have changed. */ + IN const char **NewVal, /** Pointer to an array of new values for + those variables. */ + IN int cVariables /** The count of variables included in this + notification. */ + ); + +/** {\bf UpnpNotifyExt} is similar to {\bf UpnpNotify} except that it takes + * a DOM document for the event rather than an array of strings. This + * function is synchronous and generates no callbacks. + * + * {\bf UpnpNotifyExt} may be called during a callback function to send out + * a notification. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid device + * handle. + * \item {\tt UPNP_E_INVALID_SERVICE}: The {\bf DevId}/{\bf ServId} + * pair refers to an invalid service. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf VarName}, {\bf NewVal}, + * {\bf DevID}, {\bf ServID}, or {\bf PropSet} + * is not a valid pointer or {\bf cVariables} is less than zero. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpNotifyExt( + IN UpnpDevice_Handle, /** The handle to the device sending the + event. */ + IN const char *DevID, /** The device ID of the subdevice of the + service generating the event. */ + IN const char *ServID, /** The unique identifier of the service + generating the event. */ + IN IXML_Document *PropSet /** The DOM document for the property set. + Property set documents must conform to + the XML schema defined in section 4.3 of + the Universal Plug and Play Device + Architecture specification. */ + ); + +/** {\bf UpnpRenewSubscription} renews a subscription that is about to + * expire. This function is synchronous. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf Timeout} is not a valid pointer. + * \item {\tt UPNP_E_INVALID_SID}: The SID being passed to this function + * is not a valid subscription ID. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occured. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting to + * {\bf PublisherUrl}. + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error occurred creating a socket. + * \item {\tt UPNP_E_BAD_RESPONSE}: An error occurred in response from + * the publisher. + * \item {\tt UPNP_E_SUBSCRIBE_UNACCEPTED}: The publisher refused + * the subscription renew. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpRenewSubscription( + IN UpnpClient_Handle Hnd, /** The handle of the control point that + is renewing the subscription. */ + INOUT int *TimeOut, /** Pointer to a variable containing the + requested subscription time. Upon return, + it contains the actual renewal time. */ + IN Upnp_SID SubsId /** The ID for the subscription to renew. */ + ); + +/** {\bf UpnpRenewSubscriptionAsync} renews a subscription that is about + * to expire, generating a callback when the operation is complete. + * + * Note that many of the error codes for this function are returned in + * the {\bf Upnp_Event_Subscribe} structure. In those cases, the function + * returns {\tt UPNP_E_SUCCESS} and the appropriate error code will + * be in the {\bf Upnp_Event_Subscribe.ErrCode} field in the {\bf Event} + * structure passed to the callback. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_SID}: The {\bf SubsId} is not a valid + * subscription ID. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf Fun} is not a valid + * callback function pointer or {\bf Timeout} is less than zero + * but is not {\tt UPNP_INFINITE}. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occured (returned in + * the {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket (returned in the {\bf Upnp_Event_Subscribe.ErrCode} + * field as part of the callback). + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket (returned in the + * {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding the socket + * (returned in the {\bf Upnp_Event_Subscribe.ErrCode} field as + * part of the callback). + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting to + * {\bf PublisherUrl} (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error occurred creating socket ( + * returned in the {\bf Upnp_Event_Subscribe.ErrCode} field as + * part of the callback). + * \item {\tt UPNP_E_BAD_RESPONSE}: An error occurred in response from + * the publisher (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_SUBSCRIBE_UNACCEPTED}: The publisher refused + * the subscription request (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \end{itemize} + */ + +int UpnpRenewSubscriptionAsync( + IN UpnpClient_Handle Hnd, /** The handle of the control point that + is renewing the subscription. */ + IN int TimeOut, /** The requested subscription time. The + actual timeout value is returned when + the callback function is called. */ + IN Upnp_SID SubsId, /** The ID for the subscription to renew. */ + IN Upnp_FunPtr Fun, /** Pointer to a callback function to be + invoked when the renewal is complete. */ + IN const void *Cookie /** Pointer to user data passed + to the callback function when invoked. */ + ); + +/** {\bf UpnpSetMaxSubscriptions} sets the maximum number of subscriptions + * accepted per service. The default value accepts as many as system + * resources allow. If the number of current subscriptions for a service is + * greater than the requested value, the SDK accepts no new + * subscriptions or renewals, however, the SDK does not remove + * any current subscriptions. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid device + * handle. + * \end{itemize} + */ + +int UpnpSetMaxSubscriptions( + IN UpnpDevice_Handle Hnd, /** The handle of the device for which + the maximum number of subscriptions is + being set. */ + IN int MaxSubscriptions /** The maximum number of subscriptions to be + allowed per service. */ + ); + +/** {\bf UpnpSetMaxSubscriptionTimeOut} sets the maximum time-out accepted + * for a subscription request or renewal. The default value accepts the + * time-out set by the control point. If a control point requests a + * subscription time-out less than or equal to the maximum, the SDK + * grants the value requested by the control point. If the time-out + * is greater, the SDK returns the maximum value. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid device + * handle. + * \end{itemize} + */ + +int UpnpSetMaxSubscriptionTimeOut( + IN UpnpDevice_Handle Hnd, /** The handle of the device for which + the maximum subscription time-out is + being set. */ + IN int MaxSubscriptionTimeOut /** The maximum subscription time-out to + be accepted. */ + ); + +/** {\bf UpnpSubscribe} registers a control point to receive event + * notifications from another device. This operation is synchronous. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: {\bf PublisherUrl} is not a valid URL. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf Timeout} is not a valid pointer + * or {\bf SubsId} or {\bf PublisherUrl} is {\tt NULL}. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occured. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting to + * {\bf PublisherUrl}. + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error occurred creating a socket. + * \item {\tt UPNP_E_BAD_RESPONSE}: An error occurred in response from + * the publisher. + * \item {\tt UPNP_E_SUBSCRIBE_UNACCEPTED}: The publisher refused + * the subscription request. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpSubscribe( + IN UpnpClient_Handle Hnd, /** The handle of the control point. */ + IN const char *PublisherUrl, /** The URL of the service to subscribe to. */ + INOUT int *TimeOut, /** Pointer to a variable containing + the requested subscription time. Upon + return, it contains the actual + subscription time returned from the + service. */ + OUT Upnp_SID SubsId /** Pointer to a variable to receive the + subscription ID (SID). */ + ); + +/** {\bf UpnpSubscribeAsync} performs the same operation as + * {\bf UpnpSubscribe}, but returns immediately and calls the registered + * callback function when the operation is complete. + * + * Note that many of the error codes for this function are returned in + * the {\bf Upnp_Event_Subscribe} structure. In those cases, the function + * returns {\tt UPNP_E_SUCCESS} and the appropriate error code will + * be in the {\bf Upnp_Event_Subscribe.ErrCode} field in the {\bf Event} + * structure passed to the callback. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf PublisherUrl} is not a valid + * URL. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf TimeOut} or {\bf Fun} or + * {\bf PublisherUrl} is not a valid pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occured (returned in + * the {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket (returned in the + * {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket (returned in the + * {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding the socket + * (returned in the {\bf Upnp_Event_Subscribe.ErrCode} field as + * part of the callback). + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting to + * {\bf PublisherUrl} (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error occurred creating the + * socket (returned in the {\bf Upnp_Event_Subscribe.ErrCode} + * field as part of the callback). + * \item {\tt UPNP_E_BAD_RESPONSE}: An error occurred in response from + * the publisher (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_SUBSCRIBE_UNACCEPTED}: The publisher refused + * the subscription request (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \end{itemize} + */ + +int UpnpSubscribeAsync( + IN UpnpClient_Handle Hnd, /** The handle of the control point that + is subscribing. */ + IN const char *PublisherUrl, /** The URL of the service to subscribe + to. */ + IN int TimeOut, /** The requested subscription time. Upon + return, it contains the actual + subscription time returned from the + service. */ + IN Upnp_FunPtr Fun, /** Pointer to the callback function for + this subscribe request. */ + IN const void *Cookie /** A user data value passed to the + callback function when invoked. */ + ); + +/** {\bf UpnpUnSubscribe} removes the subscription of a control point from a + * service previously subscribed to using {\bf UpnpSubscribe} or + * {\bf UpnpSubscribeAsync}. This is a synchronous call. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_SID}: The {\bf SubsId} is not a valid + * subscription ID. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occured. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting to + * {\bf PublisherUrl}. + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error ocurred creating a socket. + * \item {\tt UPNP_E_BAD_RESPONSE}: An error occurred in response from + * the publisher. + * \item {\tt UPNP_E_UNSUBSCRIBE_UNACCEPTED}: The publisher refused + * the unsubscribe request (the client is still unsubscribed and + * no longer receives events). + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpUnSubscribe( + IN UpnpClient_Handle Hnd, /** The handle of the subscribed control + point. */ + IN Upnp_SID SubsId /** The ID returned when the control point + subscribed to the service. */ + ); + +/** {\bf UpnpUnSubscribeAsync} removes a subscription of a control + * point from a service previously subscribed to using {\bf + * UpnpSubscribe} or {\bf UpnpSubscribeAsync}, generating a callback + * when the operation is complete. + * + * Note that many of the error codes for this function are returned in + * the {\bf Upnp_Event_Subscribe} structure. In those cases, the function + * returns {\tt UPNP_E_SUCCESS} and the appropriate error code will + * be in the {\bf Upnp_Event_Subscribe.ErrCode} field in the {\bf Event} + * structure passed to the callback. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_HANDLE}: The handle is not a valid control + * point handle. + * \item {\tt UPNP_E_INVALID_SID}: The {\bf SubsId} is not a valid SID. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf Fun} is not a valid callback + * function pointer. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occured (returned in + * the {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket (returned in the + * {\bf Upnp_Event_Subscribe.ErrCode} field as part of the + * callback). + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding the socket + * (returned in the {\bf Upnp_Event_Subscribe.ErrCode} field as + * part of the callback). + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting to + * {\bf PublisherUrl} (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_OUTOF_SOCKET}: An error occurred creating a socket ( + * returned in the {\bf Upnp_Event_Subscribe.ErrCode} field as + * part of the callback). + * \item {\tt UPNP_E_BAD_RESPONSE}: An error occurred in response from + * the publisher (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \item {\tt UPNP_E_UNSUBSCRIBE_UNACCEPTED}: The publisher refused + * the subscription request (returned in the {\bf + * Upnp_Event_Subscribe.ErrCode} field as part of the callback). + * \end{itemize} */ + +int UpnpUnSubscribeAsync( + IN UpnpClient_Handle Hnd, /** The handle of the subscribed control + point. */ + IN Upnp_SID SubsId, /** The ID returned when the + control point subscribed to the service. */ + IN Upnp_FunPtr Fun, /** Pointer to a callback function to be + called when the operation is complete. */ + IN const void *Cookie /** Pointer to user data to pass to the + callback function when invoked. */ + ); + +//@} // Eventing + + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// // +// C L I E N T - A P I // +// // +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +///@name Control Point HTTP API +//@{ + +/** {\bf UpnpDownloadUrlItem} downloads a file specified in a URL. + * The SDK allocates the memory for {\bf outBuf} and the + * application is responsible for freeing this memory. Note that + * the item is passed as a single buffer. Large items should not + * be transferred using this function. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url}, {\bf outBuf} + * or {\bf contentType} is not a valid pointer. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * download this file. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting a + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \end{itemize} + */ + +int UpnpDownloadUrlItem( + IN const char *url, /** URL of an item to download. */ + OUT char **outBuf, /** Buffer to store the downloaded item. */ + OUT char *contentType /** HTTP header value content type if + present. It should be at least + {\tt LINE_SIZE} bytes in size. */ + ); + +/** {\bf UpnpOpenHttpGet} gets a file specified in a URL. + * The SDK allocates the memory for {\bf handle} and + * {\bf contentType}, the application is responsible for freeing this memory. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url}, {\bf handle}, + * {\bf contentType}, {\bf contentLength} or {\bf httpStatus} + * is not a valid pointer. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * download this file. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting a + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \item {\tt UPNP_E_BAD_RESPONSE}: A bad response was received from the + * remote server. + * \end{itemize} + */ + +int UpnpOpenHttpGet( + IN const char *url, /** The URL of an item to get. */ + IN OUT void **handle, /** A pointer to store the handle for + this connection. */ + IN OUT char **contentType, /** A buffer to store the media type of + the item. */ + IN OUT int *contentLength, /** A pointer to store the length of the + item. */ + IN OUT int *httpStatus, /** The status returned on receiving a + response message. */ + IN int timeout /** The time out value sent with the + request during which a response is + expected from the server, failing + which, an error is reported back to + the user. */ + ); + +/** {\bf UpnpOpenHttpGetProxy} gets a file specified in a URL through the + * specified proxy. + * The SDK allocates the memory for {\bf handle} and + * {\bf contentType}, the application is responsible for freeing this memory. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url}, {\bf handle}, + * {\bf contentType}, {\bf contentLength} or {\bf httpStatus} + * is not a valid pointer. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * download this file. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting a + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \item {\tt UPNP_E_BAD_RESPONSE}: A bad response was received from the + * remote server. + * \end{itemize} + */ + +int UpnpOpenHttpGetProxy( + IN const char *url, /** The URL of an item to get. */ + IN const char *proxy_str, /** The URL of the proxy. */ + IN OUT void **handle, /** A pointer to store the handle for + this connection. */ + IN OUT char **contentType, /** A buffer to store the media type of + the item. */ + IN OUT int *contentLength, /** A pointer to store the length of the + item. */ + IN OUT int *httpStatus, /** The status returned on receiving a + response message. */ + IN int timeout /** The time out value sent with the + request during which a response is + expected from the server, failing + which, an error is reported back to + the user. */ + ); + +/** {\bf UpnpOpenHttpGetProxy} gets a file specified in a URL through the + * specified proxy. + * The SDK allocates the memory for {\bf handle} and + * {\bf contentType}, the application is responsible for freeing this memory. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url}, {\bf handle}, + * {\bf contentType}, {\bf contentLength} or {\bf httpStatus} + * is not a valid pointer. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * download this file. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting a + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \item {\tt UPNP_E_BAD_RESPONSE}: A bad response was received from the + * remote server. + * \end{itemize} + */ + +int UpnpOpenHttpGetProxy( + IN const char *url, /** The URL of an item to get. */ + IN const char *proxy_str, /** The URL of the proxy. */ + IN OUT void **handle, /** A pointer to store the handle for + this connection. */ + IN OUT char **contentType, /** A buffer to store the media type of + the item. */ + IN OUT int *contentLength, /** A pointer to store the length of the + item. */ + IN OUT int *httpStatus, /** The status returned on receiving a + response message. */ + IN int timeout /** The time out value sent with the + request during which a response is + expected from the server, failing + which, an error is reported back to + the user. */ + ); + +/** {\bf UpnpOpenHttpGetEx} gets specified number of bytes from a file + * specified in the URL. The number of bytes is specified through a low + * count and a high count which are passed as a range of bytes for the + * request. The SDK allocates the memory for {\bf handle} and + * {\bf contentType}, the application is responsible for freeing this memory. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url}, {\bf handle}, + * {\bf contentType}, {\bf contentLength} or {\bf httpStatus} + * is not a valid pointer. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * download this file. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting a + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \item {\tt UPNP_E_BAD_RESPONSE}: A bad response was received from the + * remote server. + * \end{itemize} + */ + +int UpnpOpenHttpGetEx( + IN const char *url, /** The URL of the item to get. */ + IN OUT void **handle, /** A pointer to store the handle for + this connection. */ + IN OUT char **contentType, /** A buffer to store the media type of the + item. */ + IN OUT int *contentLength, /** A buffer to store the length of the + item. */ + IN OUT int *httpStatus, /** The status returned on receiving a + response message from the remote + server. */ + IN int lowRange, /** An integer value representing the low + end of a range to retrieve. */ + IN int highRange, /** An integer value representing the high + end of a range to retrieve. */ + IN int timeout /** A time out value sent with the request + during which a response is expected + from the server, failing which, an + error is reported back to the user. */ + ); + +/** {\bf UpnpReadHttpGet} gets specified number of bytes from a file + * specified in a URL. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf handle}, {\bf buf} + * or {\bf size} is not a valid pointer. + * \item {\tt UPNP_E_BAD_RESPONSE}: A bad response was received from the + * remote server. + * \item {\tt UPNP_E_BAD_HTTPMSG}: Either the request or response was in + * the incorrect format. + * \item {\tt UPNP_E_CANCELED}: another thread called UpnpCancelHttpGet. + * \end{itemize} + * + * Note: In case of return values, the status code parameter of the passed + * in handle value may provide additional information on the return + * value. + */ + +int UpnpReadHttpGet( + IN void *handle, /** The token created by the call to + {\bf UpnpOpenHttpGet}. */ + IN OUT char *buf, /** The buffer to store the read item. */ + IN OUT unsigned int *size, /** The size of the buffer to be read. */ + IN int timeout /** The time out value sent with the + request during which a response is + expected from the server, failing + which, an error is reported back to + the user. */ + ); + +/** {\bf UpnpHttpGetProgress} rettrieve progress information of a http-get + * transfer. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf handle}, {\bf length} + * or {\bf total} is not a valid pointer. + * \end{itemize} + * + */ +int UpnpHttpGetProgress( + IN void *handle, /** The token created by the call to + {\bf UpnpOpenHttpGet}. */ + OUT unsigned int *length, /** The number of bytes received. */ + OUT unsigned int *total /** The content length. */ + ); + + +/** {\bf UpnpCancelHttpGet} set the cancel flag of the {\bf handle} + * parameter. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf handle} is not a valid pointer. + * \end{itemize} + */ + +int UpnpCancelHttpGet(IN void *handle); + + +/** {\bf UpnpHttpGetProgress} rettrieve progress information of a http-get + * transfer. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf handle}, {\bf length} + * or {\bf total} is not a valid pointer. + * \end{itemize} + * + */ +int UpnpHttpGetProgress( + IN void *handle, /** The token created by the call to + {\bf UpnpOpenHttpGet}. */ + OUT unsigned int *length, /** The number of bytes received. */ + OUT unsigned int *total /** The content length. */ + ); + + +/** {\bf UpnpCancelHttpGet} set the cancel flag of the {\bf handle} + * parameter. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf handle} is not a valid pointer. + * \end{itemize} + */ + +int UpnpCancelHttpGet(IN void *handle); + + +/** {\bf UpnpCloseHttpGet} closes the connection and frees memory that was + * allocated for the {\bf handle} parameter. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf handle} is not a valid pointer. + * \end{itemize} + */ + +int UpnpCloseHttpGet(IN void *handle); + + +/** {\bf UpnpOpenHttpPost} makes an HTTP POST request message, opens a + * connection to the server and sends the POST request to the server if + * the connection to the server succeeds. + * The SDK allocates the memory for {\bf handle} and + * {\bf contentType}, the application is responsible for freeing this memory. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url}, {\bf handle} + * or {\bf contentType} is not a valid pointer. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * download this file. + * \item {\tt UPNP_E_SOCKET_ERROR}: Error occured allocating a socket and + * resources or an error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting a + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \end{itemize} + */ + +int UpnpOpenHttpPost( + IN const char *url, /** The URL in which to send the POST + request. */ + IN OUT void **handle, /** A pointer in which to store the + handle for this connection. This + handle is required for futher + operations over this connection. */ + IN const char *contentType, /** A buffer to store the media type of + content being sent. */ + IN int contentLength, /** The length of the content, in bytes, + being posted. */ + IN int timeout /** The time out value sent with the + request during which a response is + expected from the receiver, failing + which, an error is reported. */ + ); + + +/** {\bf UpnpWriteHttpPost} sends a request to a server to copy the contents of + * a buffer to the URI specified in the {\bf UpnpOpenHttpPost} call. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf handle}, {\bf buf} + * or {\bf size} is not a valid pointer. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \end{itemize} + */ + +int UpnpWriteHttpPost( + IN void *handle, /** The handle of the connection created + by the call to {\bf UpnpOpenHttpPost}. */ + IN char *buf, /** The buffer to be posted. */ + IN unsigned int *size, /** The size, in bytes of {\bf buf}. */ + IN int timeout /** A timeout value sent with the request + during which a response is expected + from the server, failing which, an + error is reported. */ + ); + +/** {\bf UpnpCloseHttpPost} sends and receives any pending data, closes the + * connection with the server, and frees memory allocated during the + * {\bfUpnpOpenHttpPost} call. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf handle}, or + * {\bf httpStatus} is not a valid pointer. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \end{itemize} + */ + +int UpnpCloseHttpPost( + IN void *handle, /** The handle of the connection to close, + created by the call to + {\bf UpnpOpenHttpPost}. */ + IN OUT int *httpStatus, /** A pointer to a buffer to store the + final status of the connection. */ + IN int timeout /** A time out value sent with the request + during which a response is expected from + the server, failing which, an error is + reported. */ + ); + + +/** {\bf UpnpDownloadXmlDoc} downloads an XML document specified in a URL. + * The SDK parses the document and returns it in the form of a + * DOM document. The application is responsible for freeing the DOM document. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: Either {\bf url} or {\bf xmlDoc} + * is not a valid pointer. + * \item {\tt UPNP_E_INVALID_DESC}: The XML document was not + * found or it does not contain a valid XML description. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf url} is not a valid + * URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: There are insufficient resources to + * download the XML document. + * \item {\tt UPNP_E_NETWORK_ERROR}: A network error occurred. + * \item {\tt UPNP_E_SOCKET_WRITE}: An error or timeout occurred writing + * to a socket. + * \item {\tt UPNP_E_SOCKET_READ}: An error or timeout occurred reading + * from a socket. + * \item {\tt UPNP_E_SOCKET_BIND}: An error occurred binding a socket. + * \item {\tt UPNP_E_SOCKET_CONNECT}: An error occurred connecting the + * socket. + * \item {\tt UPNP_E_OUTOF_SOCKET}: Too many sockets are currently + * allocated. + * \end{itemize} + */ + +int UpnpDownloadXmlDoc( + IN const char *url, /** URL of the XML document. */ + OUT IXML_Document **xmlDoc /** A pointer in which to store the + XML document. */ + ); + +//@} // Control Point HTTP API + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// // +// W E B S E R V E R A P I // +// // +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +///@name Web Server API +//@{ + +/** {\bf UpnpSetWebServerRootDir} sets the document root directory for + * the internal web server. This directory is considered the + * root directory (i.e. "/") of the web server. + * + * This function also activates or deactivates the web server. + * To disable the web server, pass {\tt NULL} for {\bf rootDir}; to + * activate, pass a valid directory string. + * + * Note that this function is not available when the web server is not + * compiled into the SDK. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPPN_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_ARGUMENT}: {\bf rootDir} is an invalid + * directory. + * \end{itemize} + */ + +int UpnpSetWebServerRootDir( + IN const char* rootDir /** Path of the root directory of the web + server. */ + ); + +/** {\bf UpnpSetVirtualDirCallbacks} sets the callback function to be used to + * access a virtual directory. Refer to the description of + * {\bf UpnpVirtualDirCallbacks} for a description of the functions. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPPN_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_ARGUMENT}: {\bf callbacks} is not a valid + * pointer. + * \end{itemize} + */ + +int UpnpSetVirtualDirCallbacks( + IN struct UpnpVirtualDirCallbacks *callbacks /** Pointer to a structure + containing points to the + virtual interface + functions. */ + ); + +/** {\bf UpnpEnableWebServer} enables or disables the webserver. A value of + * {\tt TRUE} enables the webserver, {\tt FALSE} disables it. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPPN_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_ARGUMENT}: {\bf enable} is not valid. + * \end{itemize} + */ + +int UpnpEnableWebserver( + IN int enable /** {\tt TRUE} to enable, {\tt FALSE} to disable. */ + ); + +/** {\bf UpnpIsWebServerEnabled} returns {\tt TRUE} if the webserver is + * enabled, or {\tt FALSE} if it is not. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt TRUE}: The webserver is enabled. + * \item {\tt FALSE}: The webserver is not enabled + * \end{itemize} + */ + +int UpnpIsWebserverEnabled(); + +/** {\bf UpnpAddVirtualDir} adds a virtual directory mapping. + * + * All webserver requests containing the given directory are read using + * functions contained in a {\bf UpnpVirtualDirCallbacks} structure registered + * via {\bf UpnpSetVirtualDirCallbacks}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPPN_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_ARGUMENT}: {\bf dirName} is not valid. + * \end{itemize} + */ + +int UpnpAddVirtualDir( + IN const char *dirName /** The name of the new directory mapping to add. + */ + ); + +/** {\bf UpnpRemoveVirtualDir} removes a virtual directory mapping made with + * {\bf UpnpAddVirtualDir}. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPPN_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_ARGUMENT}: {\bf dirName} is not valid. + * \end{itemize} + */ + +int UpnpRemoveVirtualDir( + IN const char *dirName /** The name of the virtual directory mapping to + remove. */ + ); + +/** {\bf UpnpRemoveAllVirtualDirs} removes all virtual directory mappings. + * + * @return [void] This function does not return a value. + * + */ + +void UpnpRemoveAllVirtualDirs( ); + +void UpnpFree( + IN void *item /* The item to free. */ + ); + +//@} // Web Server API + +#ifdef __cplusplus +} +#endif // __cplusplus + +//@} The API + +#endif + diff --git a/libupnp/upnp/inc/upnpconfig.h.in b/libupnp/upnp/inc/upnpconfig.h.in new file mode 100644 index 0000000..309f763 --- /dev/null +++ b/libupnp/upnp/inc/upnpconfig.h.in @@ -0,0 +1,96 @@ +// -*- C -*- +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2006 Rémi Turboult +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UPNP_CONFIG_H +#define UPNP_CONFIG_H + + +/*************************************************************************** + * Library version + ***************************************************************************/ + +/** The library version (string) e.g. "1.3.0" */ +#undef UPNP_VERSION_STRING + +/** Major version of the library */ +#undef UPNP_VERSION_MAJOR + +/** Minor version of the library */ +#undef UPNP_VERSION_MINOR + +/** Patch version of the library */ +#undef UPNP_VERSION_PATCH + +/** The library version (numeric) e.g. 10300 means version 1.3.0 */ +#define UPNP_VERSION \ + ((UPNP_VERSION_MAJOR*100 + UPNP_VERSION_MINOR)*100 + UPNP_VERSION_PATCH) + + + +/*************************************************************************** + * Library optional features + ***************************************************************************/ + +/* + * The following defines can be tested in order to know which + * optional features have been included in the installed library. + */ + + +/** Defined to 1 if the library has been compiled with DEBUG enabled + * (i.e. configure --enable-debug) : file is available */ +#undef UPNP_HAVE_DEBUG + + +/** Defined to 1 if the library has been compiled with client API enabled + * (i.e. configure --enable-client) */ +#undef UPNP_HAVE_CLIENT + + +/** Defined to 1 if the library has been compiled with device API enabled + * (i.e. configure --enable-device) */ +#undef UPNP_HAVE_DEVICE + + +/** Defined to 1 if the library has been compiled with integrated web server + * (i.e. configure --enable-webserver --enable-device) */ +#undef UPNP_HAVE_WEBSERVER + + +/** Defined to 1 if the library has been compiled with helper API + * (i.e. configure --enable-tools) : file is available */ +#undef UPNP_HAVE_TOOLS + + +#endif // UPNP_CONFIG_H + + diff --git a/libupnp/upnp/inc/upnpdebug.h b/libupnp/upnp/inc/upnpdebug.h new file mode 100644 index 0000000..1efbbca --- /dev/null +++ b/libupnp/upnp/inc/upnpdebug.h @@ -0,0 +1,258 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// Copyright (c) 2006 Rémi Turboult +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UPNP_DEBUG_H +#define UPNP_DEBUG_H + +#include "upnpconfig.h" + +// Function declarations only if debug compiled into the library +#if UPNP_HAVE_DEBUG + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @name Other debugging features + The UPnP SDK contains other features to aid in debugging. + */ + +//@{ + +/** @name Upnp_LogLevel + * The user has the option to select 4 different types of debugging levels, + * see {\tt UpnpSetLogLevel}. + * The critical level will show only those messages + * which can halt the normal processing of the library, like memory + * allocation errors. The remaining three levels are just for debugging + * purposes. Packet level will display all the incoming and outgoing + * packets that are flowing between the control point and the device. + * Info Level displays the other important operational information + * regarding the working of the library. If the user selects All, + * then the library displays all the debugging information that it has. + * \begin{itemize} + * \item {\tt UPNP_CRITICAL [0]} + * \item {\tt UPNP_PACKET [1]} + * \item {\tt UPNP_INFO [2]} + * \item {\tt UPNP_ALL [3]} + * \end{itemize} + */ + +typedef enum Upnp_Module {SSDP,SOAP,GENA,TPOOL,MSERV,DOM,API, HTTP} Dbg_Module; +//@{ +typedef enum Upnp_LogLevel_e { + UPNP_CRITICAL, + UPNP_PACKET, + UPNP_INFO, + UPNP_ALL +} Upnp_LogLevel; +//@} + +// for backward compatibility +#define Dbg_Level Upnp_LogLevel + + +/** + * Default log level : see {\tt Upnp_LogLevel} + */ +#define UPNP_DEFAULT_LOG_LEVEL UPNP_ALL + + + +/*************************************************************************** + * Function : UpnpInitLog + * + * Parameters: void + * + * Description: + * This functions initializes the log files + * Returns: int + * -1 : If fails + * UPNP_E_SUCCESS : if success + ***************************************************************************/ +int UpnpInitLog(); + +// for backward compatibility +#define InitLog UpnpInitLog + + +/*************************************************************************** + * Function : UpnpSetLogLevel + * + * Parameters: void + * + * Description: + * This functions set the log level (see {\tt Upnp_LogLevel} + * Returns: void + ***************************************************************************/ +void UpnpSetLogLevel (Upnp_LogLevel); + + +/*************************************************************************** + * Function : UpnpCloseLog + * + * Parameters: void + * + * Description: + * This functions closes the log files + * Returns: void + ***************************************************************************/ +void UpnpCloseLog(); + +// for backward compatibility +#define CloseLog UpnpCloseLog + + +/*************************************************************************** + * Function : UpnpSetLogFileNames + * + * Parameters: + * IN const char* ErrFileName: name of the error file + * IN const char *InfoFileName: name of the information file + * IN int size: Size of the buffer + * IN int starLength: This parameter provides the width of the banner + * + * Description: + * This functions takes the buffer and writes the buffer in the file as + * per the requested banner + * Returns: void + ***************************************************************************/ +void UpnpSetLogFileNames (const char* ErrFileName, const char* InfoFileName); + +// for backward compatibility +#define SetLogFileNames UpnpSetLogFileNames + + +/*************************************************************************** + * Function : UpnpGetDebugFile + * + * Parameters: + * IN Dbg_Level DLevel: The level of the debug logging. It will decide + * whether debug statement will go to standard output, + * or any of the log files. + * IN Dbg_Module Module: debug will go in the name of this module + * + * Description: + * This function checks if the module is turned on for debug + * and returns the file descriptor corresponding to the debug level + * Returns: FILE * + * NULL : if the module is turn off for debug + * else returns the right file descriptor + ***************************************************************************/ +FILE* UpnpGetDebugFile (Upnp_LogLevel level, Dbg_Module module); + +// for backward compatibility +#define GetDebugFile UpnpGetDebugFile + + +/*************************************************************************** + * Function : UpnpPrintf + * + * Parameters: + * IN Dbg_Level DLevel: The level of the debug logging. It will decide + * whether debug statement will go to standard output, + * or any of the log files. + * IN Dbg_Module Module: debug will go in the name of this module + * IN char *DbgFileName: Name of the file from where debug statement is + * coming + * IN int DbgLineNo : Line number of the file from where debug statement + * is coming + * IN char * FmtStr, ...: Variable number of arguments that will go + * in the debug statement + * + * Description: + * This functions prints the debug statement either on the startdard + * output or log file along with the information from where this + * debug statement is coming + * Returns: void + ***************************************************************************/ +void UpnpPrintf (Upnp_LogLevel DLevel, Dbg_Module Module, + const char* DbgFileName, int DbgLineNo, + const char* FmtStr, + ...) +#if (__GNUC__ >= 3) + __attribute__((format (__printf__, 5, 6))) +#endif +; + + +/*************************************************************************** + * Function : UpnpDisplayBanner + * + * Parameters: + * IN FILE *fd: file descriptor where the banner will be written + * IN char **lines: The buffer that will be written + * IN int size: Size of the buffer + * IN int starLength: This parameter provides the width of the banner + * + * Description: + * This functions takes the buffer and writes the buffer in the file as + * per the requested banner + * Returns: void + ***************************************************************************/ +void UpnpDisplayBanner (FILE *fd, + const char** lines, size_t size, int starlength); + + +/*************************************************************************** + * Function : UpnpDisplayFileAndLine + * + * Parameters: + * IN FILE *fd: File descriptor where line number and file name will be + * written + * IN char *DbgFileName: Name of the file + * IN int DbgLineNo : Line number of the file + * + * Description: + * This function writes the file name and file number from where + * debug statement is coming to the log file + * Returns: void + ***************************************************************************/ +void UpnpDisplayFileAndLine (FILE *fd, const char *DbgFileName, int DbgLineNo); + + +//@} + + +#ifdef __cplusplus +} +#endif + +#endif // UPNP_HAVE_DEBUG + +#endif // UPNP_DEBUG_H + + + diff --git a/libupnp/upnp/inc/upnptools.h b/libupnp/upnp/inc/upnptools.h new file mode 100644 index 0000000..cdf49c8 --- /dev/null +++ b/libupnp/upnp/inc/upnptools.h @@ -0,0 +1,230 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/** @name Optional Tool APIs + * The Linux SDK for UPnP Devices contains some additional, optional + * utility APIs that can be helpful in writing applications using the + * SDK. These additional APIs can be compiled out in order to save code + * size in the SDK. Refer to the README for details. + */ + +//@{ + +#ifndef UPNP_TOOLS_H +#define UPNP_TOOLS_H + +#include "upnp.h" + +// Function declarations only if tools compiled into the library +#if UPNP_HAVE_TOOLS + +#ifdef __cplusplus +extern "C" { +#endif + +/** {\bf UpnpResolveURL} combines a base URL and a relative URL into + * a single absolute URL. The memory for {\bf AbsURL} needs to be + * allocated by the caller and must be large enough to hold the + * {\bf BaseURL} and {\bf RelURL} combined. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: {\bf RelURL} is {\tt NULL}. + * \item {\tt UPNP_E_INVALID_URL}: The {\bf BaseURL} / {\bf RelURL} + * combination does not form a valid URL. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpResolveURL( + IN const char * BaseURL, /** The base URL to combine. */ + IN const char * RelURL, /** The relative URL to {\bf BaseURL}. */ + OUT char * AbsURL /** A pointer to a buffer to store the + absolute URL. */ + ); + +/** {\bf UpnpMakeAction} creates an action request packet based on its input + * parameters (status variable name and value pair). Any number of input + * parameters can be passed to this function but every input variable name + * should have a matching value argument. + * + * @return [IXML_Document*] The action node of {\bf Upnp_Document} type or + * {\tt NULL} if the operation failed. + */ + +IXML_Document* UpnpMakeAction( + IN const char * ActionName, /** The action name. */ + IN const char * ServType, /** The service type. */ + IN int NumArg, /** Number of argument pairs to be passed. */ + IN const char * Arg, /** Status variable name and value pair. */ + IN ... /* Other status variable name and value pairs. */ + ); + +/** {\bf UpnpAddToAction} creates an action request packet based on its input + * parameters (status variable name and value pair). This API is specially + * suitable inside a loop to add any number input parameters into an existing + * action. If no action document exists in the beginning then a + * {\bf Upnp_Document} variable initialized with {\tt NULL} should be passed + * as a parameter. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: One or more of the parameters + * are invalid. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpAddToAction( + IN OUT IXML_Document ** ActionDoc, + /** A pointer to store the action + document node. */ + IN const char * ActionName, /** The action name. */ + IN const char * ServType, /** The service type. */ + IN const char * ArgName, /** The status variable name. */ + IN const char * ArgVal /** The status variable value. */ + ); + +/** {\bf UpnpMakeActionResponse} creates an action response packet based + * on its output parameters (status variable name and value pair). Any + * number of input parameters can be passed to this function but every output + * variable name should have a matching value argument. + * + * @return [IXML_Document*] The action node of {\bf Upnp_Document} type or + * {\tt NULL} if the operation failed. + */ + +IXML_Document* UpnpMakeActionResponse( + IN const char * ActionName, /** The action name. */ + IN const char * ServType, /** The service type. */ + IN int NumArg, /** The number of argument pairs passed. */ + IN const char * Arg, /** The status variable name and value pair. */ + IN ... /* Other status variable name and value pairs. */ + ); + +/** {\bf UpnpAddToActionResponse} creates an action response + * packet based on its output parameters (status variable name + * and value pair). This API is especially suitable inside + * a loop to add any number of input parameters into an existing action + * response. If no action document exists in the beginning, a + * {\bf Upnp_Document} variable initialized with {\tt NULL} should be passed + * as a parameter. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: One or more of the parameters + * are invalid. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + */ + +int UpnpAddToActionResponse( + IN OUT IXML_Document ** ActionResponse, + /** Pointer to a document to + store the action document + node. */ + IN const char * ActionName, /** The action name. */ + IN const char * ServType, /** The service type. */ + IN const char * ArgName, /** The status variable name. */ + IN const char * ArgVal /** The status variable value. */ + ); + +/** {\bf UpnpAddToPropertySet} can be used when an application needs to + * transfer the status of many variables at once. It can be used + * (inside a loop) to add some extra status variables into an existing + * property set. If the application does not already have a property + * set document, the application should create a variable initialized + * with {\tt NULL} and pass that as the first parameter. + * + * @return [int] An integer representing one of the following: + * \begin{itemize} + * \item {\tt UPNP_E_SUCCESS}: The operation completed successfully. + * \item {\tt UPNP_E_INVALID_PARAM}: One or more of the parameters + * are invalid. + * \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to + * complete this operation. + * \end{itemize} + * + */ + +int UpnpAddToPropertySet( + IN OUT IXML_Document **PropSet, + /** A pointer to the document containing + the property set document node. */ + IN const char * ArgName, /** The status variable name. */ + IN const char * ArgVal /** The status variable value. */ + ); + +/** {\bf UpnpCreatePropertySet} creates a property set + * message packet. Any number of input parameters can be passed + * to this function but every input variable name should have + * a matching value input argument. + * + * @return [IXML_Document*] {\tt NULL} on failure, or the property-set + * document node. + * + */ + +IXML_Document* UpnpCreatePropertySet( + IN int NumArg, /** The number of argument pairs passed. */ + IN const char* Arg, /** The status variable name and value pair. */ + IN ... + ); + +/** {\bf UpnpGetErrorMessage} converts an SDK error code into a + * string error message suitable for display. The memory returned + * from this function should NOT be freed. + * + * @return [char*] An ASCII text string representation of the error message + * associated with the error code. + */ + +const char * UpnpGetErrorMessage( + int errorcode /** The SDK error code to convert. */ + ); + +//@} + +#ifdef __cplusplus +} +#endif + +#endif // UPNP_HAVE_TOOLS + +#endif // UPNP_TOOLS_H + + diff --git a/libupnp/upnp/sample/common/sample_util.c b/libupnp/upnp/sample/common/sample_util.c new file mode 100644 index 0000000..bb58f35 --- /dev/null +++ b/libupnp/upnp/sample/common/sample_util.c @@ -0,0 +1,793 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "sample_util.h" +#include + +#if !UPNP_HAVE_TOOLS +# error "Need upnptools.h to compile samples ; try ./configure --enable-tools" +#endif + + +/* + Function pointer to use for displaying formatted + strings. Set on Initialization of device. + */ +int initialize = 1; +print_string gPrintFun = NULL; +state_update gStateUpdateFun = NULL; + +//mutex to control displaying of events +ithread_mutex_t display_mutex; + +/******************************************************************************** + * SampleUtil_Initialize + * + * Description: + * Initializes the sample util. Must be called before any sample util + * functions. May be called multiple times. + * + * Parameters: + * print_function - print function to use in SampleUtil_Print + * + ********************************************************************************/ +int +SampleUtil_Initialize( print_string print_function ) +{ + if( initialize ) { + ithread_mutexattr_t attr; + + gPrintFun = print_function; + ithread_mutexattr_init( &attr ); + ithread_mutexattr_setkind_np( &attr, ITHREAD_MUTEX_RECURSIVE_NP ); + ithread_mutex_init( &display_mutex, &attr ); + ithread_mutexattr_destroy( &attr ); + initialize = 0; + } + return UPNP_E_SUCCESS; +} + +/******************************************************************************** + * SampleUtil_RegisterUpdateFunction + * + * Description: + * + * Parameters: + * + ********************************************************************************/ +int +SampleUtil_RegisterUpdateFunction( state_update update_function ) +{ + static int initialize = 1; //only intialize once + + if( initialize ) { + gStateUpdateFun = update_function; + initialize = 0; + } + return UPNP_E_SUCCESS; +} + +/******************************************************************************** + * SampleUtil_Finish + * + * Description: + * Releases Resources held by sample util. + * + * Parameters: + * + ********************************************************************************/ +int +SampleUtil_Finish( ) +{ + ithread_mutex_destroy( &display_mutex ); + gPrintFun = NULL; + initialize = 1; + return UPNP_E_SUCCESS; +} + +/******************************************************************************** + * SampleUtil_GetElementValue + * + * Description: + * Given a DOM node such as 11, this routine + * extracts the value (e.g., 11) from the node and returns it as + * a string. The string must be freed by the caller using + * free. + * + * Parameters: + * node -- The DOM node from which to extract the value + * + ********************************************************************************/ + +char * +SampleUtil_GetElementValue( IN IXML_Element * element ) +{ + + IXML_Node *child = ixmlNode_getFirstChild( ( IXML_Node * ) element ); + + char *temp = NULL; + + if( ( child != 0 ) && ( ixmlNode_getNodeType( child ) == eTEXT_NODE ) ) { + temp = strdup( ixmlNode_getNodeValue( child ) ); + } + + return temp; +} + +/******************************************************************************** + * SampleUtil_GetFirstServiceList + * + * Description: + * Given a DOM node representing a UPnP Device Description Document, + * this routine parses the document and finds the first service list + * (i.e., the service list for the root device). The service list + * is returned as a DOM node list. + * + * Parameters: + * node -- The DOM node from which to extract the service list + * + ********************************************************************************/ +IXML_NodeList * +SampleUtil_GetFirstServiceList( IN IXML_Document * doc ) +{ + IXML_NodeList *ServiceList = NULL; + IXML_NodeList *servlistnodelist = NULL; + IXML_Node *servlistnode = NULL; + + servlistnodelist = + ixmlDocument_getElementsByTagName( doc, "serviceList" ); + if( servlistnodelist && ixmlNodeList_length( servlistnodelist ) ) { + + /* + we only care about the first service list, from the root device + */ + servlistnode = ixmlNodeList_item( servlistnodelist, 0 ); + + /* + create as list of DOM nodes + */ + ServiceList = + ixmlElement_getElementsByTagName( ( IXML_Element * ) + servlistnode, "service" ); + } + + if( servlistnodelist ) + ixmlNodeList_free( servlistnodelist ); + + return ServiceList; +} + +/******************************************************************************** + * SampleUtil_GetFirstDocumentItem + * + * Description: + * Given a document node, this routine searches for the first element + * named by the input string item, and returns its value as a string. + * String must be freed by caller using free. + * Parameters: + * doc -- The DOM document from which to extract the value + * item -- The item to search for + * + ********************************************************************************/ +char * +SampleUtil_GetFirstDocumentItem( IN IXML_Document * doc, + IN const char *item ) +{ + IXML_NodeList *nodeList = NULL; + IXML_Node *textNode = NULL; + IXML_Node *tmpNode = NULL; + + char *ret = NULL; + + nodeList = ixmlDocument_getElementsByTagName( doc, ( char * )item ); + + if( nodeList ) { + if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) { + textNode = ixmlNode_getFirstChild( tmpNode ); + + ret = strdup( ixmlNode_getNodeValue( textNode ) ); + } + } + + if( nodeList ) + ixmlNodeList_free( nodeList ); + return ret; +} + +/******************************************************************************** + * SampleUtil_GetFirstElementItem + * + * Description: + * Given a DOM element, this routine searches for the first element + * named by the input string item, and returns its value as a string. + * The string must be freed using free. + * Parameters: + * node -- The DOM element from which to extract the value + * item -- The item to search for + * + ********************************************************************************/ +char * +SampleUtil_GetFirstElementItem( IN IXML_Element * element, + IN const char *item ) +{ + IXML_NodeList *nodeList = NULL; + IXML_Node *textNode = NULL; + IXML_Node *tmpNode = NULL; + + char *ret = NULL; + + nodeList = ixmlElement_getElementsByTagName( element, ( char * )item ); + + if( nodeList == NULL ) { + SampleUtil_Print( "Error finding %s in XML Node\n", item ); + return NULL; + } + + if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) == NULL ) { + SampleUtil_Print( "Error finding %s value in XML Node\n", item ); + ixmlNodeList_free( nodeList ); + return NULL; + } + + textNode = ixmlNode_getFirstChild( tmpNode ); + + ret = strdup( ixmlNode_getNodeValue( textNode ) ); + + if( !ret ) { + SampleUtil_Print( "Error allocating memory for %s in XML Node\n", + item ); + ixmlNodeList_free( nodeList ); + return NULL; + } + + ixmlNodeList_free( nodeList ); + + return ret; +} + +/******************************************************************************** + * SampleUtil_PrintEventType + * + * Description: + * Prints a callback event type as a string. + * + * Parameters: + * S -- The callback event + * + ********************************************************************************/ +void +SampleUtil_PrintEventType( IN Upnp_EventType S ) +{ + switch ( S ) { + + case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: + SampleUtil_Print( "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n" ); + break; + case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: + SampleUtil_Print( "UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n" ); + break; + case UPNP_DISCOVERY_SEARCH_RESULT: + SampleUtil_Print( "UPNP_DISCOVERY_SEARCH_RESULT\n" ); + break; + case UPNP_DISCOVERY_SEARCH_TIMEOUT: + SampleUtil_Print( "UPNP_DISCOVERY_SEARCH_TIMEOUT\n" ); + break; + + /* + SOAP Stuff + */ + case UPNP_CONTROL_ACTION_REQUEST: + SampleUtil_Print( "UPNP_CONTROL_ACTION_REQUEST\n" ); + break; + case UPNP_CONTROL_ACTION_COMPLETE: + SampleUtil_Print( "UPNP_CONTROL_ACTION_COMPLETE\n" ); + break; + case UPNP_CONTROL_GET_VAR_REQUEST: + SampleUtil_Print( "UPNP_CONTROL_GET_VAR_REQUEST\n" ); + break; + case UPNP_CONTROL_GET_VAR_COMPLETE: + SampleUtil_Print( "UPNP_CONTROL_GET_VAR_COMPLETE\n" ); + break; + + /* + GENA Stuff + */ + case UPNP_EVENT_SUBSCRIPTION_REQUEST: + SampleUtil_Print( "UPNP_EVENT_SUBSCRIPTION_REQUEST\n" ); + break; + case UPNP_EVENT_RECEIVED: + SampleUtil_Print( "UPNP_EVENT_RECEIVED\n" ); + break; + case UPNP_EVENT_RENEWAL_COMPLETE: + SampleUtil_Print( "UPNP_EVENT_RENEWAL_COMPLETE\n" ); + break; + case UPNP_EVENT_SUBSCRIBE_COMPLETE: + SampleUtil_Print( "UPNP_EVENT_SUBSCRIBE_COMPLETE\n" ); + break; + case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: + SampleUtil_Print( "UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n" ); + break; + + case UPNP_EVENT_AUTORENEWAL_FAILED: + SampleUtil_Print( "UPNP_EVENT_AUTORENEWAL_FAILED\n" ); + break; + case UPNP_EVENT_SUBSCRIPTION_EXPIRED: + SampleUtil_Print( "UPNP_EVENT_SUBSCRIPTION_EXPIRED\n" ); + break; + + } +} + +/******************************************************************************** + * SampleUtil_PrintEvent + * + * Description: + * Prints callback event structure details. + * + * Parameters: + * EventType -- The type of callback event + * Event -- The callback event structure + * + ********************************************************************************/ +int +SampleUtil_PrintEvent( IN Upnp_EventType EventType, + IN void *Event ) +{ + + ithread_mutex_lock( &display_mutex ); + + SampleUtil_Print + ( "\n\n\n======================================================================\n" ); + SampleUtil_Print + ( "----------------------------------------------------------------------\n" ); + SampleUtil_PrintEventType( EventType ); + + switch ( EventType ) { + + /* + SSDP + */ + case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: + case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: + case UPNP_DISCOVERY_SEARCH_RESULT: + { + struct Upnp_Discovery *d_event = + ( struct Upnp_Discovery * )Event; + + SampleUtil_Print( "ErrCode = %d\n", + d_event->ErrCode ); + SampleUtil_Print( "Expires = %d\n", + d_event->Expires ); + SampleUtil_Print( "DeviceId = %s\n", + d_event->DeviceId ); + SampleUtil_Print( "DeviceType = %s\n", + d_event->DeviceType ); + SampleUtil_Print( "ServiceType = %s\n", + d_event->ServiceType ); + SampleUtil_Print( "ServiceVer = %s\n", + d_event->ServiceVer ); + SampleUtil_Print( "Location = %s\n", + d_event->Location ); + SampleUtil_Print( "OS = %s\n", d_event->Os ); + SampleUtil_Print( "Ext = %s\n", d_event->Ext ); + + } + break; + + case UPNP_DISCOVERY_SEARCH_TIMEOUT: + // Nothing to print out here + break; + + /* + SOAP + */ + case UPNP_CONTROL_ACTION_REQUEST: + { + struct Upnp_Action_Request *a_event = + ( struct Upnp_Action_Request * )Event; + char *xmlbuff = NULL; + + SampleUtil_Print( "ErrCode = %d\n", + a_event->ErrCode ); + SampleUtil_Print( "ErrStr = %s\n", a_event->ErrStr ); + SampleUtil_Print( "ActionName = %s\n", + a_event->ActionName ); + SampleUtil_Print( "UDN = %s\n", a_event->DevUDN ); + SampleUtil_Print( "ServiceID = %s\n", + a_event->ServiceID ); + if( a_event->ActionRequest ) { + xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionRequest ); + if( xmlbuff ) + SampleUtil_Print( "ActRequest = %s\n", xmlbuff ); + if( xmlbuff ) + ixmlFreeDOMString( xmlbuff ); + xmlbuff = NULL; + } else { + SampleUtil_Print( "ActRequest = (null)\n" ); + } + + if( a_event->ActionResult ) { + xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionResult ); + if( xmlbuff ) + SampleUtil_Print( "ActResult = %s\n", xmlbuff ); + if( xmlbuff ) + ixmlFreeDOMString( xmlbuff ); + xmlbuff = NULL; + } else { + SampleUtil_Print( "ActResult = (null)\n" ); + } + } + break; + + case UPNP_CONTROL_ACTION_COMPLETE: + { + struct Upnp_Action_Complete *a_event = + ( struct Upnp_Action_Complete * )Event; + char *xmlbuff = NULL; + + SampleUtil_Print( "ErrCode = %d\n", + a_event->ErrCode ); + SampleUtil_Print( "CtrlUrl = %s\n", + a_event->CtrlUrl ); + if( a_event->ActionRequest ) { + xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionRequest ); + if( xmlbuff ) + SampleUtil_Print( "ActRequest = %s\n", xmlbuff ); + if( xmlbuff ) + ixmlFreeDOMString( xmlbuff ); + xmlbuff = NULL; + } else { + SampleUtil_Print( "ActRequest = (null)\n" ); + } + + if( a_event->ActionResult ) { + xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionResult ); + if( xmlbuff ) + SampleUtil_Print( "ActResult = %s\n", xmlbuff ); + if( xmlbuff ) + ixmlFreeDOMString( xmlbuff ); + xmlbuff = NULL; + } else { + SampleUtil_Print( "ActResult = (null)\n" ); + } + } + break; + + case UPNP_CONTROL_GET_VAR_REQUEST: + { + struct Upnp_State_Var_Request *sv_event = + ( struct Upnp_State_Var_Request * )Event; + + SampleUtil_Print( "ErrCode = %d\n", + sv_event->ErrCode ); + SampleUtil_Print( "ErrStr = %s\n", + sv_event->ErrStr ); + SampleUtil_Print( "UDN = %s\n", + sv_event->DevUDN ); + SampleUtil_Print( "ServiceID = %s\n", + sv_event->ServiceID ); + SampleUtil_Print( "StateVarName= %s\n", + sv_event->StateVarName ); + SampleUtil_Print( "CurrentVal = %s\n", + sv_event->CurrentVal ); + } + break; + + case UPNP_CONTROL_GET_VAR_COMPLETE: + { + struct Upnp_State_Var_Complete *sv_event = + ( struct Upnp_State_Var_Complete * )Event; + + SampleUtil_Print( "ErrCode = %d\n", + sv_event->ErrCode ); + SampleUtil_Print( "CtrlUrl = %s\n", + sv_event->CtrlUrl ); + SampleUtil_Print( "StateVarName= %s\n", + sv_event->StateVarName ); + SampleUtil_Print( "CurrentVal = %s\n", + sv_event->CurrentVal ); + } + break; + + /* + GENA + */ + case UPNP_EVENT_SUBSCRIPTION_REQUEST: + { + struct Upnp_Subscription_Request *sr_event = + ( struct Upnp_Subscription_Request * )Event; + + SampleUtil_Print( "ServiceID = %s\n", + sr_event->ServiceId ); + SampleUtil_Print( "UDN = %s\n", sr_event->UDN ); + SampleUtil_Print( "SID = %s\n", sr_event->Sid ); + } + break; + + case UPNP_EVENT_RECEIVED: + { + struct Upnp_Event *e_event = ( struct Upnp_Event * )Event; + char *xmlbuff = NULL; + + SampleUtil_Print( "SID = %s\n", e_event->Sid ); + SampleUtil_Print( "EventKey = %d\n", + e_event->EventKey ); + xmlbuff = ixmlPrintNode( ( IXML_Node * ) e_event->ChangedVariables ); + SampleUtil_Print( "ChangedVars = %s\n", xmlbuff ); + ixmlFreeDOMString( xmlbuff ); + xmlbuff = NULL; + } + break; + + case UPNP_EVENT_RENEWAL_COMPLETE: + { + struct Upnp_Event_Subscribe *es_event = + ( struct Upnp_Event_Subscribe * )Event; + + SampleUtil_Print( "SID = %s\n", es_event->Sid ); + SampleUtil_Print( "ErrCode = %d\n", + es_event->ErrCode ); + SampleUtil_Print( "TimeOut = %d\n", + es_event->TimeOut ); + } + break; + + case UPNP_EVENT_SUBSCRIBE_COMPLETE: + case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: + { + struct Upnp_Event_Subscribe *es_event = + ( struct Upnp_Event_Subscribe * )Event; + + SampleUtil_Print( "SID = %s\n", es_event->Sid ); + SampleUtil_Print( "ErrCode = %d\n", + es_event->ErrCode ); + SampleUtil_Print( "PublisherURL= %s\n", + es_event->PublisherUrl ); + SampleUtil_Print( "TimeOut = %d\n", + es_event->TimeOut ); + } + break; + + case UPNP_EVENT_AUTORENEWAL_FAILED: + case UPNP_EVENT_SUBSCRIPTION_EXPIRED: + { + struct Upnp_Event_Subscribe *es_event = + ( struct Upnp_Event_Subscribe * )Event; + + SampleUtil_Print( "SID = %s\n", es_event->Sid ); + SampleUtil_Print( "ErrCode = %d\n", + es_event->ErrCode ); + SampleUtil_Print( "PublisherURL= %s\n", + es_event->PublisherUrl ); + SampleUtil_Print( "TimeOut = %d\n", + es_event->TimeOut ); + } + break; + + } + SampleUtil_Print + ( "----------------------------------------------------------------------\n" ); + SampleUtil_Print + ( "======================================================================\n\n\n\n" ); + + ithread_mutex_unlock( &display_mutex ); + return ( 0 ); +} + +/******************************************************************************** + * SampleUtil_FindAndParseService + * + * Description: + * This routine finds the first occurance of a service in a DOM representation + * of a description document and parses it. + * + * Parameters: + * DescDoc -- The DOM description document + * location -- The location of the description document + * serviceSearchType -- The type of service to search for + * serviceId -- OUT -- The service ID + * eventURL -- OUT -- The event URL for the service + * controlURL -- OUT -- The control URL for the service + * + ********************************************************************************/ +int +SampleUtil_FindAndParseService( IN IXML_Document * DescDoc, + IN char *location, + IN char *serviceType, + OUT char **serviceId, + OUT char **eventURL, + OUT char **controlURL ) +{ + int i, + length, + found = 0; + int ret; + char *tempServiceType = NULL; + char *baseURL = NULL; + char *base; + char *relcontrolURL = NULL, + *releventURL = NULL; + IXML_NodeList *serviceList = NULL; + IXML_Element *service = NULL; + + baseURL = SampleUtil_GetFirstDocumentItem( DescDoc, "URLBase" ); + + if( baseURL ) + base = baseURL; + else + base = location; + + serviceList = SampleUtil_GetFirstServiceList( DescDoc ); + length = ixmlNodeList_length( serviceList ); + for( i = 0; i < length; i++ ) { + service = ( IXML_Element * ) ixmlNodeList_item( serviceList, i ); + tempServiceType = + SampleUtil_GetFirstElementItem( ( IXML_Element * ) service, + "serviceType" ); + + if( strcmp( tempServiceType, serviceType ) == 0 ) { + SampleUtil_Print( "Found service: %s\n", serviceType ); + *serviceId = + SampleUtil_GetFirstElementItem( service, "serviceId" ); + SampleUtil_Print( "serviceId: %s\n", ( *serviceId ) ); + relcontrolURL = + SampleUtil_GetFirstElementItem( service, "controlURL" ); + releventURL = + SampleUtil_GetFirstElementItem( service, "eventSubURL" ); + + *controlURL = + malloc( strlen( base ) + strlen( relcontrolURL ) + 1 ); + if( *controlURL ) { + ret = UpnpResolveURL( base, relcontrolURL, *controlURL ); + if( ret != UPNP_E_SUCCESS ) + SampleUtil_Print + ( "Error generating controlURL from %s + %s\n", + base, relcontrolURL ); + } + + *eventURL = + malloc( strlen( base ) + strlen( releventURL ) + 1 ); + if( *eventURL ) { + ret = UpnpResolveURL( base, releventURL, *eventURL ); + if( ret != UPNP_E_SUCCESS ) + SampleUtil_Print + ( "Error generating eventURL from %s + %s\n", base, + releventURL ); + } + + if( relcontrolURL ) + free( relcontrolURL ); + if( releventURL ) + free( releventURL ); + relcontrolURL = releventURL = NULL; + + found = 1; + break; + } + + if( tempServiceType ) + free( tempServiceType ); + tempServiceType = NULL; + } + + if( tempServiceType ) + free( tempServiceType ); + if( serviceList ) + ixmlNodeList_free( serviceList ); + if( baseURL ) + free( baseURL ); + + return ( found ); +} + +// printf wrapper for portable code +int +uprint1( char *fmt, + ... ) +{ + va_list ap; + char *buf = NULL; + int size; + + va_start( ap, fmt ); + size = vsnprintf( buf, 0, fmt, ap ); + va_end( ap ); + + if( size > 0 ) { + buf = ( char * )malloc( size + 1 ); + if( vsnprintf( buf, size + 1, fmt, ap ) != size ) { + free( buf ); + buf = NULL; + } + } + + if( buf ) { + ithread_mutex_lock( &display_mutex ); + if( gPrintFun ) + gPrintFun( buf ); + ithread_mutex_unlock( &display_mutex ); + free( buf ); + } + + return size; +} + +/******************************************************************************** + * SampleUtil_Print + * + * Description: + * Provides platform-specific print functionality. This function should be + * called when you want to print content suitable for console output (i.e., + * in a large text box or on a screen). If your device/operating system is + * not supported here, you should add a port. + * + * Parameters: + * Same as printf() + * + ********************************************************************************/ +int +SampleUtil_Print( char *fmt, + ... ) +{ + va_list ap; + char buf[200]; + int rc; + + va_start( ap, fmt ); + rc = vsnprintf( buf, 200, fmt, ap ); + va_end( ap ); + + ithread_mutex_lock( &display_mutex ); + if( gPrintFun ) + gPrintFun( buf ); + ithread_mutex_unlock( &display_mutex ); + + return rc; +} + +/******************************************************************************** + * SampleUtil_StateUpdate + * + * Description: + * + * Parameters: + * + ********************************************************************************/ +void +SampleUtil_StateUpdate( const char *varName, + const char *varValue, + const char *UDN, + eventType type ) +{ + if( gStateUpdateFun ) + gStateUpdateFun( varName, varValue, UDN, type ); + // TBD: Add mutex here? +} diff --git a/libupnp/upnp/sample/common/sample_util.h b/libupnp/upnp/sample/common/sample_util.h new file mode 100644 index 0000000..9295850 --- /dev/null +++ b/libupnp/upnp/sample/common/sample_util.h @@ -0,0 +1,268 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef SAMPLE_UTIL_H +#define SAMPLE_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include "upnptools.h" +#include "ithread.h" +#include "ixml.h" + +//mutex to control displaying of events +extern ithread_mutex_t display_mutex ; + +typedef enum { + STATE_UPDATE = 0, + DEVICE_ADDED =1, + DEVICE_REMOVED=2, + GET_VAR_COMPLETE=3 +} eventType; + + +/******************************************************************************** + * SampleUtil_GetElementValue + * + * Description: + * Given a DOM node such as 11, this routine + * extracts the value (e.g., 11) from the node and returns it as + * a string. The string must be freed by the caller using + * free. + * + * Parameters: + * node -- The DOM node from which to extract the value + * + ********************************************************************************/ +char * SampleUtil_GetElementValue(IN IXML_Element *element); + +/******************************************************************************** + * SampleUtil_GetFirstServiceList + * + * Description: + * Given a DOM node representing a UPnP Device Description Document, + * this routine parses the document and finds the first service list + * (i.e., the service list for the root device). The service list + * is returned as a DOM node list. The NodeList must be freed using + * NodeList_free. + * + * Parameters: + * node -- The DOM node from which to extract the service list + * + ********************************************************************************/ + +IXML_NodeList *SampleUtil_GetFirstServiceList(IN IXML_Document * doc); + + +/******************************************************************************** + * SampleUtil_GetFirstDocumentItem + * + * Description: + * Given a document node, this routine searches for the first element + * named by the input string item, and returns its value as a string. + * String must be freed by caller using free. + * Parameters: + * doc -- The DOM document from which to extract the value + * item -- The item to search for + * + ********************************************************************************/ +char * SampleUtil_GetFirstDocumentItem(IN IXML_Document *doc, IN const char *item); + + + +/******************************************************************************** + * SampleUtil_GetFirstElementItem + * + * Description: + * Given a DOM element, this routine searches for the first element + * named by the input string item, and returns its value as a string. + * The string must be freed using free. + * Parameters: + * node -- The DOM element from which to extract the value + * item -- The item to search for + * + ********************************************************************************/ +char * SampleUtil_GetFirstElementItem(IN IXML_Element *element, IN const char *item); + +/******************************************************************************** + * SampleUtil_PrintEventType + * + * Description: + * Prints a callback event type as a string. + * + * Parameters: + * S -- The callback event + * + ********************************************************************************/ +void SampleUtil_PrintEventType(IN Upnp_EventType S); + +/******************************************************************************** + * SampleUtil_PrintEvent + * + * Description: + * Prints callback event structure details. + * + * Parameters: + * EventType -- The type of callback event + * Event -- The callback event structure + * + ********************************************************************************/ +int SampleUtil_PrintEvent(IN Upnp_EventType EventType, + IN void *Event); + +/******************************************************************************** + * SampleUtil_FindAndParseService + * + * Description: + * This routine finds the first occurance of a service in a DOM representation + * of a description document and parses it. Note that this function currently + * assumes that the eventURL and controlURL values in the service definitions + * are full URLs. Relative URLs are not handled here. + * + * Parameters: + * DescDoc -- The DOM description document + * location -- The location of the description document + * serviceSearchType -- The type of service to search for + * serviceId -- OUT -- The service ID + * eventURL -- OUT -- The event URL for the service + * controlURL -- OUT -- The control URL for the service + * + ********************************************************************************/ +int SampleUtil_FindAndParseService (IN IXML_Document *DescDoc, IN char* location, + IN char *serviceType, OUT char **serviceId, + OUT char **eventURL, OUT char **controlURL); + + +/******************************************************************************** + * print_string + * + * Description: + * Prototype for displaying strings. All printing done by the device, + * control point, and sample util, ultimately use this to display strings + * to the user. + * + * Parameters: + * const char * string. + * + ********************************************************************************/ +typedef void (*print_string)(const char *string); + +//global print function used by sample util +extern print_string gPrintFun; + +/******************************************************************************** + * state_update + * + * Description: + * Prototype for passing back state changes + * + * Parameters: + * const char * varName + * const char * varValue + * const char * UDN + * int newDevice + ********************************************************************************/ +typedef void (*state_update)( const char *varName, const char *varValue, const char *UDN, + eventType type); + +//global state update function used by smaple util +extern state_update gStateUpdateFun; + +/******************************************************************************** + * SampleUtil_Initialize + * + * Description: + * Initializes the sample util. Must be called before any sample util + * functions. May be called multiple times. + * + * Parameters: + * print_function - print function to use in SampleUtil_Print + * + ********************************************************************************/ +int SampleUtil_Initialize(print_string print_function); + +/******************************************************************************** + * SampleUtil_Finish + * + * Description: + * Releases Resources held by sample util. + * + * Parameters: + * + ********************************************************************************/ +int SampleUtil_Finish(void); + +/******************************************************************************** + * SampleUtil_Print + * + * Description: + * Function emulating printf that ultimately calls the registered print + * function with the formatted string. + * + * Parameters: + * fmt - format (see printf) + * . . . - variable number of args. (see printf) + * + ********************************************************************************/ +int SampleUtil_Print( char *fmt, ... ); + +/******************************************************************************** + * SampleUtil_RegisterUpdateFunction + * + * Description: + * + * Parameters: + * + ********************************************************************************/ +int SampleUtil_RegisterUpdateFunction( state_update update_function ); + +/******************************************************************************** + * SampleUtil_StateUpdate + * + * Description: + * + * Parameters: + * + ********************************************************************************/ +void SampleUtil_StateUpdate( const char *varName, const char *varValue, const char *UDN, + eventType type); + +#ifdef __cplusplus +}; +#endif + +#endif /* UPNPSDK_UTIL_H */ diff --git a/libupnp/upnp/sample/tvctrlpt/linux/upnp_tv_ctrlpt_main.c b/libupnp/upnp/sample/tvctrlpt/linux/upnp_tv_ctrlpt_main.c new file mode 100644 index 0000000..8a26f72 --- /dev/null +++ b/libupnp/upnp/sample/tvctrlpt/linux/upnp_tv_ctrlpt_main.c @@ -0,0 +1,467 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include "sample_util.h" +#include "upnp_tv_ctrlpt.h" +#include + +/* + Tags for valid commands issued at the command prompt + */ +enum cmdloop_tvcmds { + PRTHELP = 0, PRTFULLHELP, POWON, POWOFF, + SETCHAN, SETVOL, SETCOL, SETTINT, SETCONT, SETBRT, + CTRLACTION, PICTACTION, CTRLGETVAR, PICTGETVAR, + PRTDEV, LSTDEV, REFRESH, EXITCMD +}; + +/* + Data structure for parsing commands from the command line + */ +struct cmdloop_commands { + char *str; // the string + int cmdnum; // the command + int numargs; // the number of arguments + char *args; // the args +} cmdloop_commands; + +/* + Mappings between command text names, command tag, + and required command arguments for command line + commands + */ +static struct cmdloop_commands cmdloop_cmdlist[] = { + {"Help", PRTHELP, 1, ""}, + {"HelpFull", PRTFULLHELP, 1, ""}, + {"ListDev", LSTDEV, 1, ""}, + {"Refresh", REFRESH, 1, ""}, + {"PrintDev", PRTDEV, 2, ""}, + {"PowerOn", POWON, 2, ""}, + {"PowerOff", POWOFF, 2, ""}, + {"SetChannel", SETCHAN, 3, " "}, + {"SetVolume", SETVOL, 3, " "}, + {"SetColor", SETCOL, 3, " "}, + {"SetTint", SETTINT, 3, " "}, + {"SetContrast", SETCONT, 3, " "}, + {"SetBrightness", SETBRT, 3, " "}, + {"CtrlAction", CTRLACTION, 2, " "}, + {"PictAction", PICTACTION, 2, " "}, + {"CtrlGetVar", CTRLGETVAR, 2, " "}, + {"PictGetVar", PICTGETVAR, 2, " "}, + {"Exit", EXITCMD, 1, ""} +}; + +void +linux_print( const char *string ) +{ + puts( string ); +} + +/******************************************************************************** + * TvCtrlPointPrintHelp + * + * Description: + * Print help info for this application. + ********************************************************************************/ +void +TvCtrlPointPrintShortHelp( void ) +{ + SampleUtil_Print( "Commands:" ); + SampleUtil_Print( " Help" ); + SampleUtil_Print( " HelpFull" ); + SampleUtil_Print( " ListDev" ); + SampleUtil_Print( " Refresh" ); + SampleUtil_Print( " PrintDev " ); + SampleUtil_Print( " PowerOn " ); + SampleUtil_Print( " PowerOff " ); + SampleUtil_Print( " SetChannel " ); + SampleUtil_Print( " SetVolume " ); + SampleUtil_Print( " SetColor " ); + SampleUtil_Print( " SetTint " ); + SampleUtil_Print( " SetContrast " ); + SampleUtil_Print( " SetBrightness " ); + SampleUtil_Print( " CtrlAction " ); + SampleUtil_Print( " PictAction " ); + SampleUtil_Print( " CtrlGetVar " ); + SampleUtil_Print( " PictGetVar " ); + SampleUtil_Print( " Exit" ); +} + +void +TvCtrlPointPrintLongHelp( void ) +{ + SampleUtil_Print( "" ); + SampleUtil_Print( "******************************" ); + SampleUtil_Print( "* TV Control Point Help Info *" ); + SampleUtil_Print( "******************************" ); + SampleUtil_Print( "" ); + SampleUtil_Print + ( "This sample control point application automatically searches" ); + SampleUtil_Print + ( "for and subscribes to the services of television device emulator" ); + SampleUtil_Print + ( "devices, described in the tvdevicedesc.xml description document." ); + SampleUtil_Print( "" ); + SampleUtil_Print( "Commands:" ); + SampleUtil_Print( " Help" ); + SampleUtil_Print( " Print this help info." ); + SampleUtil_Print( " ListDev" ); + SampleUtil_Print + ( " Print the current list of TV Device Emulators that this" ); + SampleUtil_Print + ( " control point is aware of. Each device is preceded by a" ); + SampleUtil_Print + ( " device number which corresponds to the devnum argument of" ); + SampleUtil_Print( " commands listed below." ); + SampleUtil_Print( " Refresh" ); + SampleUtil_Print + ( " Delete all of the devices from the device list and issue new" ); + SampleUtil_Print + ( " search request to rebuild the list from scratch." ); + SampleUtil_Print( " PrintDev " ); + SampleUtil_Print + ( " Print the state table for the device ." ); + SampleUtil_Print + ( " e.g., 'PrintDev 1' prints the state table for the first" ); + SampleUtil_Print( " device in the device list." ); + SampleUtil_Print( " PowerOn " ); + SampleUtil_Print + ( " Sends the PowerOn action to the Control Service of" ); + SampleUtil_Print( " device ." ); + SampleUtil_Print( " PowerOff " ); + SampleUtil_Print + ( " Sends the PowerOff action to the Control Service of" ); + SampleUtil_Print( " device ." ); + SampleUtil_Print( " SetChannel " ); + SampleUtil_Print + ( " Sends the SetChannel action to the Control Service of" ); + SampleUtil_Print + ( " device , requesting the channel to be changed" ); + SampleUtil_Print( " to ." ); + SampleUtil_Print( " SetVolume " ); + SampleUtil_Print + ( " Sends the SetVolume action to the Control Service of" ); + SampleUtil_Print + ( " device , requesting the volume to be changed" ); + SampleUtil_Print( " to ." ); + SampleUtil_Print( " SetColor " ); + SampleUtil_Print + ( " Sends the SetColor action to the Control Service of" ); + SampleUtil_Print + ( " device , requesting the color to be changed" ); + SampleUtil_Print( " to ." ); + SampleUtil_Print( " SetTint " ); + SampleUtil_Print + ( " Sends the SetTint action to the Control Service of" ); + SampleUtil_Print + ( " device , requesting the tint to be changed" ); + SampleUtil_Print( " to ." ); + SampleUtil_Print( " SetContrast " ); + SampleUtil_Print + ( " Sends the SetContrast action to the Control Service of" ); + SampleUtil_Print + ( " device , requesting the contrast to be changed" ); + SampleUtil_Print( " to ." ); + SampleUtil_Print( " SetBrightness " ); + SampleUtil_Print + ( " Sends the SetBrightness action to the Control Service of" ); + SampleUtil_Print + ( " device , requesting the brightness to be changed" ); + SampleUtil_Print( " to ." ); + SampleUtil_Print( " CtrlAction " ); + SampleUtil_Print + ( " Sends an action request specified by the string " ); + SampleUtil_Print + ( " to the Control Service of device . This command" ); + SampleUtil_Print + ( " only works for actions that have no arguments." ); + SampleUtil_Print + ( " (e.g., \"CtrlAction 1 IncreaseChannel\")" ); + SampleUtil_Print( " PictAction " ); + SampleUtil_Print + ( " Sends an action request specified by the string " ); + SampleUtil_Print + ( " to the Picture Service of device . This command" ); + SampleUtil_Print + ( " only works for actions that have no arguments." ); + SampleUtil_Print + ( " (e.g., \"PictAction 1 DecreaseContrast\")" ); + SampleUtil_Print( " CtrlGetVar " ); + SampleUtil_Print + ( " Requests the value of a variable specified by the string " ); + SampleUtil_Print + ( " from the Control Service of device ." ); + SampleUtil_Print( " (e.g., \"CtrlGetVar 1 Volume\")" ); + SampleUtil_Print( " PictGetVar " ); + SampleUtil_Print + ( " Requests the value of a variable specified by the string " ); + SampleUtil_Print + ( " from the Picture Service of device ." ); + SampleUtil_Print( " (e.g., \"PictGetVar 1 Tint\")" ); + SampleUtil_Print( " Exit" ); + SampleUtil_Print( " Exits the control point application." ); +} + +/******************************************************************************** + * TvCtrlPointPrintCommands + * + * Description: + * Print the list of valid command line commands to the user + * + * Parameters: + * None + * + ********************************************************************************/ +void +TvCtrlPointPrintCommands( ) +{ + int i; + int numofcmds = sizeof( cmdloop_cmdlist ) / sizeof( cmdloop_commands ); + + SampleUtil_Print( "Valid Commands:" ); + for( i = 0; i < numofcmds; i++ ) { + SampleUtil_Print( " %-14s %s", cmdloop_cmdlist[i].str, + cmdloop_cmdlist[i].args ); + } + SampleUtil_Print( "" ); +} + +/******************************************************************************** + * TvCtrlPointCommandLoop + * + * Description: + * Function that receives commands from the user at the command prompt + * during the lifetime of the control point, and calls the appropriate + * functions for those commands. + * + * Parameters: + * None + * + ********************************************************************************/ +void * +TvCtrlPointCommandLoop( void *args ) +{ + char cmdline[100]; + + while( 1 ) { + SampleUtil_Print( "\n>> " ); + fgets( cmdline, 100, stdin ); + TvCtrlPointProcessCommand( cmdline ); + } + + return NULL; +} + +int +TvCtrlPointProcessCommand( char *cmdline ) +{ + char cmd[100]; + char strarg[100]; + int arg_val_err = -99999; + int arg1 = arg_val_err; + int arg2 = arg_val_err; + int cmdnum = -1; + int numofcmds = sizeof( cmdloop_cmdlist ) / sizeof( cmdloop_commands ); + int cmdfound = 0; + int i, + rc; + int invalidargs = 0; + int validargs; + + validargs = sscanf( cmdline, "%s %d %d", cmd, &arg1, &arg2 ); + + for( i = 0; i < numofcmds; i++ ) { + if( strcasecmp( cmd, cmdloop_cmdlist[i].str ) == 0 ) { + cmdnum = cmdloop_cmdlist[i].cmdnum; + cmdfound++; + if( validargs != cmdloop_cmdlist[i].numargs ) + invalidargs++; + break; + } + } + + if( !cmdfound ) { + SampleUtil_Print( "Command not found; try 'Help'" ); + return TV_SUCCESS; + } + + if( invalidargs ) { + SampleUtil_Print( "Invalid arguments; try 'Help'" ); + return TV_SUCCESS; + } + + switch ( cmdnum ) { + case PRTHELP: + TvCtrlPointPrintShortHelp( ); + break; + + case PRTFULLHELP: + TvCtrlPointPrintLongHelp( ); + break; + + case POWON: + TvCtrlPointSendPowerOn( arg1 ); + break; + + case POWOFF: + TvCtrlPointSendPowerOff( arg1 ); + break; + + case SETCHAN: + TvCtrlPointSendSetChannel( arg1, arg2 ); + break; + + case SETVOL: + TvCtrlPointSendSetVolume( arg1, arg2 ); + break; + + case SETCOL: + TvCtrlPointSendSetColor( arg1, arg2 ); + break; + + case SETTINT: + TvCtrlPointSendSetTint( arg1, arg2 ); + break; + + case SETCONT: + TvCtrlPointSendSetContrast( arg1, arg2 ); + break; + + case SETBRT: + TvCtrlPointSendSetBrightness( arg1, arg2 ); + break; + + case CTRLACTION: + /* + re-parse commandline since second arg is string + */ + validargs = sscanf( cmdline, "%s %d %s", cmd, &arg1, strarg ); + if( 3 == validargs ) + TvCtrlPointSendAction( TV_SERVICE_CONTROL, arg1, strarg, + NULL, NULL, 0 ); + else + invalidargs++; + break; + + case PICTACTION: + /* + re-parse commandline since second arg is string + */ + validargs = sscanf( cmdline, "%s %d %s", cmd, &arg1, strarg ); + if( 3 == validargs ) + TvCtrlPointSendAction( TV_SERVICE_PICTURE, arg1, strarg, + NULL, NULL, 0 ); + else + invalidargs++; + break; + + case CTRLGETVAR: + /* + re-parse commandline since second arg is string + */ + validargs = sscanf( cmdline, "%s %d %s", cmd, &arg1, strarg ); + if( 3 == validargs ) + TvCtrlPointGetVar( TV_SERVICE_CONTROL, arg1, strarg ); + else + invalidargs++; + break; + + case PICTGETVAR: + /* + re-parse commandline since second arg is string + */ + validargs = sscanf( cmdline, "%s %d %s", cmd, &arg1, strarg ); + if( 3 == validargs ) + TvCtrlPointGetVar( TV_SERVICE_PICTURE, arg1, strarg ); + else + invalidargs++; + break; + + case PRTDEV: + TvCtrlPointPrintDevice( arg1 ); + break; + + case LSTDEV: + TvCtrlPointPrintList( ); + break; + + case REFRESH: + TvCtrlPointRefresh( ); + break; + + case EXITCMD: + rc = TvCtrlPointStop( ); + exit( rc ); + break; + + default: + SampleUtil_Print( "Command not implemented; see 'Help'" ); + break; + } + + if( invalidargs ) + SampleUtil_Print( "Invalid args in command; see 'Help'" ); + + return TV_SUCCESS; +} + +int +main( int argc, + char **argv ) +{ + int rc; + ithread_t cmdloop_thread; + int sig; + sigset_t sigs_to_catch; + int code; + + rc = TvCtrlPointStart( linux_print, NULL ); + if( rc != TV_SUCCESS ) { + SampleUtil_Print( "Error starting UPnP TV Control Point" ); + exit( rc ); + } + // start a command loop thread + code = + ithread_create( &cmdloop_thread, NULL, TvCtrlPointCommandLoop, + NULL ); + + /* + Catch Ctrl-C and properly shutdown + */ + sigemptyset( &sigs_to_catch ); + sigaddset( &sigs_to_catch, SIGINT ); + sigwait( &sigs_to_catch, &sig ); + + SampleUtil_Print( "Shutting down on signal %d...", sig ); + rc = TvCtrlPointStop( ); + exit( rc ); +} diff --git a/libupnp/upnp/sample/tvctrlpt/upnp_tv_ctrlpt.c b/libupnp/upnp/sample/tvctrlpt/upnp_tv_ctrlpt.c new file mode 100644 index 0000000..d84e079 --- /dev/null +++ b/libupnp/upnp/sample/tvctrlpt/upnp_tv_ctrlpt.c @@ -0,0 +1,1409 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "upnp_tv_ctrlpt.h" + +/* + Mutex for protecting the global device list + in a multi-threaded, asynchronous environment. + All functions should lock this mutex before reading + or writing the device list. + */ +ithread_mutex_t DeviceListMutex; + +UpnpClient_Handle ctrlpt_handle = -1; + +char TvDeviceType[] = "urn:schemas-upnp-org:device:tvdevice:1"; +char *TvServiceType[] = { + "urn:schemas-upnp-org:service:tvcontrol:1", + "urn:schemas-upnp-org:service:tvpicture:1" +}; +char *TvServiceName[] = { "Control", "Picture" }; + +/* + Global arrays for storing variable names and counts for + TvControl and TvPicture services + */ +char *TvVarName[TV_SERVICE_SERVCOUNT][TV_MAXVARS] = { + {"Power", "Channel", "Volume", ""}, + {"Color", "Tint", "Contrast", "Brightness"} +}; +char TvVarCount[TV_SERVICE_SERVCOUNT] = + { TV_CONTROL_VARCOUNT, TV_PICTURE_VARCOUNT }; + +/* + Timeout to request during subscriptions + */ +int default_timeout = 1801; + +/* + The first node in the global device list, or NULL if empty + */ +struct TvDeviceNode *GlobalDeviceList = NULL; + +/******************************************************************************** + * TvCtrlPointDeleteNode + * + * Description: + * Delete a device node from the global device list. Note that this + * function is NOT thread safe, and should be called from another + * function that has already locked the global device list. + * + * Parameters: + * node -- The device node + * + ********************************************************************************/ +int +TvCtrlPointDeleteNode( struct TvDeviceNode *node ) +{ + int rc, + service, + var; + + if( NULL == node ) { + SampleUtil_Print( "ERROR: TvCtrlPointDeleteNode: Node is empty" ); + return TV_ERROR; + } + + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + /* + If we have a valid control SID, then unsubscribe + */ + if( strcmp( node->device.TvService[service].SID, "" ) != 0 ) { + rc = UpnpUnSubscribe( ctrlpt_handle, + node->device.TvService[service].SID ); + if( UPNP_E_SUCCESS == rc ) { + SampleUtil_Print + ( "Unsubscribed from Tv %s EventURL with SID=%s", + TvServiceName[service], + node->device.TvService[service].SID ); + } else { + SampleUtil_Print + ( "Error unsubscribing to Tv %s EventURL -- %d", + TvServiceName[service], rc ); + } + } + + for( var = 0; var < TvVarCount[service]; var++ ) { + if( node->device.TvService[service].VariableStrVal[var] ) { + free( node->device.TvService[service]. + VariableStrVal[var] ); + } + } + } + + //Notify New Device Added + SampleUtil_StateUpdate( NULL, NULL, node->device.UDN, DEVICE_REMOVED ); + free( node ); + node = NULL; + + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointRemoveDevice + * + * Description: + * Remove a device from the global device list. + * + * Parameters: + * UDN -- The Unique Device Name for the device to remove + * + ********************************************************************************/ +int +TvCtrlPointRemoveDevice( char *UDN ) +{ + struct TvDeviceNode *curdevnode, + *prevdevnode; + + ithread_mutex_lock( &DeviceListMutex ); + + curdevnode = GlobalDeviceList; + if( !curdevnode ) { + SampleUtil_Print + ( "WARNING: TvCtrlPointRemoveDevice: Device list empty" ); + } else { + if( 0 == strcmp( curdevnode->device.UDN, UDN ) ) { + GlobalDeviceList = curdevnode->next; + TvCtrlPointDeleteNode( curdevnode ); + } else { + prevdevnode = curdevnode; + curdevnode = curdevnode->next; + + while( curdevnode ) { + if( strcmp( curdevnode->device.UDN, UDN ) == 0 ) { + prevdevnode->next = curdevnode->next; + TvCtrlPointDeleteNode( curdevnode ); + break; + } + + prevdevnode = curdevnode; + curdevnode = curdevnode->next; + } + } + } + + ithread_mutex_unlock( &DeviceListMutex ); + + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointRemoveAll + * + * Description: + * Remove all devices from the global device list. + * + * Parameters: + * None + * + ********************************************************************************/ +int +TvCtrlPointRemoveAll( void ) +{ + struct TvDeviceNode *curdevnode, + *next; + + ithread_mutex_lock( &DeviceListMutex ); + + curdevnode = GlobalDeviceList; + GlobalDeviceList = NULL; + + while( curdevnode ) { + next = curdevnode->next; + TvCtrlPointDeleteNode( curdevnode ); + curdevnode = next; + } + + ithread_mutex_unlock( &DeviceListMutex ); + + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointRefresh + * + * Description: + * Clear the current global device list and issue new search + * requests to build it up again from scratch. + * + * Parameters: + * None + * + ********************************************************************************/ +int +TvCtrlPointRefresh( void ) +{ + int rc; + + TvCtrlPointRemoveAll( ); + + /* + Search for all devices of type tvdevice version 1, + waiting for up to 5 seconds for the response + */ + rc = UpnpSearchAsync( ctrlpt_handle, 5, TvDeviceType, NULL ); + if( UPNP_E_SUCCESS != rc ) { + SampleUtil_Print( "Error sending search request%d", rc ); + return TV_ERROR; + } + + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointGetVar + * + * Description: + * Send a GetVar request to the specified service of a device. + * + * Parameters: + * service -- The service + * devnum -- The number of the device (order in the list, + * starting with 1) + * varname -- The name of the variable to request. + * + ********************************************************************************/ +int +TvCtrlPointGetVar( int service, + int devnum, + char *varname ) +{ + struct TvDeviceNode *devnode; + int rc; + + ithread_mutex_lock( &DeviceListMutex ); + + rc = TvCtrlPointGetDevice( devnum, &devnode ); + + if( TV_SUCCESS == rc ) { + rc = UpnpGetServiceVarStatusAsync( ctrlpt_handle, + devnode->device. + TvService[service].ControlURL, + varname, + TvCtrlPointCallbackEventHandler, + NULL ); + if( rc != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error in UpnpGetServiceVarStatusAsync -- %d", rc ); + rc = TV_ERROR; + } + } + + ithread_mutex_unlock( &DeviceListMutex ); + + return rc; +} + +int +TvCtrlPointGetPower( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_CONTROL, devnum, "Power" ); +} + +int +TvCtrlPointGetChannel( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_CONTROL, devnum, "Channel" ); +} + +int +TvCtrlPointGetVolume( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_CONTROL, devnum, "Volume" ); +} + +int +TvCtrlPointGetColor( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Color" ); +} + +int +TvCtrlPointGetTint( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Tint" ); +} + +int +TvCtrlPointGetContrast( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Contrast" ); +} + +int +TvCtrlPointGetBrightness( int devnum ) +{ + return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Brightness" ); +} + +/******************************************************************************** + * TvCtrlPointSendAction + * + * Description: + * Send an Action request to the specified service of a device. + * + * Parameters: + * service -- The service + * devnum -- The number of the device (order in the list, + * starting with 1) + * actionname -- The name of the action. + * param_name -- An array of parameter names + * param_val -- The corresponding parameter values + * param_count -- The number of parameters + * + ********************************************************************************/ +int +TvCtrlPointSendAction( int service, + int devnum, + char *actionname, + char **param_name, + char **param_val, + int param_count ) +{ + struct TvDeviceNode *devnode; + IXML_Document *actionNode = NULL; + int rc = TV_SUCCESS; + int param; + + ithread_mutex_lock( &DeviceListMutex ); + + rc = TvCtrlPointGetDevice( devnum, &devnode ); + if( TV_SUCCESS == rc ) { + if( 0 == param_count ) { + actionNode = + UpnpMakeAction( actionname, TvServiceType[service], 0, + NULL ); + } else { + for( param = 0; param < param_count; param++ ) { + if( UpnpAddToAction + ( &actionNode, actionname, TvServiceType[service], + param_name[param], + param_val[param] ) != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "ERROR: TvCtrlPointSendAction: Trying to add action param" ); + //return -1; // TBD - BAD! leaves mutex locked + } + } + } + + rc = UpnpSendActionAsync( ctrlpt_handle, + devnode->device.TvService[service]. + ControlURL, TvServiceType[service], + NULL, actionNode, + TvCtrlPointCallbackEventHandler, NULL ); + + if( rc != UPNP_E_SUCCESS ) { + SampleUtil_Print( "Error in UpnpSendActionAsync -- %d", rc ); + rc = TV_ERROR; + } + } + + ithread_mutex_unlock( &DeviceListMutex ); + + if( actionNode ) + ixmlDocument_free( actionNode ); + + return rc; +} + +/******************************************************************************** + * TvCtrlPointSendActionNumericArg + * + * Description:Send an action with one argument to a device in the global device list. + * + * Parameters: + * devnum -- The number of the device (order in the list, starting with 1) + * service -- TV_SERVICE_CONTROL or TV_SERVICE_PICTURE + * actionName -- The device action, i.e., "SetChannel" + * paramName -- The name of the parameter that is being passed + * paramValue -- Actual value of the parameter being passed + * + ********************************************************************************/ +int +TvCtrlPointSendActionNumericArg( int devnum, + int service, + char *actionName, + char *paramName, + int paramValue ) +{ + char param_val_a[50]; + char *param_val = param_val_a; + + sprintf( param_val_a, "%d", paramValue ); + + return TvCtrlPointSendAction( service, devnum, actionName, ¶mName, + ¶m_val, 1 ); +} + +int +TvCtrlPointSendPowerOn( int devnum ) +{ + return TvCtrlPointSendAction( TV_SERVICE_CONTROL, devnum, "PowerOn", + NULL, NULL, 0 ); +} + +int +TvCtrlPointSendPowerOff( int devnum ) +{ + return TvCtrlPointSendAction( TV_SERVICE_CONTROL, devnum, "PowerOff", + NULL, NULL, 0 ); +} + +int +TvCtrlPointSendSetChannel( int devnum, + int channel ) +{ + return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_CONTROL, + "SetChannel", "Channel", + channel ); +} + +int +TvCtrlPointSendSetVolume( int devnum, + int volume ) +{ + return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_CONTROL, + "SetVolume", "Volume", + volume ); +} + +int +TvCtrlPointSendSetColor( int devnum, + int color ) +{ + return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE, + "SetColor", "Color", color ); +} + +int +TvCtrlPointSendSetTint( int devnum, + int tint ) +{ + return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE, + "SetTint", "Tint", tint ); +} + +int +TvCtrlPointSendSetContrast( int devnum, + int contrast ) +{ + return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE, + "SetContrast", "Contrast", + contrast ); +} + +int +TvCtrlPointSendSetBrightness( int devnum, + int brightness ) +{ + return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE, + "SetBrightness", "Brightness", + brightness ); +} + +/******************************************************************************** + * TvCtrlPointGetDevice + * + * Description: + * Given a list number, returns the pointer to the device + * node at that position in the global device list. Note + * that this function is not thread safe. It must be called + * from a function that has locked the global device list. + * + * Parameters: + * devnum -- The number of the device (order in the list, + * starting with 1) + * devnode -- The output device node pointer + * + ********************************************************************************/ +int +TvCtrlPointGetDevice( int devnum, + struct TvDeviceNode **devnode ) +{ + int count = devnum; + struct TvDeviceNode *tmpdevnode = NULL; + + if( count ) + tmpdevnode = GlobalDeviceList; + + while( --count && tmpdevnode ) { + tmpdevnode = tmpdevnode->next; + } + + if( !tmpdevnode ) { + SampleUtil_Print( "Error finding TvDevice number -- %d", devnum ); + return TV_ERROR; + } + + *devnode = tmpdevnode; + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointPrintList + * + * Description: + * Print the universal device names for each device in the global device list + * + * Parameters: + * None + * + ********************************************************************************/ +int +TvCtrlPointPrintList( ) +{ + struct TvDeviceNode *tmpdevnode; + int i = 0; + + ithread_mutex_lock( &DeviceListMutex ); + + SampleUtil_Print( "TvCtrlPointPrintList:" ); + tmpdevnode = GlobalDeviceList; + while( tmpdevnode ) { + SampleUtil_Print( " %3d -- %s", ++i, tmpdevnode->device.UDN ); + tmpdevnode = tmpdevnode->next; + } + SampleUtil_Print( "" ); + ithread_mutex_unlock( &DeviceListMutex ); + + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointPrintDevice + * + * Description: + * Print the identifiers and state table for a device from + * the global device list. + * + * Parameters: + * devnum -- The number of the device (order in the list, + * starting with 1) + * + ********************************************************************************/ +int +TvCtrlPointPrintDevice( int devnum ) +{ + struct TvDeviceNode *tmpdevnode; + int i = 0, + service, + var; + char spacer[15]; + + if( devnum <= 0 ) { + SampleUtil_Print + ( "Error in TvCtrlPointPrintDevice: invalid devnum = %d", + devnum ); + return TV_ERROR; + } + + ithread_mutex_lock( &DeviceListMutex ); + + SampleUtil_Print( "TvCtrlPointPrintDevice:" ); + tmpdevnode = GlobalDeviceList; + while( tmpdevnode ) { + i++; + if( i == devnum ) + break; + tmpdevnode = tmpdevnode->next; + } + + if( !tmpdevnode ) { + SampleUtil_Print + ( "Error in TvCtrlPointPrintDevice: invalid devnum = %d -- actual device count = %d", + devnum, i ); + } else { + SampleUtil_Print( " TvDevice -- %d", devnum ); + SampleUtil_Print( " | " ); + SampleUtil_Print( " +- UDN = %s", + tmpdevnode->device.UDN ); + SampleUtil_Print( " +- DescDocURL = %s", + tmpdevnode->device.DescDocURL ); + SampleUtil_Print( " +- FriendlyName = %s", + tmpdevnode->device.FriendlyName ); + SampleUtil_Print( " +- PresURL = %s", + tmpdevnode->device.PresURL ); + SampleUtil_Print( " +- Adver. TimeOut = %d", + tmpdevnode->device.AdvrTimeOut ); + + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + if( service < TV_SERVICE_SERVCOUNT - 1 ) + sprintf( spacer, " | " ); + else + sprintf( spacer, " " ); + SampleUtil_Print( " | " ); + SampleUtil_Print( " +- Tv %s Service", + TvServiceName[service] ); + SampleUtil_Print( "%s+- ServiceId = %s", spacer, + tmpdevnode->device.TvService[service]. + ServiceId ); + SampleUtil_Print( "%s+- ServiceType = %s", spacer, + tmpdevnode->device.TvService[service]. + ServiceType ); + SampleUtil_Print( "%s+- EventURL = %s", spacer, + tmpdevnode->device.TvService[service]. + EventURL ); + SampleUtil_Print( "%s+- ControlURL = %s", spacer, + tmpdevnode->device.TvService[service]. + ControlURL ); + SampleUtil_Print( "%s+- SID = %s", spacer, + tmpdevnode->device.TvService[service].SID ); + SampleUtil_Print( "%s+- ServiceStateTable", spacer ); + + for( var = 0; var < TvVarCount[service]; var++ ) { + SampleUtil_Print( "%s +- %-10s = %s", spacer, + TvVarName[service][var], + tmpdevnode->device.TvService[service]. + VariableStrVal[var] ); + } + } + } + + SampleUtil_Print( "" ); + ithread_mutex_unlock( &DeviceListMutex ); + + return TV_SUCCESS; +} + +/******************************************************************************** + * TvCtrlPointAddDevice + * + * Description: + * If the device is not already included in the global device list, + * add it. Otherwise, update its advertisement expiration timeout. + * + * Parameters: + * DescDoc -- The description document for the device + * location -- The location of the description document URL + * expires -- The expiration time for this advertisement + * + ********************************************************************************/ +void +TvCtrlPointAddDevice( IXML_Document * DescDoc, + char *location, + int expires ) +{ + char *deviceType = NULL; + char *friendlyName = NULL; + char presURL[200]; + char *baseURL = NULL; + char *relURL = NULL; + char *UDN = NULL; + char *serviceId[TV_SERVICE_SERVCOUNT] = { NULL, NULL }; + char *eventURL[TV_SERVICE_SERVCOUNT] = { NULL, NULL }; + char *controlURL[TV_SERVICE_SERVCOUNT] = { NULL, NULL }; + Upnp_SID eventSID[TV_SERVICE_SERVCOUNT]; + int TimeOut[TV_SERVICE_SERVCOUNT] = + { default_timeout, default_timeout }; + struct TvDeviceNode *deviceNode; + struct TvDeviceNode *tmpdevnode; + int ret = 1; + int found = 0; + int service, + var; + + ithread_mutex_lock( &DeviceListMutex ); + + /* + Read key elements from description document + */ + UDN = SampleUtil_GetFirstDocumentItem( DescDoc, "UDN" ); + deviceType = SampleUtil_GetFirstDocumentItem( DescDoc, "deviceType" ); + friendlyName = + SampleUtil_GetFirstDocumentItem( DescDoc, "friendlyName" ); + baseURL = SampleUtil_GetFirstDocumentItem( DescDoc, "URLBase" ); + relURL = SampleUtil_GetFirstDocumentItem( DescDoc, "presentationURL" ); + + ret = + UpnpResolveURL( ( baseURL ? baseURL : location ), relURL, + presURL ); + + if( UPNP_E_SUCCESS != ret ) + SampleUtil_Print( "Error generating presURL from %s + %s", baseURL, + relURL ); + + if( strcmp( deviceType, TvDeviceType ) == 0 ) { + SampleUtil_Print( "Found Tv device" ); + + // Check if this device is already in the list + tmpdevnode = GlobalDeviceList; + while( tmpdevnode ) { + if( strcmp( tmpdevnode->device.UDN, UDN ) == 0 ) { + found = 1; + break; + } + tmpdevnode = tmpdevnode->next; + } + + if( found ) { + // The device is already there, so just update + // the advertisement timeout field + tmpdevnode->device.AdvrTimeOut = expires; + } else { + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + if( SampleUtil_FindAndParseService + ( DescDoc, location, TvServiceType[service], + &serviceId[service], &eventURL[service], + &controlURL[service] ) ) { + SampleUtil_Print( "Subscribing to EventURL %s...", + eventURL[service] ); + + ret = + UpnpSubscribe( ctrlpt_handle, eventURL[service], + &TimeOut[service], + eventSID[service] ); + + if( ret == UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Subscribed to EventURL with SID=%s", + eventSID[service] ); + } else { + SampleUtil_Print + ( "Error Subscribing to EventURL -- %d", ret ); + strcpy( eventSID[service], "" ); + } + } else { + SampleUtil_Print( "Error: Could not find Service: %s", + TvServiceType[service] ); + } + } + + /* + Create a new device node + */ + deviceNode = + ( struct TvDeviceNode * ) + malloc( sizeof( struct TvDeviceNode ) ); + strcpy( deviceNode->device.UDN, UDN ); + strcpy( deviceNode->device.DescDocURL, location ); + strcpy( deviceNode->device.FriendlyName, friendlyName ); + strcpy( deviceNode->device.PresURL, presURL ); + deviceNode->device.AdvrTimeOut = expires; + + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + strcpy( deviceNode->device.TvService[service].ServiceId, + serviceId[service] ); + strcpy( deviceNode->device.TvService[service].ServiceType, + TvServiceType[service] ); + strcpy( deviceNode->device.TvService[service].ControlURL, + controlURL[service] ); + strcpy( deviceNode->device.TvService[service].EventURL, + eventURL[service] ); + strcpy( deviceNode->device.TvService[service].SID, + eventSID[service] ); + + for( var = 0; var < TvVarCount[service]; var++ ) { + deviceNode->device.TvService[service]. + VariableStrVal[var] = + ( char * )malloc( TV_MAX_VAL_LEN ); + strcpy( deviceNode->device.TvService[service]. + VariableStrVal[var], "" ); + } + } + + deviceNode->next = NULL; + + // Insert the new device node in the list + if( ( tmpdevnode = GlobalDeviceList ) ) { + + while( tmpdevnode ) { + if( tmpdevnode->next ) { + tmpdevnode = tmpdevnode->next; + } else { + tmpdevnode->next = deviceNode; + break; + } + } + } else { + GlobalDeviceList = deviceNode; + } + + //Notify New Device Added + SampleUtil_StateUpdate( NULL, NULL, deviceNode->device.UDN, + DEVICE_ADDED ); + } + } + + ithread_mutex_unlock( &DeviceListMutex ); + + if( deviceType ) + free( deviceType ); + if( friendlyName ) + free( friendlyName ); + if( UDN ) + free( UDN ); + if( baseURL ) + free( baseURL ); + if( relURL ) + free( relURL ); + + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + if( serviceId[service] ) + free( serviceId[service] ); + if( controlURL[service] ) + free( controlURL[service] ); + if( eventURL[service] ) + free( eventURL[service] ); + } +} + +/******************************************************************************** + * TvStateUpdate + * + * Description: + * Update a Tv state table. Called when an event is + * received. Note: this function is NOT thread save. It must be + * called from another function that has locked the global device list. + * + * Parameters: + * UDN -- The UDN of the parent device. + * Service -- The service state table to update + * ChangedVariables -- DOM document representing the XML received + * with the event + * State -- pointer to the state table for the Tv service + * to update + * + ********************************************************************************/ +void +TvStateUpdate( char *UDN, + int Service, + IXML_Document * ChangedVariables, + char **State ) +{ + IXML_NodeList *properties, + *variables; + IXML_Element *property, + *variable; + int length, + length1; + int i, + j; + char *tmpstate = NULL; + + SampleUtil_Print( "Tv State Update (service %d): ", Service ); + + /* + Find all of the e:property tags in the document + */ + properties = + ixmlDocument_getElementsByTagName( ChangedVariables, + "e:property" ); + if( NULL != properties ) { + length = ixmlNodeList_length( properties ); + for( i = 0; i < length; i++ ) { /* Loop through each property change found */ + property = + ( IXML_Element * ) ixmlNodeList_item( properties, i ); + + /* + For each variable name in the state table, check if this + is a corresponding property change + */ + for( j = 0; j < TvVarCount[Service]; j++ ) { + variables = + ixmlElement_getElementsByTagName( property, + TvVarName[Service] + [j] ); + + /* + If a match is found, extract the value, and update the state table + */ + if( variables ) { + length1 = ixmlNodeList_length( variables ); + if( length1 ) { + variable = + ( IXML_Element * ) + ixmlNodeList_item( variables, 0 ); + tmpstate = SampleUtil_GetElementValue( variable ); + + if( tmpstate ) { + strcpy( State[j], tmpstate ); + SampleUtil_Print + ( " Variable Name: %s New Value:'%s'", + TvVarName[Service][j], State[j] ); + } + + if( tmpstate ) + free( tmpstate ); + tmpstate = NULL; + } + + ixmlNodeList_free( variables ); + variables = NULL; + } + } + + } + ixmlNodeList_free( properties ); + } +} + +/******************************************************************************** + * TvCtrlPointHandleEvent + * + * Description: + * Handle a UPnP event that was received. Process the event and update + * the appropriate service state table. + * + * Parameters: + * sid -- The subscription id for the event + * eventkey -- The eventkey number for the event + * changes -- The DOM document representing the changes + * + ********************************************************************************/ +void +TvCtrlPointHandleEvent( Upnp_SID sid, + int evntkey, + IXML_Document * changes ) +{ + struct TvDeviceNode *tmpdevnode; + int service; + + ithread_mutex_lock( &DeviceListMutex ); + + tmpdevnode = GlobalDeviceList; + while( tmpdevnode ) { + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + if( strcmp( tmpdevnode->device.TvService[service].SID, sid ) == + 0 ) { + SampleUtil_Print( "Received Tv %s Event: %d for SID %s", + TvServiceName[service], evntkey, sid ); + + TvStateUpdate( tmpdevnode->device.UDN, service, changes, + ( char ** )&tmpdevnode->device. + TvService[service].VariableStrVal ); + break; + } + } + tmpdevnode = tmpdevnode->next; + } + + ithread_mutex_unlock( &DeviceListMutex ); +} + +/******************************************************************************** + * TvCtrlPointHandleSubscribeUpdate + * + * Description: + * Handle a UPnP subscription update that was received. Find the + * service the update belongs to, and update its subscription + * timeout. + * + * Parameters: + * eventURL -- The event URL for the subscription + * sid -- The subscription id for the subscription + * timeout -- The new timeout for the subscription + * + ********************************************************************************/ +void +TvCtrlPointHandleSubscribeUpdate( char *eventURL, + Upnp_SID sid, + int timeout ) +{ + struct TvDeviceNode *tmpdevnode; + int service; + + ithread_mutex_lock( &DeviceListMutex ); + + tmpdevnode = GlobalDeviceList; + while( tmpdevnode ) { + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + + if( strcmp + ( tmpdevnode->device.TvService[service].EventURL, + eventURL ) == 0 ) { + SampleUtil_Print + ( "Received Tv %s Event Renewal for eventURL %s", + TvServiceName[service], eventURL ); + strcpy( tmpdevnode->device.TvService[service].SID, sid ); + break; + } + } + + tmpdevnode = tmpdevnode->next; + } + + ithread_mutex_unlock( &DeviceListMutex ); +} + +void +TvCtrlPointHandleGetVar( char *controlURL, + char *varName, + DOMString varValue ) +{ + + struct TvDeviceNode *tmpdevnode; + int service; + + ithread_mutex_lock( &DeviceListMutex ); + + tmpdevnode = GlobalDeviceList; + while( tmpdevnode ) { + for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) { + if( strcmp + ( tmpdevnode->device.TvService[service].ControlURL, + controlURL ) == 0 ) { + SampleUtil_StateUpdate( varName, varValue, + tmpdevnode->device.UDN, + GET_VAR_COMPLETE ); + break; + } + } + tmpdevnode = tmpdevnode->next; + } + + ithread_mutex_unlock( &DeviceListMutex ); +} + +/******************************************************************************** + * TvCtrlPointCallbackEventHandler + * + * Description: + * The callback handler registered with the SDK while registering + * the control point. Detects the type of callback, and passes the + * request on to the appropriate function. + * + * Parameters: + * EventType -- The type of callback event + * Event -- Data structure containing event data + * Cookie -- Optional data specified during callback registration + * + ********************************************************************************/ +int +TvCtrlPointCallbackEventHandler( Upnp_EventType EventType, + void *Event, + void *Cookie ) +{ + SampleUtil_PrintEvent( EventType, Event ); + + switch ( EventType ) { + /* + SSDP Stuff + */ + case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: + case UPNP_DISCOVERY_SEARCH_RESULT: + { + struct Upnp_Discovery *d_event = + ( struct Upnp_Discovery * )Event; + IXML_Document *DescDoc = NULL; + int ret; + + if( d_event->ErrCode != UPNP_E_SUCCESS ) { + SampleUtil_Print( "Error in Discovery Callback -- %d", + d_event->ErrCode ); + } + + if( ( ret = + UpnpDownloadXmlDoc( d_event->Location, + &DescDoc ) ) != + UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error obtaining device description from %s -- error = %d", + d_event->Location, ret ); + } else { + TvCtrlPointAddDevice( DescDoc, d_event->Location, + d_event->Expires ); + } + + if( DescDoc ) + ixmlDocument_free( DescDoc ); + + TvCtrlPointPrintList( ); + break; + } + + case UPNP_DISCOVERY_SEARCH_TIMEOUT: + /* + Nothing to do here... + */ + break; + + case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: + { + struct Upnp_Discovery *d_event = + ( struct Upnp_Discovery * )Event; + + if( d_event->ErrCode != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error in Discovery ByeBye Callback -- %d", + d_event->ErrCode ); + } + + SampleUtil_Print( "Received ByeBye for Device: %s", + d_event->DeviceId ); + TvCtrlPointRemoveDevice( d_event->DeviceId ); + + SampleUtil_Print( "After byebye:" ); + TvCtrlPointPrintList( ); + + break; + } + + /* + SOAP Stuff + */ + case UPNP_CONTROL_ACTION_COMPLETE: + { + struct Upnp_Action_Complete *a_event = + ( struct Upnp_Action_Complete * )Event; + + if( a_event->ErrCode != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error in Action Complete Callback -- %d", + a_event->ErrCode ); + } + + /* + No need for any processing here, just print out results. Service state + table updates are handled by events. + */ + + break; + } + + case UPNP_CONTROL_GET_VAR_COMPLETE: + { + struct Upnp_State_Var_Complete *sv_event = + ( struct Upnp_State_Var_Complete * )Event; + + if( sv_event->ErrCode != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error in Get Var Complete Callback -- %d", + sv_event->ErrCode ); + } else { + TvCtrlPointHandleGetVar( sv_event->CtrlUrl, + sv_event->StateVarName, + sv_event->CurrentVal ); + } + + break; + } + + /* + GENA Stuff + */ + case UPNP_EVENT_RECEIVED: + { + struct Upnp_Event *e_event = ( struct Upnp_Event * )Event; + + TvCtrlPointHandleEvent( e_event->Sid, e_event->EventKey, + e_event->ChangedVariables ); + break; + } + + case UPNP_EVENT_SUBSCRIBE_COMPLETE: + case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: + case UPNP_EVENT_RENEWAL_COMPLETE: + { + struct Upnp_Event_Subscribe *es_event = + ( struct Upnp_Event_Subscribe * )Event; + + if( es_event->ErrCode != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error in Event Subscribe Callback -- %d", + es_event->ErrCode ); + } else { + TvCtrlPointHandleSubscribeUpdate( es_event-> + PublisherUrl, + es_event->Sid, + es_event->TimeOut ); + } + + break; + } + + case UPNP_EVENT_AUTORENEWAL_FAILED: + case UPNP_EVENT_SUBSCRIPTION_EXPIRED: + { + int TimeOut = default_timeout; + Upnp_SID newSID; + int ret; + + struct Upnp_Event_Subscribe *es_event = + ( struct Upnp_Event_Subscribe * )Event; + + ret = + UpnpSubscribe( ctrlpt_handle, es_event->PublisherUrl, + &TimeOut, newSID ); + + if( ret == UPNP_E_SUCCESS ) { + SampleUtil_Print( "Subscribed to EventURL with SID=%s", + newSID ); + TvCtrlPointHandleSubscribeUpdate( es_event-> + PublisherUrl, newSID, + TimeOut ); + } else { + SampleUtil_Print + ( "Error Subscribing to EventURL -- %d", ret ); + } + break; + } + + /* + ignore these cases, since this is not a device + */ + case UPNP_EVENT_SUBSCRIPTION_REQUEST: + case UPNP_CONTROL_GET_VAR_REQUEST: + case UPNP_CONTROL_ACTION_REQUEST: + break; + } + + return 0; +} + +/******************************************************************************** + * TvCtrlPointVerifyTimeouts + * + * Description: + * Checks the advertisement each device + * in the global device list. If an advertisement expires, + * the device is removed from the list. If an advertisement is about to + * expire, a search request is sent for that device. + * + * Parameters: + * incr -- The increment to subtract from the timeouts each time the + * function is called. + * + ********************************************************************************/ +void +TvCtrlPointVerifyTimeouts( int incr ) +{ + struct TvDeviceNode *prevdevnode, + *curdevnode; + int ret; + + ithread_mutex_lock( &DeviceListMutex ); + + prevdevnode = NULL; + curdevnode = GlobalDeviceList; + + while( curdevnode ) { + curdevnode->device.AdvrTimeOut -= incr; + //SampleUtil_Print("Advertisement Timeout: %d\n", curdevnode->device.AdvrTimeOut); + + if( curdevnode->device.AdvrTimeOut <= 0 ) { + /* + This advertisement has expired, so we should remove the device + from the list + */ + + if( GlobalDeviceList == curdevnode ) + GlobalDeviceList = curdevnode->next; + else + prevdevnode->next = curdevnode->next; + TvCtrlPointDeleteNode( curdevnode ); + if( prevdevnode ) + curdevnode = prevdevnode->next; + else + curdevnode = GlobalDeviceList; + } else { + + if( curdevnode->device.AdvrTimeOut < 2 * incr ) { + /* + This advertisement is about to expire, so send + out a search request for this device UDN to + try to renew + */ + ret = UpnpSearchAsync( ctrlpt_handle, incr, + curdevnode->device.UDN, NULL ); + if( ret != UPNP_E_SUCCESS ) + SampleUtil_Print + ( "Error sending search request for Device UDN: %s -- err = %d", + curdevnode->device.UDN, ret ); + } + + prevdevnode = curdevnode; + curdevnode = curdevnode->next; + } + + } + ithread_mutex_unlock( &DeviceListMutex ); + +} + +/******************************************************************************** + * TvCtrlPointTimerLoop + * + * Description: + * Function that runs in its own thread and monitors advertisement + * and subscription timeouts for devices in the global device list. + * + * Parameters: + * None + * + ********************************************************************************/ +void * +TvCtrlPointTimerLoop( void *args ) +{ + int incr = 30; // how often to verify the timeouts, in seconds + + while( 1 ) { + isleep( incr ); + TvCtrlPointVerifyTimeouts( incr ); + } + + return NULL; +} + +/******************************************************************************** + * TvCtrlPointStart + * + * Description: + * Call this function to initialize the UPnP library and start the TV Control + * Point. This function creates a timer thread and provides a callback + * handler to process any UPnP events that are received. + * + * Parameters: + * None + * + * Returns: + * TV_SUCCESS if everything went well, else TV_ERROR + * + ********************************************************************************/ +int +TvCtrlPointStart( print_string printFunctionPtr, + state_update updateFunctionPtr ) +{ + ithread_t timer_thread; + int rc; + short int port = 0; + char *ip_address = NULL; + + SampleUtil_Initialize( printFunctionPtr ); + SampleUtil_RegisterUpdateFunction( updateFunctionPtr ); + + ithread_mutex_init( &DeviceListMutex, 0 ); + + SampleUtil_Print( "Intializing UPnP with ipaddress=%s port=%d", + ip_address, port ); + rc = UpnpInit( ip_address, port ); + if( UPNP_E_SUCCESS != rc ) { + SampleUtil_Print( "WinCEStart: UpnpInit() Error: %d", rc ); + UpnpFinish( ); + return TV_ERROR; + } + + if( NULL == ip_address ) + ip_address = UpnpGetServerIpAddress( ); + if( 0 == port ) + port = UpnpGetServerPort( ); + + SampleUtil_Print( "UPnP Initialized (%s:%d)", ip_address, port ); + + SampleUtil_Print( "Registering Control Point" ); + rc = UpnpRegisterClient( TvCtrlPointCallbackEventHandler, + &ctrlpt_handle, &ctrlpt_handle ); + if( UPNP_E_SUCCESS != rc ) { + SampleUtil_Print( "Error registering CP: %d", rc ); + UpnpFinish( ); + return TV_ERROR; + } + + SampleUtil_Print( "Control Point Registered" ); + + TvCtrlPointRefresh( ); + + // start a timer thread + ithread_create( &timer_thread, NULL, TvCtrlPointTimerLoop, NULL ); + + return TV_SUCCESS; +} + +int +TvCtrlPointStop( void ) +{ + TvCtrlPointRemoveAll( ); + UpnpUnRegisterClient( ctrlpt_handle ); + UpnpFinish( ); + SampleUtil_Finish( ); + + return TV_SUCCESS; +} diff --git a/libupnp/upnp/sample/tvctrlpt/upnp_tv_ctrlpt.h b/libupnp/upnp/sample/tvctrlpt/upnp_tv_ctrlpt.h new file mode 100644 index 0000000..b47f5c6 --- /dev/null +++ b/libupnp/upnp/sample/tvctrlpt/upnp_tv_ctrlpt.h @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UPNP_TV_CTRLPT_H +#define UPNP_TV_CTRLPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "ithread.h" +#include +#include +#include +#include +#include + +#include "upnp.h" +#include "upnptools.h" +#include "sample_util.h" + +#define TV_SERVICE_SERVCOUNT 2 +#define TV_SERVICE_CONTROL 0 +#define TV_SERVICE_PICTURE 1 + +#define TV_CONTROL_VARCOUNT 3 +#define TV_CONTROL_POWER 0 +#define TV_CONTROL_CHANNEL 1 +#define TV_CONTROL_VOLUME 2 + +#define TV_PICTURE_VARCOUNT 4 +#define TV_PICTURE_COLOR 0 +#define TV_PICTURE_TINT 1 +#define TV_PICTURE_CONTRAST 2 +#define TV_PICTURE_BRIGHTNESS 3 + +#define TV_MAX_VAL_LEN 5 + +#define TV_SUCCESS 0 +#define TV_ERROR (-1) +#define TV_WARNING 1 + +/* This should be the maximum VARCOUNT from above */ +#define TV_MAXVARS TV_PICTURE_VARCOUNT + +extern char TvDeviceType[]; +extern char *TvServiceType[]; +extern char *TvServiceName[]; +extern char *TvVarName[TV_SERVICE_SERVCOUNT][TV_MAXVARS]; +extern char TvVarCount[]; + +struct tv_service { + char ServiceId[NAME_SIZE]; + char ServiceType[NAME_SIZE]; + char *VariableStrVal[TV_MAXVARS]; + char EventURL[NAME_SIZE]; + char ControlURL[NAME_SIZE]; + char SID[NAME_SIZE]; +}; + +extern struct TvDeviceNode *GlobalDeviceList; + +struct TvDevice { + char UDN[250]; + char DescDocURL[250]; + char FriendlyName[250]; + char PresURL[250]; + int AdvrTimeOut; + struct tv_service TvService[TV_SERVICE_SERVCOUNT]; +}; + +struct TvDeviceNode { + struct TvDevice device; + struct TvDeviceNode *next; +}; + +extern ithread_mutex_t DeviceListMutex; + +extern UpnpClient_Handle ctrlpt_handle; + +void TvCtrlPointPrintHelp( void ); +int TvCtrlPointDeleteNode(struct TvDeviceNode*); +int TvCtrlPointRemoveDevice(char*); +int TvCtrlPointRemoveAll( void ); +int TvCtrlPointRefresh( void ); + + +int TvCtrlPointSendAction(int, int, char *, char **, char **, int); +int TvCtrlPointSendActionNumericArg(int devnum, int service, char *actionName, char *paramName, int paramValue); +int TvCtrlPointSendPowerOn(int devnum); +int TvCtrlPointSendPowerOff(int devnum); +int TvCtrlPointSendSetChannel(int, int); +int TvCtrlPointSendSetVolume(int, int); +int TvCtrlPointSendSetColor(int, int); +int TvCtrlPointSendSetTint(int, int); +int TvCtrlPointSendSetContrast(int, int); +int TvCtrlPointSendSetBrightness(int, int); + +int TvCtrlPointGetVar(int, int, char*); +int TvCtrlPointGetPower(int devnum); +int TvCtrlPointGetChannel(int); +int TvCtrlPointGetVolume(int); +int TvCtrlPointGetColor(int); +int TvCtrlPointGetTint(int); +int TvCtrlPointGetContrast(int); +int TvCtrlPointGetBrightness(int); + +int TvCtrlPointGetDevice(int, struct TvDeviceNode **); +int TvCtrlPointPrintList( void ); +int TvCtrlPointPrintDevice(int); +void TvCtrlPointAddDevice (IXML_Document *, char *, int); +void TvCtrlPointHandleGetVar(char *,char *,DOMString); +void TvStateUpdate(char*,int, IXML_Document * , char **); +void TvCtrlPointHandleEvent(Upnp_SID, int, IXML_Document *); +void TvCtrlPointHandleSubscribeUpdate(char *, Upnp_SID, int); +int TvCtrlPointCallbackEventHandler(Upnp_EventType, void *, void *); +void TvCtrlPointVerifyTimeouts(int); +void TvCtrlPointPrintCommands( void ); +void* TvCtrlPointCommandLoop( void* ); +int TvCtrlPointStart( print_string printFunctionPtr, state_update updateFunctionPtr ); +int TvCtrlPointStop( void ); +int TvCtrlPointProcessCommand( char *cmdline ); + +#ifdef __cplusplus +}; +#endif + +#endif //UPNP_TV_CTRLPT_H diff --git a/libupnp/upnp/sample/tvdevice/linux/upnp_tv_device_main.c b/libupnp/upnp/sample/tvdevice/linux/upnp_tv_device_main.c new file mode 100644 index 0000000..665190b --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/linux/upnp_tv_device_main.c @@ -0,0 +1,191 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include "sample_util.h" +#include "upnp_tv_device.h" + +/****************************************************************************** + * linux_print + * + * Description: + * Prints a string to standard out. + * + * Parameters: + * None + * + *****************************************************************************/ +void +linux_print( const char *string ) +{ + printf( "%s", string ); +} + +/****************************************************************************** + * TvDeviceCommandLoop + * + * Description: + * Function that receives commands from the user at the command prompt + * during the lifetime of the device, and calls the appropriate + * functions for those commands. Only one command, exit, is currently + * defined. + * + * Parameters: + * None + * + *****************************************************************************/ +void * +TvDeviceCommandLoop( void *args ) +{ + int stoploop = 0; + char cmdline[100]; + char cmd[100]; + + while( !stoploop ) { + sprintf( cmdline, " " ); + sprintf( cmd, " " ); + + SampleUtil_Print( "\n>> " ); + + // Get a command line + fgets( cmdline, 100, stdin ); + + sscanf( cmdline, "%s", cmd ); + + if( strcasecmp( cmd, "exit" ) == 0 ) { + SampleUtil_Print( "Shutting down...\n" ); + TvDeviceStop( ); + exit( 0 ); + } else { + SampleUtil_Print( "\n Unknown command: %s\n\n", cmd ); + SampleUtil_Print( " Valid Commands:\n" ); + SampleUtil_Print( " Exit\n\n" ); + } + + } + + return NULL; +} + +/****************************************************************************** + * main + * + * Description: + * Main entry point for tv device application. + * Initializes and registers with the sdk. + * Initializes the state stables of the service. + * Starts the command loop. + * + * Parameters: + * int argc - count of arguments + * char ** argv -arguments. The application + * accepts the following optional arguments: + * + * -ip ipaddress + * -port port + * -desc desc_doc_name + * -webdir web_dir_path" + * -help + * + * + *****************************************************************************/ +int +main( IN int argc, + IN char **argv ) +{ + + unsigned int portTemp = 0; + char *ip_address = NULL, + *desc_doc_name = NULL, + *web_dir_path = NULL; + ithread_t cmdloop_thread; + int code; + unsigned int port = 0; + int sig; + sigset_t sigs_to_catch; + + int i = 0; + + SampleUtil_Initialize( linux_print ); + + //Parse options + for( i = 1; i < argc; i++ ) { + if( strcmp( argv[i], "-ip" ) == 0 ) { + ip_address = argv[++i]; + } else if( strcmp( argv[i], "-port" ) == 0 ) { + sscanf( argv[++i], "%u", &portTemp ); + } else if( strcmp( argv[i], "-desc" ) == 0 ) { + desc_doc_name = argv[++i]; + } else if( strcmp( argv[i], "-webdir" ) == 0 ) { + web_dir_path = argv[++i]; + } else if( strcmp( argv[i], "-help" ) == 0 ) { + SampleUtil_Print( "Usage: %s -ip ipaddress -port port" + " -desc desc_doc_name -webdir web_dir_path" + " -help (this message)\n", argv[0] ); + SampleUtil_Print( "\tipaddress: IP address of the device" + " (must match desc. doc)\n" ); + SampleUtil_Print( "\t\te.g.: 192.168.0.4\n" ); + SampleUtil_Print( "\tport: Port number to use for " + "receiving UPnP messages (must match desc. doc)\n" ); + SampleUtil_Print( "\t\te.g.: 5431\n" ); + SampleUtil_Print + ( "\tdesc_doc_name: name of device description document\n" ); + SampleUtil_Print( "\t\te.g.: tvdevicedesc.xml\n" ); + SampleUtil_Print + ( "\tweb_dir_path: Filesystem path where web files " + "related to the device are stored\n" ); + SampleUtil_Print( "\t\te.g.: /upnp/sample/tvdevice/web\n" ); + exit( 1 ); + } + } + + port = ( unsigned short )portTemp; + + TvDeviceStart( ip_address, port, desc_doc_name, web_dir_path, + linux_print ); + + /* + start a command loop thread + */ + code = ithread_create( &cmdloop_thread, NULL, TvDeviceCommandLoop, + NULL ); + + /* + Catch Ctrl-C and properly shutdown + */ + sigemptyset( &sigs_to_catch ); + sigaddset( &sigs_to_catch, SIGINT ); + sigwait( &sigs_to_catch, &sig ); + + SampleUtil_Print( "Shutting down on signal %d...\n", sig ); + TvDeviceStop( ); + exit( 0 ); +} diff --git a/libupnp/upnp/sample/tvdevice/upnp_tv_device.c b/libupnp/upnp/sample/tvdevice/upnp_tv_device.c new file mode 100644 index 0000000..cec09f1 --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/upnp_tv_device.c @@ -0,0 +1,2033 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "upnp_tv_device.h" + +#define DEFAULT_WEB_DIR "./web" + +#define DESC_URL_SIZE 200 + +/* + Device type for tv device + */ +char TvDeviceType[] = "urn:schemas-upnp-org:device:tvdevice:1"; + +/* + Service types for tv services + */ +char *TvServiceType[] = { "urn:schemas-upnp-org:service:tvcontrol:1", + "urn:schemas-upnp-org:service:tvpicture:1" +}; + +/* + Global arrays for storing Tv Control Service + variable names, values, and defaults + */ +char *tvc_varname[] = { "Power", "Channel", "Volume" }; +char tvc_varval[TV_CONTROL_VARCOUNT][TV_MAX_VAL_LEN]; +char *tvc_varval_def[] = { "1", "1", "5" }; + +/* + Global arrays for storing Tv Picture Service + variable names, values, and defaults + */ +char *tvp_varname[] = { "Color", "Tint", "Contrast", "Brightness" }; +char tvp_varval[TV_PICTURE_VARCOUNT][TV_MAX_VAL_LEN]; +char *tvp_varval_def[] = { "5", "5", "5", "5" }; + +/* + The amount of time (in seconds) before advertisements + will expire + */ +int default_advr_expire = 100; + +/* + Global structure for storing the state table for this device + */ +struct TvService tv_service_table[2]; + +/* + Device handle supplied by UPnP SDK + */ +UpnpDevice_Handle device_handle = -1; + +/* + Mutex for protecting the global state table data + in a multi-threaded, asynchronous environment. + All functions should lock this mutex before reading + or writing the state table data. + */ +ithread_mutex_t TVDevMutex; + +//Color constants +#define MAX_COLOR 10 +#define MIN_COLOR 1 + +//Brightness constants +#define MAX_BRIGHTNESS 10 +#define MIN_BRIGHTNESS 1 + +//Power constants +#define POWER_ON 1 +#define POWER_OFF 0 + +//Tint constants +#define MAX_TINT 10 +#define MIN_TINT 1 + +//Volume constants +#define MAX_VOLUME 10 +#define MIN_VOLUME 1 + +//Contrast constants +#define MAX_CONTRAST 10 +#define MIN_CONTRAST 1 + +//Channel constants +#define MAX_CHANNEL 100 +#define MIN_CHANNEL 1 + +/****************************************************************************** + * SetServiceTable + * + * Description: + * Initializes the service table for the specified service. + * Note that + * knowledge of the service description is + * assumed. + * Parameters: + * int serviceType - one of TV_SERVICE_CONTROL or, TV_SERVICE_PICTURE + * const char * UDN - UDN of device containing service + * const char * serviceId - serviceId of service + * const char * serviceTypeS - service type (as specified in Description + * Document) + * struct TvService *out - service containing table to be set. + * + *****************************************************************************/ +int +SetServiceTable( IN int serviceType, + IN const char *UDN, + IN const char *serviceId, + IN const char *serviceTypeS, + INOUT struct TvService *out ) +{ + unsigned int i = 0; + + strcpy( out->UDN, UDN ); + strcpy( out->ServiceId, serviceId ); + strcpy( out->ServiceType, serviceTypeS ); + + switch ( serviceType ) { + case TV_SERVICE_CONTROL: + out->VariableCount = TV_CONTROL_VARCOUNT; + for( i = 0; + i < tv_service_table[TV_SERVICE_CONTROL].VariableCount; + i++ ) { + tv_service_table[TV_SERVICE_CONTROL].VariableName[i] + = tvc_varname[i]; + tv_service_table[TV_SERVICE_CONTROL].VariableStrVal[i] + = tvc_varval[i]; + strcpy( tv_service_table[TV_SERVICE_CONTROL]. + VariableStrVal[i], tvc_varval_def[i] ); + } + + break; + case TV_SERVICE_PICTURE: + out->VariableCount = TV_PICTURE_VARCOUNT; + + for( i = 0; + i < tv_service_table[TV_SERVICE_PICTURE].VariableCount; + i++ ) { + tv_service_table[TV_SERVICE_PICTURE].VariableName[i] = + tvp_varname[i]; + tv_service_table[TV_SERVICE_PICTURE].VariableStrVal[i] = + tvp_varval[i]; + strcpy( tv_service_table[TV_SERVICE_PICTURE]. + VariableStrVal[i], tvp_varval_def[i] ); + } + + break; + default: + assert( 0 ); + } + + return SetActionTable( serviceType, out ); + +} + +/****************************************************************************** + * SetActionTable + * + * Description: + * Initializes the action table for the specified service. + * Note that + * knowledge of the service description is + * assumed. Action names are hardcoded. + * Parameters: + * int serviceType - one of TV_SERVICE_CONTROL or, TV_SERVICE_PICTURE + * struct TvService *out - service containing action table to set. + * + *****************************************************************************/ +int +SetActionTable( IN int serviceType, + INOUT struct TvService *out ) +{ + if( serviceType == TV_SERVICE_CONTROL ) { + out->ActionNames[0] = "PowerOn"; + out->actions[0] = TvDevicePowerOn; + out->ActionNames[1] = "PowerOff"; + out->actions[1] = TvDevicePowerOff; + out->ActionNames[2] = "SetChannel"; + out->actions[2] = TvDeviceSetChannel; + out->ActionNames[3] = "IncreaseChannel"; + out->actions[3] = TvDeviceIncreaseChannel; + out->ActionNames[4] = "DecreaseChannel"; + out->actions[4] = TvDeviceDecreaseChannel; + out->ActionNames[5] = "SetVolume"; + out->actions[5] = TvDeviceSetVolume; + out->ActionNames[6] = "IncreaseVolume"; + out->actions[6] = TvDeviceIncreaseVolume; + out->ActionNames[7] = "DecreaseVolume"; + out->actions[7] = TvDeviceDecreaseVolume; + out->ActionNames[8] = NULL; + return 1; + } else if( serviceType == TV_SERVICE_PICTURE ) { + out->ActionNames[0] = "SetColor"; + out->ActionNames[1] = "IncreaseColor"; + out->ActionNames[2] = "DecreaseColor"; + out->actions[0] = TvDeviceSetColor; + out->actions[1] = TvDeviceIncreaseColor; + out->actions[2] = TvDeviceDecreaseColor; + out->ActionNames[3] = "SetTint"; + out->ActionNames[4] = "IncreaseTint"; + out->ActionNames[5] = "DecreaseTint"; + out->actions[3] = TvDeviceSetTint; + out->actions[4] = TvDeviceIncreaseTint; + out->actions[5] = TvDeviceDecreaseTint; + + out->ActionNames[6] = "SetBrightness"; + out->ActionNames[7] = "IncreaseBrightness"; + out->ActionNames[8] = "DecreaseBrightness"; + out->actions[6] = TvDeviceSetBrightness; + out->actions[7] = TvDeviceIncreaseBrightness; + out->actions[8] = TvDeviceDecreaseBrightness; + + out->ActionNames[9] = "SetContrast"; + out->ActionNames[10] = "IncreaseContrast"; + out->ActionNames[11] = "DecreaseContrast"; + + out->actions[9] = TvDeviceSetContrast; + out->actions[10] = TvDeviceIncreaseContrast; + out->actions[11] = TvDeviceDecreaseContrast; + return 1; + } + + return 0; + +} + +/****************************************************************************** + * TvDeviceStateTableInit + * + * Description: + * Initialize the device state table for + * this TvDevice, pulling identifier info + * from the description Document. Note that + * knowledge of the service description is + * assumed. State table variables and default + * values are currently hardcoded in this file + * rather than being read from service description + * documents. + * + * Parameters: + * DescDocURL -- The description document URL + * + *****************************************************************************/ +int +TvDeviceStateTableInit( IN char *DescDocURL ) +{ + IXML_Document *DescDoc = NULL; + int ret = UPNP_E_SUCCESS; + char *servid_ctrl = NULL, + *evnturl_ctrl = NULL, + *ctrlurl_ctrl = NULL; + char *servid_pict = NULL, + *evnturl_pict = NULL, + *ctrlurl_pict = NULL; + char *udn = NULL; + + //Download description document + if( UpnpDownloadXmlDoc( DescDocURL, &DescDoc ) != UPNP_E_SUCCESS ) { + SampleUtil_Print( "TvDeviceStateTableInit -- Error Parsing %s\n", + DescDocURL ); + ret = UPNP_E_INVALID_DESC; + goto error_handler; + } + + udn = SampleUtil_GetFirstDocumentItem( DescDoc, "UDN" ); + + /* + Find the Tv Control Service identifiers + */ + if( !SampleUtil_FindAndParseService( DescDoc, DescDocURL, + TvServiceType[TV_SERVICE_CONTROL], + &servid_ctrl, &evnturl_ctrl, + &ctrlurl_ctrl ) ) { + SampleUtil_Print( "TvDeviceStateTableInit -- Error: Could not find" + " Service: %s\n", + TvServiceType[TV_SERVICE_CONTROL] ); + + ret = UPNP_E_INVALID_DESC; + goto error_handler; + } + + //set control service table + SetServiceTable( TV_SERVICE_CONTROL, udn, servid_ctrl, + TvServiceType[TV_SERVICE_CONTROL], + &tv_service_table[TV_SERVICE_CONTROL] ); + + /* + Find the Tv Picture Service identifiers + */ + if( !SampleUtil_FindAndParseService( DescDoc, DescDocURL, + TvServiceType[TV_SERVICE_PICTURE], + &servid_pict, &evnturl_pict, + &ctrlurl_pict ) ) { + SampleUtil_Print( "TvDeviceStateTableInit -- Error: Could not find" + " Service: %s\n", + TvServiceType[TV_SERVICE_PICTURE] ); + + ret = UPNP_E_INVALID_DESC; + goto error_handler; + } + //set picture service table + SetServiceTable( TV_SERVICE_PICTURE, udn, servid_pict, + TvServiceType[TV_SERVICE_PICTURE], + &tv_service_table[TV_SERVICE_PICTURE] ); + + error_handler: + + //clean up + if( udn ) + free( udn ); + if( servid_ctrl ) + free( servid_ctrl ); + if( evnturl_ctrl ) + free( evnturl_ctrl ); + if( ctrlurl_ctrl ) + free( ctrlurl_ctrl ); + if( servid_pict ) + free( servid_pict ); + if( evnturl_pict ) + free( evnturl_pict ); + if( ctrlurl_pict ) + free( ctrlurl_pict ); + if( DescDoc ) + ixmlDocument_free( DescDoc ); + + return ( ret ); +} + +/****************************************************************************** + * TvDeviceHandleSubscriptionRequest + * + * Description: + * Called during a subscription request callback. If the + * subscription request is for this device and either its + * control service or picture service, then accept it. + * + * Parameters: + * sr_event -- The subscription request event structure + * + *****************************************************************************/ +int +TvDeviceHandleSubscriptionRequest( IN struct Upnp_Subscription_Request + *sr_event ) +{ + unsigned int i = 0; //,j=0; + + // IXML_Document *PropSet=NULL; + + //lock state mutex + ithread_mutex_lock( &TVDevMutex ); + + for( i = 0; i < TV_SERVICE_SERVCOUNT; i++ ) { + if( ( strcmp( sr_event->UDN, tv_service_table[i].UDN ) == 0 ) && + ( strcmp( sr_event->ServiceId, tv_service_table[i].ServiceId ) + == 0 ) ) { + + /* + PropSet = NULL; + + for (j=0; j< tv_service_table[i].VariableCount; j++) + { + //add each variable to the property set + //for initial state dump + UpnpAddToPropertySet(&PropSet, + tv_service_table[i].VariableName[j], + tv_service_table[i].VariableStrVal[j]); + } + + //dump initial state + UpnpAcceptSubscriptionExt(device_handle, sr_event->UDN, + sr_event->ServiceId, + PropSet,sr_event->Sid); + //free document + Document_free(PropSet); + + */ + + UpnpAcceptSubscription( device_handle, + sr_event->UDN, + sr_event->ServiceId, + ( const char ** )tv_service_table[i]. + VariableName, + ( const char ** )tv_service_table[i]. + VariableStrVal, + tv_service_table[i].VariableCount, + sr_event->Sid ); + + } + } + + ithread_mutex_unlock( &TVDevMutex ); + + return ( 1 ); +} + +/****************************************************************************** + * TvDeviceHandleGetVarRequest + * + * Description: + * Called during a get variable request callback. If the + * request is for this device and either its control service + * or picture service, then respond with the variable value. + * + * Parameters: + * cgv_event -- The control get variable request event structure + * + *****************************************************************************/ +int +TvDeviceHandleGetVarRequest( INOUT struct Upnp_State_Var_Request + *cgv_event ) +{ + unsigned int i = 0, + j = 0; + int getvar_succeeded = 0; + + cgv_event->CurrentVal = NULL; + + ithread_mutex_lock( &TVDevMutex ); + + for( i = 0; i < TV_SERVICE_SERVCOUNT; i++ ) { + //check udn and service id + if( ( strcmp( cgv_event->DevUDN, tv_service_table[i].UDN ) == 0 ) + && + ( strcmp( cgv_event->ServiceID, tv_service_table[i].ServiceId ) + == 0 ) ) { + //check variable name + for( j = 0; j < tv_service_table[i].VariableCount; j++ ) { + if( strcmp( cgv_event->StateVarName, + tv_service_table[i].VariableName[j] ) == 0 ) { + getvar_succeeded = 1; + cgv_event->CurrentVal = + ixmlCloneDOMString( tv_service_table[i]. + VariableStrVal[j] ); + break; + } + } + } + } + + if( getvar_succeeded ) { + cgv_event->ErrCode = UPNP_E_SUCCESS; + } else { + SampleUtil_Print + ( "Error in UPNP_CONTROL_GET_VAR_REQUEST callback:\n" ); + SampleUtil_Print( " Unknown variable name = %s\n", + cgv_event->StateVarName ); + cgv_event->ErrCode = 404; + strcpy( cgv_event->ErrStr, "Invalid Variable" ); + } + + ithread_mutex_unlock( &TVDevMutex ); + + return ( cgv_event->ErrCode == UPNP_E_SUCCESS ); +} + +/****************************************************************************** + * TvDeviceHandleActionRequest + * + * Description: + * Called during an action request callback. If the + * request is for this device and either its control service + * or picture service, then perform the action and respond. + * + * Parameters: + * ca_event -- The control action request event structure + * + *****************************************************************************/ +int +TvDeviceHandleActionRequest( INOUT struct Upnp_Action_Request *ca_event ) +{ + + /* + Defaults if action not found + */ + int action_found = 0; + int i = 0; + int service = -1; + int retCode = 0; + char *errorString = NULL; + + ca_event->ErrCode = 0; + ca_event->ActionResult = NULL; + + if( ( strcmp( ca_event->DevUDN, + tv_service_table[TV_SERVICE_CONTROL].UDN ) == 0 ) && + ( strcmp + ( ca_event->ServiceID, + tv_service_table[TV_SERVICE_CONTROL].ServiceId ) == 0 ) ) { + /* + Request for action in the TvDevice Control Service + */ + service = TV_SERVICE_CONTROL; + } else if( ( strcmp( ca_event->DevUDN, + tv_service_table[TV_SERVICE_PICTURE].UDN ) == 0 ) + && + ( strcmp + ( ca_event->ServiceID, + tv_service_table[TV_SERVICE_PICTURE].ServiceId ) == + 0 ) ) { + /* + Request for action in the TvDevice Picture Service + */ + service = TV_SERVICE_PICTURE; + } + //Find and call appropriate procedure based on action name + //Each action name has an associated procedure stored in the + //service table. These are set at initialization. + + for( i = 0; ( ( i < TV_MAXACTIONS ) && + ( tv_service_table[service].ActionNames[i] != NULL ) ); + i++ ) { + + if( !strcmp( ca_event->ActionName, + tv_service_table[service].ActionNames[i] ) ) { + + if( ( !strcmp( tv_service_table[TV_SERVICE_CONTROL]. + VariableStrVal[TV_CONTROL_POWER], "1" ) ) + || ( !strcmp( ca_event->ActionName, "PowerOn" ) ) ) { + retCode = + tv_service_table[service].actions[i] ( ca_event-> + ActionRequest, + &ca_event-> + ActionResult, + &errorString ); + } else { + errorString = "Power is Off"; + retCode = UPNP_E_INTERNAL_ERROR; + } + action_found = 1; + break; + } + } + + if( !action_found ) { + ca_event->ActionResult = NULL; + strcpy( ca_event->ErrStr, "Invalid Action" ); + ca_event->ErrCode = 401; + } else { + if( retCode == UPNP_E_SUCCESS ) { + ca_event->ErrCode = UPNP_E_SUCCESS; + } else { + //copy the error string + strcpy( ca_event->ErrStr, errorString ); + switch ( retCode ) { + case UPNP_E_INVALID_PARAM: + { + ca_event->ErrCode = 402; + break; + } + case UPNP_E_INTERNAL_ERROR: + default: + { + ca_event->ErrCode = 501; + break; + } + + } + } + } + + return ( ca_event->ErrCode ); +} + +/****************************************************************************** + * TvDeviceSetServiceTableVar + * + * Description: + * Update the TvDevice service state table, and notify all subscribed + * control points of the updated state. Note that since this function + * blocks on the mutex TVDevMutex, to avoid a hang this function should + * not be called within any other function that currently has this mutex + * locked. + * + * Parameters: + * service -- The service number (TV_SERVICE_CONTROL or TV_SERVICE_PICTURE) + * variable -- The variable number (TV_CONTROL_POWER, TV_CONTROL_CHANNEL, + * TV_CONTROL_VOLUME, TV_PICTURE_COLOR, TV_PICTURE_TINT, + * TV_PICTURE_CONTRAST, or TV_PICTURE_BRIGHTNESS) + * value -- The string representation of the new value + * + *****************************************************************************/ +int +TvDeviceSetServiceTableVar( IN unsigned int service, + IN unsigned int variable, + IN char *value ) +{ + //IXML_Document *PropSet= NULL; + + if( ( service >= TV_SERVICE_SERVCOUNT ) + || ( variable >= tv_service_table[service].VariableCount ) + || ( strlen( value ) >= TV_MAX_VAL_LEN ) ) { + return ( 0 ); + } + + ithread_mutex_lock( &TVDevMutex ); + + strcpy( tv_service_table[service].VariableStrVal[variable], value ); + + /* + //Using utility api + PropSet= UpnpCreatePropertySet(1,tv_service_table[service]. + VariableName[variable], + tv_service_table[service]. + VariableStrVal[variable]); + + UpnpNotifyExt(device_handle, tv_service_table[service].UDN, + tv_service_table[service].ServiceId,PropSet); + + //Free created property set + Document_free(PropSet); + */ + + UpnpNotify( device_handle, + tv_service_table[service].UDN, + tv_service_table[service].ServiceId, + ( const char ** )&tv_service_table[service]. + VariableName[variable], + ( const char ** )&tv_service_table[service]. + VariableStrVal[variable], 1 ); + + ithread_mutex_unlock( &TVDevMutex ); + + return ( 1 ); + +} + +/****************************************************************************** + * TvDeviceSetPower + * + * Description: + * Turn the power on/off, update the TvDevice control service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * on -- If 1, turn power on. If 0, turn power off. + * + *****************************************************************************/ +int +TvDeviceSetPower( IN int on ) +{ + char value[TV_MAX_VAL_LEN]; + int ret = 0; + + if( on != POWER_ON && on != POWER_OFF ) { + SampleUtil_Print( "error: can't set power to value %d\n", on ); + return ( 0 ); + } + + /* + Vendor-specific code to turn the power on/off goes here + */ + + sprintf( value, "%d", on ); + ret = TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, TV_CONTROL_POWER, + value ); + + return ( ret ); +} + +/****************************************************************************** + * TvDevicePowerOn + * + * Description: + * Turn the power on. + * + * Parameters: + * + * IXML_Document * in - document of action request + * IXML_Document **out - action result + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDevicePowerOn( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + ( *out ) = NULL; + ( *errorString ) = NULL; + + if( TvDeviceSetPower( POWER_ON ) ) { + //create a response + + if( UpnpAddToActionResponse( out, "PowerOn", + TvServiceType[TV_SERVICE_CONTROL], + "Power", "1" ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * TvDevicePowerOff + * + * Description: + * Turn the power off. + * + * Parameters: + * + * IXML_Document * in - document of action request + * IXML_Document **out - action result + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDevicePowerOff( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + ( *out ) = NULL; + ( *errorString ) = NULL; + if( TvDeviceSetPower( POWER_OFF ) ) { + //create a response + + if( UpnpAddToActionResponse( out, "PowerOff", + TvServiceType[TV_SERVICE_CONTROL], + "Power", "0" ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + + return UPNP_E_SUCCESS; + } + + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; +} + +/****************************************************************************** + * TvDeviceSetChannel + * + * Description: + * Change the channel, update the TvDevice control service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceSetChannel( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + char *value = NULL; + + int channel = 0; + + ( *out ) = NULL; + ( *errorString ) = NULL; + + if( !( value = SampleUtil_GetFirstDocumentItem( in, "Channel" ) ) ) { + ( *errorString ) = "Invalid Channel"; + return UPNP_E_INVALID_PARAM; + } + + channel = atoi( value ); + + if( channel < MIN_CHANNEL || channel > MAX_CHANNEL ) { + + free( value ); + SampleUtil_Print( "error: can't change to channel %d\n", channel ); + ( *errorString ) = "Invalid Channel"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, + TV_CONTROL_CHANNEL, value ) ) { + if( UpnpAddToActionResponse( out, "SetChannel", + TvServiceType[TV_SERVICE_CONTROL], + "NewChannel", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + free( value ); + return UPNP_E_INTERNAL_ERROR; + } + free( value ); + return UPNP_E_SUCCESS; + } else { + free( value ); + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * IncrementChannel + * + * Description: + * Increment the channel. Read the current channel from the state + * table, add the increment, and then change the channel. + * + * Parameters: + * incr -- The increment by which to change the channel. + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +IncrementChannel( IN int incr, + IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + int curchannel, + newchannel; + + char *actionName = NULL; + char value[TV_MAX_VAL_LEN]; + + if( incr > 0 ) { + actionName = "IncreaseChannel"; + } else { + actionName = "DecreaseChannel"; + } + + ithread_mutex_lock( &TVDevMutex ); + curchannel = atoi( tv_service_table[TV_SERVICE_CONTROL]. + VariableStrVal[TV_CONTROL_CHANNEL] ); + ithread_mutex_unlock( &TVDevMutex ); + + newchannel = curchannel + incr; + + if( newchannel < MIN_CHANNEL || newchannel > MAX_CHANNEL ) { + SampleUtil_Print( "error: can't change to channel %d\n", + newchannel ); + ( *errorString ) = "Invalid Channel"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + sprintf( value, "%d", newchannel ); + + if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, + TV_CONTROL_CHANNEL, value ) ) { + if( UpnpAddToActionResponse( out, actionName, + TvServiceType[TV_SERVICE_CONTROL], + "Channel", value ) != UPNP_E_SUCCESS ) + { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } +} + +/****************************************************************************** + * TvDeviceDecreaseChannel + * + * Description: + * Decrease the channel. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceDecreaseChannel( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + return IncrementChannel( -1, in, out, errorString ); + +} + +/****************************************************************************** + * TvDeviceIncreaseChannel + * + * Description: + * Increase the channel. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceIncreaseChannel( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + return IncrementChannel( 1, in, out, errorString ); + +} + +/****************************************************************************** + * TvDeviceSetVolume + * + * Description: + * Change the volume, update the TvDevice control service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceSetVolume( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + char *value = NULL; + + int volume = 0; + + ( *out ) = NULL; + ( *errorString ) = NULL; + + if( !( value = SampleUtil_GetFirstDocumentItem( in, "Volume" ) ) ) { + ( *errorString ) = "Invalid Volume"; + return UPNP_E_INVALID_PARAM; + } + + volume = atoi( value ); + + if( volume < MIN_VOLUME || volume > MAX_VOLUME ) { + SampleUtil_Print( "error: can't change to volume %d\n", volume ); + ( *errorString ) = "Invalid Volume"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the volume goes here + */ + + if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, + TV_CONTROL_VOLUME, value ) ) { + if( UpnpAddToActionResponse( out, "SetVolume", + TvServiceType[TV_SERVICE_CONTROL], + "NewVolume", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + free( value ); + return UPNP_E_INTERNAL_ERROR; + } + free( value ); + return UPNP_E_SUCCESS; + } else { + free( value ); + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * IncrementVolume + * + * Description: + * Increment the volume. Read the current volume from the state + * table, add the increment, and then change the volume. + * + * Parameters: + * incr -- The increment by which to change the volume. + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +IncrementVolume( IN int incr, + IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + int curvolume, + newvolume; + char *actionName = NULL; + char value[TV_MAX_VAL_LEN]; + + if( incr > 0 ) { + actionName = "IncreaseVolume"; + } else { + actionName = "DecreaseVolume"; + } + + ithread_mutex_lock( &TVDevMutex ); + curvolume = atoi( tv_service_table[TV_SERVICE_CONTROL]. + VariableStrVal[TV_CONTROL_VOLUME] ); + ithread_mutex_unlock( &TVDevMutex ); + + newvolume = curvolume + incr; + + if( newvolume < MIN_VOLUME || newvolume > MAX_VOLUME ) { + SampleUtil_Print( "error: can't change to volume %d\n", + newvolume ); + ( *errorString ) = "Invalid Volume"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + sprintf( value, "%d", newvolume ); + + if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, + TV_CONTROL_VOLUME, value ) ) { + if( UpnpAddToActionResponse( out, actionName, + TvServiceType[TV_SERVICE_CONTROL], + "Volume", value ) != UPNP_E_SUCCESS ) + { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * TvDeviceIncrVolume + * + * Description: + * Increase the volume. + * + * Parameters: + * + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +TvDeviceIncreaseVolume( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementVolume( 1, in, out, errorString ); + +} + +/****************************************************************************** + * TvDeviceDecreaseVolume + * + * Description: + * Decrease the volume. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceDecreaseVolume( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementVolume( -1, in, out, errorString ); + +} + +/****************************************************************************** + * TvDeviceSetColor + * + * Description: + * Change the color, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceSetColor( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + char *value = NULL; + + int color = 0; + + ( *out ) = NULL; + ( *errorString ) = NULL; + if( !( value = SampleUtil_GetFirstDocumentItem( in, "Color" ) ) ) { + ( *errorString ) = "Invalid Color"; + return UPNP_E_INVALID_PARAM; + } + + color = atoi( value ); + + if( color < MIN_COLOR || color > MAX_COLOR ) { + SampleUtil_Print( "error: can't change to color %d\n", color ); + ( *errorString ) = "Invalid Color"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the volume goes here + */ + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_COLOR, value ) ) { + if( UpnpAddToActionResponse( out, "SetColor", + TvServiceType[TV_SERVICE_PICTURE], + "NewColor", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + free( value ); + return UPNP_E_INTERNAL_ERROR; + } + free( value ); + return UPNP_E_SUCCESS; + } else { + free( value ); + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * IncrementColor + * + * Description: + * Increment the color. Read the current color from the state + * table, add the increment, and then change the color. + * + * Parameters: + * incr -- The increment by which to change the color. + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ + +int +IncrementColor( IN int incr, + IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + int curcolor, + newcolor; + + char *actionName; + char value[TV_MAX_VAL_LEN]; + + if( incr > 0 ) { + actionName = "IncreaseColor"; + } else { + actionName = "DecreaseColor"; + } + + ithread_mutex_lock( &TVDevMutex ); + curcolor = atoi( tv_service_table[TV_SERVICE_PICTURE]. + VariableStrVal[TV_PICTURE_COLOR] ); + ithread_mutex_unlock( &TVDevMutex ); + + newcolor = curcolor + incr; + + if( newcolor < MIN_COLOR || newcolor > MAX_COLOR ) { + SampleUtil_Print( "error: can't change to color %d\n", newcolor ); + ( *errorString ) = "Invalid Color"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + sprintf( value, "%d", newcolor ); + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_COLOR, value ) ) { + if( UpnpAddToActionResponse( out, actionName, + TvServiceType[TV_SERVICE_PICTURE], + "Color", value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } +} + +/****************************************************************************** + * TvDeviceDecreaseColor + * + * Description: + * Decrease the color. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +TvDeviceDecreaseColor( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementColor( -1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceIncreaseColor + * + * Description: + * Increase the color. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +TvDeviceIncreaseColor( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementColor( 1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceSetTint + * + * Description: + * Change the tint, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceSetTint( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + char *value = NULL; + + int tint = -1; + + ( *out ) = NULL; + ( *errorString ) = NULL; + + if( !( value = SampleUtil_GetFirstDocumentItem( in, "Tint" ) ) ) { + ( *errorString ) = "Invalid Tint"; + return UPNP_E_INVALID_PARAM; + } + + tint = atoi( value ); + + if( tint < MIN_TINT || tint > MAX_TINT ) { + SampleUtil_Print( "error: can't change to tint %d\n", tint ); + ( *errorString ) = "Invalid Tint"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the volume goes here + */ + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_TINT, value ) ) { + if( UpnpAddToActionResponse( out, "SetTint", + TvServiceType[TV_SERVICE_PICTURE], + "NewTint", value ) != UPNP_E_SUCCESS ) + { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + free( value ); + return UPNP_E_INTERNAL_ERROR; + } + free( value ); + return UPNP_E_SUCCESS; + } else { + free( value ); + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * IncrementTint + * + * Description: + * Increment the tint. Read the current tint from the state + * table, add the increment, and then change the tint. + * + * Parameters: + * incr -- The increment by which to change the tint. + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +IncrementTint( IN int incr, + IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + int curtint, + newtint; + + char *actionName = NULL; + char value[TV_MAX_VAL_LEN]; + + if( incr > 0 ) { + actionName = "IncreaseTint"; + } else { + actionName = "DecreaseTint"; + } + + ithread_mutex_lock( &TVDevMutex ); + curtint = atoi( tv_service_table[TV_SERVICE_PICTURE]. + VariableStrVal[TV_PICTURE_TINT] ); + ithread_mutex_unlock( &TVDevMutex ); + + newtint = curtint + incr; + + if( newtint < MIN_TINT || newtint > MAX_TINT ) { + SampleUtil_Print( "error: can't change to tint %d\n", newtint ); + ( *errorString ) = "Invalid Tint"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + sprintf( value, "%d", newtint ); + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_TINT, value ) ) { + if( UpnpAddToActionResponse( out, actionName, + TvServiceType[TV_SERVICE_PICTURE], + "Tint", value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * TvDeviceIncreaseTint + * + * Description: + * Increase tint. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceIncreaseTint( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementTint( 1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceDecreaseTint + * + * Description: + * Decrease tint. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceDecreaseTint( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementTint( -1, in, out, errorString ); +} + +/***************************************************************************** + * TvDeviceSetContrast + * + * Description: + * Change the contrast, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + ****************************************************************************/ +int +TvDeviceSetContrast( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + char *value = NULL; + int contrast = -1; + + ( *out ) = NULL; + ( *errorString ) = NULL; + + if( !( value = SampleUtil_GetFirstDocumentItem( in, "Contrast" ) ) ) { + ( *errorString ) = "Invalid Contrast"; + return UPNP_E_INVALID_PARAM; + } + + contrast = atoi( value ); + + if( contrast < MIN_CONTRAST || contrast > MAX_CONTRAST ) { + SampleUtil_Print( "error: can't change to contrast %d\n", + contrast ); + ( *errorString ) = "Invalid Contrast"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the volume goes here + */ + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_CONTRAST, value ) ) { + if( UpnpAddToActionResponse( out, "SetContrast", + TvServiceType[TV_SERVICE_PICTURE], + "NewContrast", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + free( value ); + return UPNP_E_INTERNAL_ERROR; + } + free( value ); + return UPNP_E_SUCCESS; + } else { + free( value ); + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * IncrementContrast + * + * Description: + * Increment the contrast. Read the current contrast from the state + * table, add the increment, and then change the contrast. + * + * Parameters: + * incr -- The increment by which to change the contrast. + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +IncrementContrast( IN int incr, + IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + int curcontrast, + newcontrast; + + char *actionName = NULL; + char value[TV_MAX_VAL_LEN]; + + if( incr > 0 ) { + actionName = "IncreaseContrast"; + } else { + actionName = "DecreaseContrast"; + } + + ithread_mutex_lock( &TVDevMutex ); + curcontrast = atoi( tv_service_table[TV_SERVICE_PICTURE]. + VariableStrVal[TV_PICTURE_CONTRAST] ); + ithread_mutex_unlock( &TVDevMutex ); + + newcontrast = curcontrast + incr; + + if( newcontrast < MIN_CONTRAST || newcontrast > MAX_CONTRAST ) { + SampleUtil_Print( "error: can't change to contrast %d\n", + newcontrast ); + ( *errorString ) = "Invalid Contrast"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + sprintf( value, "%d", newcontrast ); + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_CONTRAST, value ) ) { + if( UpnpAddToActionResponse( out, actionName, + TvServiceType[TV_SERVICE_PICTURE], + "Contrast", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } +} + +/****************************************************************************** + * TvDeviceIncreaseContrast + * + * Description: + * + * Increase the contrast. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceIncreaseContrast( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + return IncrementContrast( 1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceDecreaseContrast + * + * Description: + * Decrease the contrast. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceDecreaseContrast( IXML_Document * in, + IXML_Document ** out, + char **errorString ) +{ + return IncrementContrast( -1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceSetBrightness + * + * Description: + * Change the brightness, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * brightness -- The brightness value to change to. + * + *****************************************************************************/ +int +TvDeviceSetBrightness( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + + char *value = NULL; + int brightness = -1; + + ( *out ) = NULL; + ( *errorString ) = NULL; + + if( !( value = SampleUtil_GetFirstDocumentItem( in, "Brightness" ) ) ) { + ( *errorString ) = "Invalid Brightness"; + return UPNP_E_INVALID_PARAM; + } + + brightness = atoi( value ); + + if( brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS ) { + SampleUtil_Print( "error: can't change to brightness %d\n", + brightness ); + ( *errorString ) = "Invalid Brightness"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the volume goes here + */ + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_BRIGHTNESS, value ) ) { + if( UpnpAddToActionResponse( out, "SetBrightness", + TvServiceType[TV_SERVICE_PICTURE], + "NewBrightness", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + free( value ); + return UPNP_E_INTERNAL_ERROR; + } + free( value ); + return UPNP_E_SUCCESS; + } else { + free( value ); + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + +} + +/****************************************************************************** + * IncrementBrightness + * + * Description: + * Increment the brightness. Read the current brightness from the state + * table, add the increment, and then change the brightness. + * + * Parameters: + * incr -- The increment by which to change the brightness. + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int +IncrementBrightness( IN int incr, + IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + int curbrightness, + newbrightness; + char *actionName = NULL; + char value[TV_MAX_VAL_LEN]; + + if( incr > 0 ) { + actionName = "IncreaseBrightness"; + } else { + actionName = "DecreaseBrightness"; + } + + ithread_mutex_lock( &TVDevMutex ); + curbrightness = atoi( tv_service_table[TV_SERVICE_PICTURE]. + VariableStrVal[TV_PICTURE_BRIGHTNESS] ); + ithread_mutex_unlock( &TVDevMutex ); + + newbrightness = curbrightness + incr; + + if( newbrightness < MIN_BRIGHTNESS || newbrightness > MAX_BRIGHTNESS ) { + SampleUtil_Print( "error: can't change to brightness %d\n", + newbrightness ); + ( *errorString ) = "Invalid Brightness"; + return UPNP_E_INVALID_PARAM; + } + + /* + Vendor-specific code to set the channel goes here + */ + + sprintf( value, "%d", newbrightness ); + + if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, + TV_PICTURE_BRIGHTNESS, value ) ) { + if( UpnpAddToActionResponse( out, actionName, + TvServiceType[TV_SERVICE_PICTURE], + "Brightness", + value ) != UPNP_E_SUCCESS ) { + ( *out ) = NULL; + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } + return UPNP_E_SUCCESS; + } else { + ( *errorString ) = "Internal Error"; + return UPNP_E_INTERNAL_ERROR; + } +} + +/****************************************************************************** + * TvDeviceIncreaseBrightness + * + * Description: + * Increase brightness. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceIncreaseBrightness( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + return IncrementBrightness( 1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceDecreaseBrightness + * + * Description: + * Decrease brightnesss. + * + * Parameters: + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int +TvDeviceDecreaseBrightness( IN IXML_Document * in, + OUT IXML_Document ** out, + OUT char **errorString ) +{ + return IncrementBrightness( -1, in, out, errorString ); +} + +/****************************************************************************** + * TvDeviceCallbackEventHandler + * + * Description: + * The callback handler registered with the SDK while registering + * root device. Dispatches the request to the appropriate procedure + * based on the value of EventType. The four requests handled by the + * device are: + * 1) Event Subscription requests. + * 2) Get Variable requests. + * 3) Action requests. + * + * Parameters: + * + * EventType -- The type of callback event + * Event -- Data structure containing event data + * Cookie -- Optional data specified during callback registration + * + *****************************************************************************/ +int +TvDeviceCallbackEventHandler( Upnp_EventType EventType, + void *Event, + void *Cookie ) +{ + + switch ( EventType ) { + + case UPNP_EVENT_SUBSCRIPTION_REQUEST: + + TvDeviceHandleSubscriptionRequest( ( struct + Upnp_Subscription_Request + * )Event ); + break; + + case UPNP_CONTROL_GET_VAR_REQUEST: + TvDeviceHandleGetVarRequest( ( struct Upnp_State_Var_Request + * )Event ); + break; + + case UPNP_CONTROL_ACTION_REQUEST: + TvDeviceHandleActionRequest( ( struct Upnp_Action_Request * ) + Event ); + break; + + /* + ignore these cases, since this is not a control point + */ + case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: + case UPNP_DISCOVERY_SEARCH_RESULT: + case UPNP_DISCOVERY_SEARCH_TIMEOUT: + case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: + case UPNP_CONTROL_ACTION_COMPLETE: + case UPNP_CONTROL_GET_VAR_COMPLETE: + case UPNP_EVENT_RECEIVED: + case UPNP_EVENT_RENEWAL_COMPLETE: + case UPNP_EVENT_SUBSCRIBE_COMPLETE: + case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: + break; + + default: + SampleUtil_Print + ( "Error in TvDeviceCallbackEventHandler: unknown event type %d\n", + EventType ); + } + + /* + Print a summary of the event received + */ + SampleUtil_PrintEvent( EventType, Event ); + + return ( 0 ); +} + +/****************************************************************************** + * TvDeviceStop + * + * Description: + * Stops the device. Uninitializes the sdk. + * + * Parameters: + * + *****************************************************************************/ +int +TvDeviceStop( ) +{ + UpnpUnRegisterRootDevice( device_handle ); + UpnpFinish( ); + SampleUtil_Finish( ); + ithread_mutex_destroy( &TVDevMutex ); + return UPNP_E_SUCCESS; +} + +/****************************************************************************** + * TvDeviceStart + * + * Description: + * Initializes the UPnP Sdk, registers the device, and sends out + * advertisements. + * + * Parameters: + * + * ip_address - ip address to initialize the sdk (may be NULL) + * if null, then the first non null loopback address is used. + * port - port number to initialize the sdk (may be 0) + * if zero, then a random number is used. + * desc_doc_name - name of description document. + * may be NULL. Default is tvdevicedesc.xml + * web_dir_path - path of web directory. + * may be NULL. Default is ./web (for Linux) or ../tvdevice/web + * for windows. + * pfun - print function to use. + * + *****************************************************************************/ +int +TvDeviceStart( char *ip_address, + unsigned short port, + char *desc_doc_name, + char *web_dir_path, + print_string pfun ) +{ + int ret = UPNP_E_SUCCESS; + + char desc_doc_url[DESC_URL_SIZE]; + + ithread_mutex_init( &TVDevMutex, NULL ); + + SampleUtil_Initialize( pfun ); + + SampleUtil_Print + ( "Initializing UPnP Sdk with \n \t ipaddress = %s port = %d\n", + ip_address, port ); + + if( ( ret = UpnpInit( ip_address, port ) ) != UPNP_E_SUCCESS ) { + SampleUtil_Print( "Error with UpnpInit -- %d\n", ret ); + UpnpFinish( ); + return ret; + } + + if( ip_address == NULL ) { + ip_address = UpnpGetServerIpAddress( ); + } + + if( port == 0 ) { + port = UpnpGetServerPort( ); + } + + SampleUtil_Print( "UPnP Initialized\n \t ipaddress= %s port = %d\n", + ip_address, port ); + + if( desc_doc_name == NULL ) + desc_doc_name = "tvdevicedesc.xml"; + + if( web_dir_path == NULL ) + web_dir_path = DEFAULT_WEB_DIR; + + snprintf( desc_doc_url, DESC_URL_SIZE, "http://%s:%d/%s", ip_address, + port, desc_doc_name ); + + SampleUtil_Print( "Specifying the webserver root directory -- %s\n", + web_dir_path ); + if( ( ret = + UpnpSetWebServerRootDir( web_dir_path ) ) != UPNP_E_SUCCESS ) { + SampleUtil_Print + ( "Error specifying webserver root directory -- %s: %d\n", + web_dir_path, ret ); + UpnpFinish( ); + return ret; + } + + SampleUtil_Print + ( "Registering the RootDevice\n\t with desc_doc_url: %s\n", + desc_doc_url ); + + if( ( ret = UpnpRegisterRootDevice( desc_doc_url, + TvDeviceCallbackEventHandler, + &device_handle, &device_handle ) ) + != UPNP_E_SUCCESS ) { + SampleUtil_Print( "Error registering the rootdevice : %d\n", ret ); + UpnpFinish( ); + return ret; + } else { + SampleUtil_Print( "RootDevice Registered\n" ); + + SampleUtil_Print( "Initializing State Table\n" ); + TvDeviceStateTableInit( desc_doc_url ); + SampleUtil_Print( "State Table Initialized\n" ); + + if( ( ret = + UpnpSendAdvertisement( device_handle, default_advr_expire ) ) + != UPNP_E_SUCCESS ) { + SampleUtil_Print( "Error sending advertisements : %d\n", ret ); + UpnpFinish( ); + return ret; + } + + SampleUtil_Print( "Advertisements Sent\n" ); + } + return UPNP_E_SUCCESS; +} diff --git a/libupnp/upnp/sample/tvdevice/upnp_tv_device.h b/libupnp/upnp/sample/tvdevice/upnp_tv_device.h new file mode 100644 index 0000000..c797409 --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/upnp_tv_device.h @@ -0,0 +1,638 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UPNP_TV_DEVICE_H +#define UPNP_TV_DEVICE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ithread.h" +#include +#include +#include +#include "upnp.h" +#include "sample_util.h" + +//Color constants +#define MAX_COLOR 10 +#define MIN_COLOR 1 + +//Brightness constants +#define MAX_BRIGHTNESS 10 +#define MIN_BRIGHTNESS 1 + +//Power constants +#define POWER_ON 1 +#define POWER_OFF 0 + +//Tint constants +#define MAX_TINT 10 +#define MIN_TINT 1 + +//Volume constants +#define MAX_VOLUME 10 +#define MIN_VOLUME 1 + +//Contrast constants +#define MAX_CONTRAST 10 +#define MIN_CONTRAST 1 + +//Channel constants +#define MAX_CHANNEL 100 +#define MIN_CHANNEL 1 + +//Number of services. +#define TV_SERVICE_SERVCOUNT 2 + +//Index of control service +#define TV_SERVICE_CONTROL 0 + +//Index of picture service +#define TV_SERVICE_PICTURE 1 + +//Number of control variables +#define TV_CONTROL_VARCOUNT 3 + +//Index of power variable +#define TV_CONTROL_POWER 0 + +//Index of channel variable +#define TV_CONTROL_CHANNEL 1 + +//Index of volume variable +#define TV_CONTROL_VOLUME 2 + +//Number of picture variables +#define TV_PICTURE_VARCOUNT 4 + +//Index of color variable +#define TV_PICTURE_COLOR 0 + +//Index of tint variable +#define TV_PICTURE_TINT 1 + +//Index of contrast variable +#define TV_PICTURE_CONTRAST 2 + +//Index of brightness variable +#define TV_PICTURE_BRIGHTNESS 3 + +//Max value length +#define TV_MAX_VAL_LEN 5 + +//Max actions +#define TV_MAXACTIONS 12 + +/* This should be the maximum VARCOUNT from above */ +#define TV_MAXVARS TV_PICTURE_VARCOUNT + + +extern char TvDeviceType[]; + +extern char *TvServiceType[]; + + + +/****************************************************************************** + * upnp_action + * + * Description: + * Prototype for all actions. For each action that a service + * implements, there is a corresponding function with this prototype. + * Pointers to these functions, along with action names, are stored + * in the service table. When an action request comes in the action + * name is matched, and the appropriate function is called. + * Each function returns UPNP_E_SUCCESS, on success, and a nonzero + * error code on failure. + * + * Parameters: + * + * IXML_Document * request - document of action request + * IXML_Document **out - action result + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ + +typedef int (*upnp_action) (IXML_Document *request, IXML_Document **out, + char **errorString); + +/* Structure for storing Tv Service + identifiers and state table */ +struct TvService { + + char UDN[NAME_SIZE]; /* Universally Unique Device Name */ + char ServiceId[NAME_SIZE]; + char ServiceType[NAME_SIZE]; + char *VariableName[TV_MAXVARS]; + char *VariableStrVal[TV_MAXVARS]; + char *ActionNames[TV_MAXACTIONS]; + upnp_action actions[TV_MAXACTIONS]; + unsigned int VariableCount; +}; + +//Array of service structures +extern struct TvService tv_service_table[]; + +//Device handle returned from sdk +extern UpnpDevice_Handle device_handle; + + +/* Mutex for protecting the global state table data + in a multi-threaded, asynchronous environment. + All functions should lock this mutex before reading + or writing the state table data. */ +extern ithread_mutex_t TVDevMutex; + + + +/****************************************************************************** + * SetActionTable + * + * Description: + * Initializes the action table for the specified service. + * Note that + * knowledge of the service description is + * assumed. Action names are hardcoded. + * Parameters: + * int serviceType - one of TV_SERVICE_CONTROL or, TV_SERVICE_PICTURE + * struct TvService *out - service containing action table to set. + * + *****************************************************************************/ +int SetActionTable(int serviceType, struct TvService * out); + +/****************************************************************************** + * TvDeviceStateTableInit + * + * Description: + * Initialize the device state table for + * this TvDevice, pulling identifier info + * from the description Document. Note that + * knowledge of the service description is + * assumed. State table variables and default + * values are currently hardcoded in this file + * rather than being read from service description + * documents. + * + * Parameters: + * DescDocURL -- The description document URL + * + *****************************************************************************/ +int TvDeviceStateTableInit(char*); + + +/****************************************************************************** + * TvDeviceHandleSubscriptionRequest + * + * Description: + * Called during a subscription request callback. If the + * subscription request is for this device and either its + * control service or picture service, then accept it. + * + * Parameters: + * sr_event -- The subscription request event structure + * + *****************************************************************************/ +int TvDeviceHandleSubscriptionRequest(struct Upnp_Subscription_Request *); + +/****************************************************************************** + * TvDeviceHandleGetVarRequest + * + * Description: + * Called during a get variable request callback. If the + * request is for this device and either its control service + * or picture service, then respond with the variable value. + * + * Parameters: + * cgv_event -- The control get variable request event structure + * + *****************************************************************************/ +int TvDeviceHandleGetVarRequest(struct Upnp_State_Var_Request *); + +/****************************************************************************** + * TvDeviceHandleActionRequest + * + * Description: + * Called during an action request callback. If the + * request is for this device and either its control service + * or picture service, then perform the action and respond. + * + * Parameters: + * ca_event -- The control action request event structure + * + *****************************************************************************/ +int TvDeviceHandleActionRequest(struct Upnp_Action_Request *); + +/****************************************************************************** + * TvDeviceCallbackEventHandler + * + * Description: + * The callback handler registered with the SDK while registering + * root device. Dispatches the request to the appropriate procedure + * based on the value of EventType. The four requests handled by the + * device are: + * 1) Event Subscription requests. + * 2) Get Variable requests. + * 3) Action requests. + * + * Parameters: + * + * EventType -- The type of callback event + * Event -- Data structure containing event data + * Cookie -- Optional data specified during callback registration + * + *****************************************************************************/ +int TvDeviceCallbackEventHandler(Upnp_EventType, void*, void*); + +/****************************************************************************** + * TvDeviceSetServiceTableVar + * + * Description: + * Update the TvDevice service state table, and notify all subscribed + * control points of the updated state. Note that since this function + * blocks on the mutex TVDevMutex, to avoid a hang this function should + * not be called within any other function that currently has this mutex + * locked. + * + * Parameters: + * service -- The service number (TV_SERVICE_CONTROL or TV_SERVICE_PICTURE) + * variable -- The variable number (TV_CONTROL_POWER, TV_CONTROL_CHANNEL, + * TV_CONTROL_VOLUME, TV_PICTURE_COLOR, TV_PICTURE_TINT, + * TV_PICTURE_CONTRAST, or TV_PICTURE_BRIGHTNESS) + * value -- The string representation of the new value + * + *****************************************************************************/ +int TvDeviceSetServiceTableVar(unsigned int, unsigned int, char*); + +//Control Service Actions + +/****************************************************************************** + * TvDevicePowerOn + * + * Description: + * Turn the power on. + * + * Parameters: + * + * IXML_Document * in - document of action request + * IXML_Document **out - action result + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDevicePowerOn(IN IXML_Document * in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDevicePowerOff + * + * Description: + * Turn the power off. + * + * Parameters: + * + * IXML_Document * in - document of action request + * IXML_Document **out - action result + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDevicePowerOff(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceSetChannel + * + * Description: + * Change the channel, update the TvDevice control service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceSetChannel(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceIncreaseChannel + * + * Description: + * Increase the channel. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceIncreaseChannel(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); +/****************************************************************************** + * TvDeviceDecreaseChannel + * + * Description: + * Decrease the channel. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceDecreaseChannel(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); +/****************************************************************************** + * TvDeviceSetVolume + * + * Description: + * Change the volume, update the TvDevice control service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceSetVolume(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceIncreaseVolume + * + * Description: + * Increase the volume. + * + * Parameters: + * + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int TvDeviceIncreaseVolume(IN IXML_Document *in, OUT IXML_Document**out, + OUT char **errorString); + + +/****************************************************************************** + * TvDeviceDecreaseVolume + * + * Description: + * Decrease the volume. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceDecreaseVolume(IN IXML_Document *in, OUT IXML_Document**out, + OUT char **errorString); + + +//Picture Service Actions + +/****************************************************************************** + * TvDeviceSetColor + * + * Description: + * Change the color, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceSetColor(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + + +/****************************************************************************** + * TvDeviceIncreaseColor + * + * Description: + * Increase the color. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int TvDeviceIncreaseColor(IN IXML_Document * in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceDecreaseColor + * + * Description: + * Decrease the color. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + *****************************************************************************/ +int TvDeviceDecreaseColor(IN IXML_Document * in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceSetTint + * + * Description: + * Change the tint, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceSetTint(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceIncreaseTint + * + * Description: + * Increase tint. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceIncreaseTint(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceDecreaseTint + * + * Description: + * Decrease tint. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceDecreaseTint(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/***************************************************************************** + * TvDeviceSetContrast + * + * Description: + * Change the contrast, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + ****************************************************************************/ +int TvDeviceSetContrast(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceIncreaseContrast + * + * Description: + * + * Increase the contrast. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceIncreaseContrast(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); +/****************************************************************************** + * TvDeviceDecreaseContrast + * + * Description: + * Decrease the contrast. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceDecreaseContrast(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceSetBrightness + * + * Description: + * Change the brightness, update the TvDevice picture service + * state table, and notify all subscribed control points of the + * updated state. + * + * Parameters: + * brightness -- The brightness value to change to. + * + *****************************************************************************/ +int TvDeviceSetBrightness(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceIncreaseBrightness + * + * Description: + * Increase brightness. + * + * Parameters: + * + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceIncreaseBrightness(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +/****************************************************************************** + * TvDeviceDecreaseBrightness + * + * Description: + * Decrease brightnesss. + * + * Parameters: + * IXML_Document * in - action request document + * IXML_Document **out - action result document + * char **errorString - errorString (in case action was unsuccessful) + * + *****************************************************************************/ +int TvDeviceDecreaseBrightness(IN IXML_Document *in, OUT IXML_Document **out, + OUT char **errorString); + +int TvDeviceStart(char * ip_address, unsigned short port,char * desc_doc_name, + char *web_dir_path, print_string pfun); +int TvDeviceStop(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libupnp/upnp/sample/tvdevice/web/tvcontrolSCPD.xml b/libupnp/upnp/sample/tvdevice/web/tvcontrolSCPD.xml new file mode 100644 index 0000000..f556a59 --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/web/tvcontrolSCPD.xml @@ -0,0 +1,178 @@ + + + + + 1 + 0 + + + + + + + + PowerOn + + + + Power + Power + out + + + + + + PowerOff + + + + Power + Power + out + + + + + + SetChannel + + + + NewChannel + + Channel + out + + + + Channel + Channel + in + + + + + + IncreaseChannel + + + Channel + + Channel + out + + + + + + DecreaseChannel + + + Channel + + Channel + out + + + + + + SetVolume + + + + NewVolume + + Volume + out + + + Volume + Volume + in + + + + + + IncreaseVolume + + + + Volume + + Volume + out + + + + + + + DecreaseVolume + + + + Volume + + Volume + out + + + + + + + + + + + + + Power + Boolean + 0 + + + + Channel + i4 + + 1 + 100 + 1 + + 1 + + + + Volume + i4 + + 0 + 10 + 1 + + 5 + + + + + + + + + + + + + + + + + + + + + diff --git a/libupnp/upnp/sample/tvdevice/web/tvdevicedesc.xml b/libupnp/upnp/sample/tvdevice/web/tvdevicedesc.xml new file mode 100644 index 0000000..e14d854 --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/web/tvdevicedesc.xml @@ -0,0 +1,37 @@ + + + + 1 + 0 + + + urn:schemas-upnp-org:device:tvdevice:1 + UPnP Television Emulator + TV Manufacturer Name + http://www.manufacturer.com + UPnP Television Device Emulator 1.0 + TVEmulator + 1.0 + http://www.manufacturer.com/TVEmulator/ + 123456789001 + uuid:Upnp-TVEmulator-1_0-1234567890001 + 123456789 + + + urn:schemas-upnp-org:service:tvcontrol:1 + urn:upnp-org:serviceId:tvcontrol1 + /upnp/control/tvcontrol1 + /upnp/event/tvcontrol1 + /tvcontrolSCPD.xml + + + urn:schemas-upnp-org:service:tvpicture:1 + urn:upnp-org:serviceId:tvpicture1 + /upnp/control/tvpicture1 + /upnp/event/tvpicture1 + /tvpictureSCPD.xml + + + /tvdevicepres.html + + diff --git a/libupnp/upnp/sample/tvdevice/web/tvdevicepres.html b/libupnp/upnp/sample/tvdevice/web/tvdevicepres.html new file mode 100644 index 0000000..5f066c9 --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/web/tvdevicepres.html @@ -0,0 +1,300 @@ + + + + Presentation page for Controlling a UPnP Television + + + + +

Device Name

+ + +

TvDevice Control State Table

+ + + + + + + + + + + + + + + + + + + + + +
VariableValueActions
Power

+    + +    + +    +
Channel

+    + +    + +    +
Volume

+    + +    + +    +
+ + +
+
+
+
+
+
+
+
+
+ +

TvDevice Picture State Table

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableValueActions
Color

+    + +    + +    +
Tint

+    + +    + +    +
Contrast

+    + +    + +    +
Brightness

+    + +    + +    +
+

 

+ + + + + + + + + diff --git a/libupnp/upnp/sample/tvdevice/web/tvpictureSCPD.xml b/libupnp/upnp/sample/tvdevice/web/tvpictureSCPD.xml new file mode 100644 index 0000000..8388fd7 --- /dev/null +++ b/libupnp/upnp/sample/tvdevice/web/tvpictureSCPD.xml @@ -0,0 +1,251 @@ + + + + + 1 + 0 + + + + + + + SetColor + + + NewColor + + Color + out + + + + Color + Color + in + + + + + + IncreaseColor + + + Color + + Color + out + + + + + + DecreaseColor + + + Color + + Color + out + + + + + + SetTint + + + NewTint + + Tint + out + + + + Tint + Tint + in + + + + + + IncreaseTint + + + Tint + + Tint + out + + + + + + DecreaseTint + + + Tint + + Tint + out + + + + + + SetContrast + + + NewContrast + + Contrast + out + + + Contrast + Contrast + in + + + + + + IncreaseContrast + + + Contrast + + Contrast + out + + + + + + DecreaseContrast + + + Contrast + + Contrast + out + + + + + + SetBrightness + + + + NewBrightness + + Brightness + out + + + + Brightness + Brightness + in + + + + + + IncreaseBrightness + + + + Brightness + + Brightness + out + + + + + + DecreaseBrightness + + + + Brightness + + Brightness + out + + + + + + + + + + + + Color + i4 + + 1 + 10 + 1 + + 5 + + + + Tint + i4 + + 1 + 10 + 1 + + 5 + + + + Contrast + i4 + + 1 + 10 + 1 + + 5 + + + + Brightness + i4 + + 1 + 10 + 1 + + 5 + + + + + + + + + + + + + + + + + + + + + + diff --git a/libupnp/upnp/src/api/upnpapi.c b/libupnp/upnp/src/api/upnpapi.c new file mode 100644 index 0000000..40764e6 --- /dev/null +++ b/libupnp/upnp/src/api/upnpapi.c @@ -0,0 +1,4255 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +//File upnpapi.c +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef SPARC_SOLARIS +// #include + #include +#else + #include + #include + #include +#endif +#include "upnpapi.h" +#include "httpreadwrite.h" +#include "ssdplib.h" +#include "soaplib.h" +#include "ThreadPool.h" +#include "membuffer.h" +#include +#include +#include + +#include "httpreadwrite.h" + +//************************************ +//Needed for GENA +#include "gena.h" +#include "service_table.h" +#include "miniserver.h" +//******************************************* + +/* + ********************* */ +#ifdef INTERNAL_WEB_SERVER +#include "webserver.h" +#include "urlconfig.h" +#endif // INTERNAL_WEB_SERVER +/* + ****************** */ + +//Mutex to synchronize the subscription handling at the client side +CLIENTONLY( ithread_mutex_t GlobalClientSubscribeMutex; + ) + //Mutex to synchronize handles ( root device or control point handle) + ithread_mutex_t GlobalHndMutex; + +//Mutex to synchronize the uuid creation process + ithread_mutex_t gUUIDMutex; + + TimerThread gTimerThread; + + ThreadPool gRecvThreadPool; + + ThreadPool gSendThreadPool; + +//Flag to indicate the state of web server + WebServerState bWebServerState = WEB_SERVER_DISABLED; + +// static buffer to store the local host ip address or host name + char LOCAL_HOST[LINE_SIZE]; + +// local port for the mini-server + unsigned short LOCAL_PORT; + +// UPnP device and control point handle table + void *HandleTable[NUM_HANDLE]; + +//This structure is for virtual directory callbacks + struct UpnpVirtualDirCallbacks virtualDirCallback; + +// a local dir which serves as webserver root + extern membuffer gDocumentRootDir; + +// Maximum content-length that the SDK will process on an incoming packet. +// Content-Length exceeding this size will be not processed and error 413 +// (HTTP Error Code) will be returned to the remote end point. +size_t g_maxContentLength = DEFAULT_SOAP_CONTENT_LENGTH; // in bytes + +// Global variable to denote the state of Upnp SDK +// = 0 if uninitialized, = 1 if initialized. + int UpnpSdkInit = 0; + +// Global variable to denote the state of Upnp SDK device registration. +// = 0 if unregistered, = 1 if registered. + int UpnpSdkDeviceRegistered = 0; + +// Global variable to denote the state of Upnp SDK client registration. +// = 0 if unregistered, = 1 if registered. + int UpnpSdkClientRegistered = 0; + +/**************************************************************************** + * Function: UpnpInit + * + * Parameters: + * IN const char * HostIP: Local IP Address + * IN short DestPort: Local Port to listen for incoming connections + * Description: + * Initializes + * - Mutex objects, + * - Handle Table + * - Thread Pool and Thread Pool Attributes + * - MiniServer(starts listening for incoming requests) + * and WebServer (Sends request to the + * Upper Layer after HTTP Parsing) + * - Checks for IP Address passed as an argument. IF NULL, + * gets local host name + * - Sets GENA and SOAP Callbacks. + * - Starts the timer thread. + * + * Returns: + * UPNP_E_SUCCESS on success, nonzero on failure. + * UPNP_E_INIT_FAILED if Initialization fails. + * UPNP_E_INIT if UPnP is already initialized + *****************************************************************************/ +int UpnpInit( IN const char *HostIP, + IN unsigned short DestPort ) +{ + int retVal = 0; + ThreadPoolAttr attr; + + if( UpnpSdkInit == 1 ) { + // already initialized + return UPNP_E_INIT; + } + + membuffer_init( &gDocumentRootDir ); + + srand( time( NULL ) ); // needed by SSDP or other parts + + DBGONLY( if( InitLog( ) != UPNP_E_SUCCESS ) + return UPNP_E_INIT_FAILED; ); + + DBGONLY( UpnpPrintf + ( UPNP_INFO, API, __FILE__, __LINE__, "Inside UpnpInit \n" ); + ) + //initialize mutex + if( ithread_mutex_init( &GlobalHndMutex, NULL ) != 0 ) { + return UPNP_E_INIT_FAILED; + } + + if( ithread_mutex_init( &gUUIDMutex, NULL ) != 0 ) { + return UPNP_E_INIT_FAILED; + } + //initialize subscribe mutex + CLIENTONLY( if + ( ithread_mutex_init( &GlobalClientSubscribeMutex, NULL ) + != 0 ) { + return UPNP_E_INIT_FAILED;} + ) + + HandleLock( ); + if( HostIP != NULL ) + strcpy( LOCAL_HOST, HostIP ); + else { + if( getlocalhostname( LOCAL_HOST ) != UPNP_E_SUCCESS ) { + HandleUnlock( ); + return UPNP_E_INIT_FAILED; + } + } + + if( UpnpSdkInit != 0 ) { + HandleUnlock( ); + return UPNP_E_INIT; + } + + InitHandleList( ); + HandleUnlock( ); + + TPAttrInit( &attr ); + TPAttrSetMaxThreads( &attr, MAX_THREADS ); + TPAttrSetMinThreads( &attr, MIN_THREADS ); + TPAttrSetJobsPerThread( &attr, JOBS_PER_THREAD ); + TPAttrSetIdleTime( &attr, THREAD_IDLE_TIME ); + + if( ThreadPoolInit( &gSendThreadPool, &attr ) != UPNP_E_SUCCESS ) { + UpnpSdkInit = 0; + UpnpFinish( ); + return UPNP_E_INIT_FAILED; + } + + if( ThreadPoolInit( &gRecvThreadPool, &attr ) != UPNP_E_SUCCESS ) { + UpnpSdkInit = 0; + UpnpFinish( ); + return UPNP_E_INIT_FAILED; + } + + UpnpSdkInit = 1; +#if EXCLUDE_SOAP == 0 + DEVICEONLY( SetSoapCallback( soap_device_callback ); + ); +#endif +#if EXCLUDE_GENA == 0 + SetGenaCallback( genaCallback ); +#endif + + if( ( retVal = TimerThreadInit( &gTimerThread, + &gSendThreadPool ) ) != + UPNP_E_SUCCESS ) { + UpnpSdkInit = 0; + UpnpFinish( ); + return retVal; + } +#if EXCLUDE_MINISERVER == 0 + if( ( retVal = StartMiniServer( DestPort ) ) <= 0 ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "Miniserver failed to start" ); + ) + UpnpFinish( ); + UpnpSdkInit = 0; + if( retVal != -1 ) + return retVal; + else // if miniserver is already running for unknown reasons! + return UPNP_E_INIT_FAILED; + } +#endif + DestPort = retVal; + LOCAL_PORT = DestPort; + +#if EXCLUDE_WEB_SERVER == 0 + if( ( retVal = + UpnpEnableWebserver( WEB_SERVER_ENABLED ) ) != UPNP_E_SUCCESS ) { + UpnpFinish( ); + UpnpSdkInit = 0; + return retVal; + } +#endif + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Host Ip: %s Host Port: %d\n", LOCAL_HOST, + LOCAL_PORT ) ); + + DBGONLY( UpnpPrintf + ( UPNP_INFO, API, __FILE__, __LINE__, "Exiting UpnpInit \n" ); + ) + + return UPNP_E_SUCCESS; + +} /***************** end of UpnpInit ******************/ + +DBGONLY( +static void +PrintThreadPoolStats (const char* DbgFileName, int DbgLineNo, + const char* msg, const ThreadPoolStats* const stats) +{ + UpnpPrintf (UPNP_INFO, API, DbgFileName, DbgLineNo, + "%s \n High Jobs pending = %d \nMed Jobs Pending = %d\n" + " Low Jobs Pending = %d \nWorker Threads = %d\n" + "Idle Threads = %d\nPersistent Threads = %d\n" + "Average Time spent in High Q = %lf\n" + "Average Time spent in Med Q = %lf\n" + "Average Time spent in Low Q = %lf\n" + "Max Threads Used: %d\nTotal Work Time= %lf\n" + "Total Idle Time = %lf\n", + msg, + stats->currentJobsHQ, stats->currentJobsMQ, + stats->currentJobsLQ, stats->workerThreads, + stats->idleThreads, stats->persistentThreads, + stats->avgWaitHQ, stats->avgWaitMQ, stats->avgWaitLQ, + stats->maxThreads, stats->totalWorkTime, + stats->totalIdleTime ); +}) + + +/**************************************************************************** + * Function: UpnpFinish + * + * Parameters: NONE + * + * Description: + * Checks for pending jobs and threads + * Unregisters either the client or device + * Shuts down the Timer Thread + * Stops the Mini Server + * Uninitializes the Thread Pool + * For Win32 cleans up Winsock Interface + * Cleans up mutex objects + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpFinish( ) +{ + DEVICEONLY( UpnpDevice_Handle device_handle; + ) + CLIENTONLY( UpnpClient_Handle client_handle; + ) + struct Handle_Info *temp; + + DBGONLY( ThreadPoolStats stats; + ) + + if( UpnpSdkInit != 1 ) + return UPNP_E_FINISH; + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Inside UpnpFinish : UpnpSdkInit is :%d:\n", + UpnpSdkInit ); if( UpnpSdkInit == 1 ) { + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpFinish : UpnpSdkInit is ONE\n" );} + ThreadPoolGetStats( &gRecvThreadPool, &stats ); + PrintThreadPoolStats (__FILE__, __LINE__, + "Recv Thread Pool", &stats); + ThreadPoolGetStats( &gSendThreadPool, &stats ); + PrintThreadPoolStats (__FILE__, __LINE__, + "Send Thread Pool", &stats); + ) +#ifdef INCLUDE_DEVICE_APIS + if( GetDeviceHandleInfo( &device_handle, &temp ) == HND_DEVICE ) + UpnpUnRegisterRootDevice( device_handle ); +#endif + +#ifdef INCLUDE_CLIENT_APIS + if( GetClientHandleInfo( &client_handle, &temp ) == HND_CLIENT ) + UpnpUnRegisterClient( client_handle ); +#endif + + TimerThreadShutdown( &gTimerThread ); + + StopMiniServer( ); + +#if EXCLUDE_WEB_SERVER == 0 + web_server_destroy( ); +#endif + + ThreadPoolShutdown( &gSendThreadPool ); + ThreadPoolShutdown( &gRecvThreadPool ); + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, "Exiting UpnpFinish : UpnpSdkInit is :%d:\n", UpnpSdkInit ); ThreadPoolGetStats( &gRecvThreadPool, &stats ); PrintThreadPoolStats ( __FILE__, __LINE__, "Recv Thread Pool", &stats); ThreadPoolGetStats( &gSendThreadPool, &stats ); PrintThreadPoolStats (__FILE__, __LINE__, "Send Thread Pool", &stats); ) // DBGONLY + DBGONLY( CloseLog( ); + ); + + CLIENTONLY( ithread_mutex_destroy( &GlobalClientSubscribeMutex ); + ) + + ithread_mutex_destroy( &GlobalHndMutex ); + ithread_mutex_destroy( &gUUIDMutex ); + + // remove all virtual dirs + UpnpRemoveAllVirtualDirs( ); + + UpnpSdkInit = 0; + + return UPNP_E_SUCCESS; + +} /********************* End of UpnpFinish *************************/ + +/**************************************************************************** + * Function: UpnpGetServerPort + * + * Parameters: NONE + * + * Description: + * Gives back the miniserver port. + * + * Return Values: + * local port on success, zero on failure. + *****************************************************************************/ +unsigned short +UpnpGetServerPort( void ) +{ + + if( UpnpSdkInit != 1 ) + return 0; + + return LOCAL_PORT; +} + +/*************************************************************************** + * Function: UpnpGetServerIpAddress + * + * Parameters: NONE + * + * Description: + * Gives back the local ipaddress. + * + * Return Values: char * + * return the IP address string on success else NULL of failure + ***************************************************************************/ +char * +UpnpGetServerIpAddress( void ) +{ + + if( UpnpSdkInit != 1 ) + return NULL; + + return LOCAL_HOST; +} + +#ifdef INCLUDE_DEVICE_APIS +#if 0 + +/**************************************************************************** + * Function: UpnpAddRootDevice + * + * Parameters: + * IN const char *DescURL: Location of the root device + * description xml file + * IN UpnpDevice_Handle Hnd: The device handle + * + * Description: + * downloads the description file and update the service table of the + * device. This function has been deprecated. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpAddRootDevice( IN const char *DescURL, + IN UpnpDevice_Handle Hnd ) +{ + int retVal = 0; + struct Handle_Info *HInfo; + IXML_Document *temp; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + if( ( retVal = + UpnpDownloadXmlDoc( DescURL, &( temp ) ) ) != UPNP_E_SUCCESS ) { + return retVal; + } + + HandleLock( ); + if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) { + HandleUnlock( ); + ixmlDocument_free( temp ); + return UPNP_E_INVALID_HANDLE; + } + + if( addServiceTable + ( ( IXML_Node * ) temp, &HInfo->ServiceTable, DescURL ) ) { + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpAddRootDevice: GENA Service Table \n" ); + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Here are the known services: \n" ); + printServiceTable( &HInfo->ServiceTable, UPNP_INFO, API ); + ) + } else { + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "\nUpnpAddRootDevice: No Eventing Support Found \n" ); + ) + } + + ixmlDocument_free( temp ); + HandleUnlock( ); + + return UPNP_E_SUCCESS; +} +#endif // 0 +#endif //INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_DEVICE_APIS + +/**************************************************************************** + * Function: UpnpRegisterRootDevice + * + * Parameters: + * IN const char *DescUrl:Pointer to a string containing the + * description URL for this root device instance. + * IN Upnp_FunPtr Callback: Pointer to the callback function for + * receiving asynchronous events. + * IN const void *Cookie: Pointer to user data returned with the + * callback function when invoked. + * OUT UpnpDevice_Handle *Hnd: Pointer to a variable to store the + * new device handle. + * + * Description: + * This function registers a device application with + * the UPnP Library. A device application cannot make any other API + * calls until it registers using this function. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpRegisterRootDevice( IN const char *DescUrl, + IN Upnp_FunPtr Fun, + IN const void *Cookie, + OUT UpnpDevice_Handle * Hnd ) +{ + + struct Handle_Info *HInfo; + int retVal = 0; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Inside UpnpRegisterRootDevice \n" ); + ) + HandleLock( ); + + if( UpnpSdkDeviceRegistered ) { + HandleUnlock( ); + return UPNP_E_ALREADY_REGISTERED; + } + + if( Hnd == NULL || Fun == NULL || + DescUrl == NULL || strlen( DescUrl ) == 0 ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + if( ( *Hnd = GetFreeHandle( ) ) == UPNP_E_OUTOF_HANDLE ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + + HInfo = ( struct Handle_Info * )malloc( sizeof( struct Handle_Info ) ); + if( HInfo == NULL ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + HandleTable[*Hnd] = HInfo; + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Root device URL is %s\n", DescUrl ); + ) + + HInfo->aliasInstalled = 0; + HInfo->HType = HND_DEVICE; + strcpy( HInfo->DescURL, DescUrl ); + HInfo->Callback = Fun; + HInfo->Cookie = ( void * )Cookie; + HInfo->MaxAge = DEFAULT_MAXAGE; + HInfo->DeviceList = NULL; + HInfo->ServiceList = NULL; + HInfo->DescDocument = NULL; + CLIENTONLY( ListInit( &HInfo->SsdpSearchList, NULL, NULL ); + ); + CLIENTONLY( HInfo->ClientSubList = NULL; + ) + HInfo->MaxSubscriptions = UPNP_INFINITE; + HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE; + + if( ( retVal = + UpnpDownloadXmlDoc( HInfo->DescURL, &( HInfo->DescDocument ) ) ) + != UPNP_E_SUCCESS ) { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + FreeHandle( *Hnd ); + HandleUnlock( ); + return retVal; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice: Valid Description\n" ); + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice: DescURL : %s\n", + HInfo->DescURL ); + ) + + HInfo->DeviceList = + ixmlDocument_getElementsByTagName( HInfo->DescDocument, "device" ); + if( HInfo->DeviceList == NULL ) { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + ixmlDocument_free( HInfo->DescDocument ); + FreeHandle( *Hnd ); + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice: No devices found for RootDevice\n" ); + ) + return UPNP_E_INVALID_DESC; + } + + HInfo->ServiceList = + ixmlDocument_getElementsByTagName( HInfo->DescDocument, + "serviceList" ); + if( HInfo->ServiceList == NULL ) { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + ixmlNodeList_free( HInfo->DeviceList ); + ixmlDocument_free( HInfo->DescDocument ); + FreeHandle( *Hnd ); + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice: No services found for RootDevice\n" ); + ) + return UPNP_E_INVALID_DESC; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice: Gena Check\n" ); + ) + //******************************* + //GENA SET UP + //******************************* + if( getServiceTable( ( IXML_Node * ) HInfo->DescDocument, + &HInfo->ServiceTable, HInfo->DescURL ) ) { + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice: GENA Service Table \n" ); + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Here are the known services: \n" ); + printServiceTable( &HInfo->ServiceTable, UPNP_INFO, API ); + ) + } else { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + FreeHandle( *Hnd ); + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "\nUpnpRegisterRootDevice: Errors retrieving service table \n" ); + ) + return UPNP_E_INVALID_DESC; + } + + UpnpSdkDeviceRegistered = 1; + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Exiting RegisterRootDevice Successfully\n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpRegisterRootDevice *********************/ + +#endif // INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_DEVICE_APIS +#if 0 + +/**************************************************************************** + * Function: UpnpRemoveRootDevice + * + * Parameters: + * IN const char *DescURL: Location of the root device + * description xml file + * IN UpnpDevice_Handle Hnd: The device handle + * + * Description: + * downloads the description file and update the service table of the + * device. This function has been deprecated. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpRemoveRootDevice( IN const char *DescURL, + IN UpnpDevice_Handle Hnd ) +{ + int retVal = 0; + struct Handle_Info *HInfo; + + IXML_Document *temp; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + if( ( retVal = + UpnpDownloadXmlDoc( DescURL, &( temp ) ) ) != UPNP_E_SUCCESS ) { + return retVal; + } + + HandleLock( ); + if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) { + HandleUnlock( ); + ixmlDocument_free( temp ); + return UPNP_E_INVALID_HANDLE; + } + + if( removeServiceTable( ( IXML_Node * ) temp, &HInfo->ServiceTable ) ) { + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpRemoveRootDevice: GENA Service Table \n" ); + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Here are the known services: \n" ); + printServiceTable( &HInfo->ServiceTable, UPNP_INFO, API ); + ) + } else { + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "\nUpnpRemoveRootDevice: No Services Removed\n" ); + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Here are the known services: \n" ); + printServiceTable( &HInfo->ServiceTable, UPNP_INFO, API ); + ) + } + + HandleUnlock( ); + + ixmlDocument_free( temp ); + return UPNP_E_SUCCESS; +} +#endif //0 +#endif //INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_DEVICE_APIS + +/**************************************************************************** + * Function: UpnpUnRegisterRootDevice + * + * Parameters: + * IN UpnpDevice_Handle Hnd: The handle of the device instance + * to unregister + * Description: + * This function unregisters a root device registered with + * UpnpRegisterRootDevice} or UpnpRegisterRootDevice2. After this call, the + * UpnpDevice_Handle Hnd is no longer valid. For all advertisements that + * have not yet expired, the UPnP library sends a device unavailable message + * automatically. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpUnRegisterRootDevice( IN UpnpDevice_Handle Hnd ) +{ + int retVal = 0; + struct Handle_Info *HInfo = NULL; + + // struct Handle_Info *info=NULL; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + HandleLock( ); + if( !UpnpSdkDeviceRegistered ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Inside UpnpUnRegisterRootDevice \n" ); + ) +#if EXCLUDE_GENA == 0 + if( genaUnregisterDevice( Hnd ) != UPNP_E_SUCCESS ) + return UPNP_E_INVALID_HANDLE; +#endif + + HandleLock( ); + if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + +#if EXCLUDE_SSDP == 0 + retVal = AdvertiseAndReply( -1, Hnd, 0, ( struct sockaddr_in * )NULL, + ( char * )NULL, ( char * )NULL, + ( char * )NULL, HInfo->MaxAge ); +#endif + + HandleLock( ); + if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + //info = (struct Handle_Info *) HandleTable[Hnd]; + ixmlNodeList_free( HInfo->DeviceList ); + ixmlNodeList_free( HInfo->ServiceList ); + ixmlDocument_free( HInfo->DescDocument ); + + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + +#ifdef INTERNAL_WEB_SERVER + if( HInfo->aliasInstalled ) { + web_server_set_alias( NULL, NULL, 0, 0 ); + } +#endif // INTERNAL_WEB_SERVER + + FreeHandle( Hnd ); + UpnpSdkDeviceRegistered = 0; + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Exiting UpnpUnRegisterRootDevice \n" ); + ) + + return retVal; + +} /****************** End of UpnpUnRegisterRootDevice *********************/ + +#endif //INCLUDE_DEVICE_APIS + +// ************************************************************* +#ifdef INCLUDE_DEVICE_APIS +#ifdef INTERNAL_WEB_SERVER + +/************************************************************************** + * Function: GetNameForAlias + * + * Parameters: + * IN char *name: name of the file + * OUT char** alias: pointer to alias string + * + * Description: + * This function determines alias for given name which is a file name + * or URL. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + ***************************************************************************/ +static int +GetNameForAlias( IN char *name, + OUT char **alias ) +{ + char *ext; + char *al; + + ext = strrchr( name, '.' ); + if( ext == NULL || strcasecmp( ext, ".xml" ) != 0 ) { + return UPNP_E_EXT_NOT_XML; + } + + al = strrchr( name, '/' ); + if( al == NULL ) { + *alias = name; + } else { + *alias = al; + } + + return UPNP_E_SUCCESS; +} + +/************************************************************************** + * Function: get_server_addr + * + * Parameters: + * OUT struct sockaddr_in* serverAddr: pointer to server address + * structure + * + * Description: + * This function fills the sockadr_in with miniserver information. + * + * Return Values: VOID + * + ***************************************************************************/ +static void +get_server_addr( OUT struct sockaddr_in *serverAddr ) +{ + memset( serverAddr, 0, sizeof( struct sockaddr_in ) ); + + serverAddr->sin_family = AF_INET; + serverAddr->sin_port = htons( LOCAL_PORT ); + //inet_aton( LOCAL_HOST, &serverAddr->sin_addr ); + serverAddr->sin_addr.s_addr = inet_addr( LOCAL_HOST ); +} + +/************************************************************************** + * Function: GetDescDocumentAndURL ( In the case of device) + * + * Parameters: + * IN Upnp_DescType descriptionType: pointer to server address + * structure + * IN char* description: + * IN unsigned int bufferLen: + * IN int config_baseURL: + * OUT IXML_Document **xmlDoc: + * OUT char descURL[LINE_SIZE]: + * + * Description: + * This function fills the sockadr_in with miniserver information. + * + * Return Values: VOID + * + ***************************************************************************/ +static int +GetDescDocumentAndURL( IN Upnp_DescType descriptionType, + IN char *description, + IN unsigned int bufferLen, + IN int config_baseURL, + OUT IXML_Document ** xmlDoc, + OUT char descURL[LINE_SIZE] ) +{ + int retVal = 0; + char *membuf = NULL; + char aliasStr[LINE_SIZE]; + char *temp_str = NULL; + FILE *fp = NULL; + unsigned fileLen; + unsigned num_read; + time_t last_modified; + struct stat file_info; + struct sockaddr_in serverAddr; + int rc = UPNP_E_SUCCESS; + + if( description == NULL ) { + return UPNP_E_INVALID_PARAM; + } + // non-URL description must have configuration specified + if( descriptionType != UPNPREG_URL_DESC && ( !config_baseURL ) ) { + return UPNP_E_INVALID_PARAM; + } + // get XML doc and last modified time + if( descriptionType == UPNPREG_URL_DESC ) { + if( ( retVal = + UpnpDownloadXmlDoc( description, + xmlDoc ) ) != UPNP_E_SUCCESS ) { + return retVal; + } + last_modified = time( NULL ); + } else if( descriptionType == UPNPREG_FILENAME_DESC ) { + retVal = stat( description, &file_info ); + if( retVal == -1 ) { + return UPNP_E_FILE_NOT_FOUND; + } + fileLen = file_info.st_size; + last_modified = file_info.st_mtime; + + if( ( fp = fopen( description, "rb" ) ) == NULL ) { + return UPNP_E_FILE_NOT_FOUND; + } + + if( ( membuf = ( char * )malloc( fileLen + 1 ) ) == NULL ) { + fclose( fp ); + return UPNP_E_OUTOF_MEMORY; + } + + num_read = fread( membuf, 1, fileLen, fp ); + if( num_read != fileLen ) { + fclose( fp ); + free( membuf ); + return UPNP_E_FILE_READ_ERROR; + } + + membuf[fileLen] = 0; + fclose( fp ); + rc = ixmlParseBufferEx( membuf, xmlDoc ); + free( membuf ); + } else if( descriptionType == UPNPREG_BUF_DESC ) { + last_modified = time( NULL ); + rc = ixmlParseBufferEx( description, xmlDoc ); + } else { + return UPNP_E_INVALID_PARAM; + } + + if( rc != IXML_SUCCESS && descriptionType != UPNPREG_URL_DESC ) { + if( rc == IXML_INSUFFICIENT_MEMORY ) { + return UPNP_E_OUTOF_MEMORY; + } else { + return UPNP_E_INVALID_DESC; + } + } + // determine alias + if( config_baseURL ) { + if( descriptionType == UPNPREG_BUF_DESC ) { + strcpy( aliasStr, "description.xml" ); + } else // URL or filename + { + retVal = GetNameForAlias( description, &temp_str ); + if( retVal != UPNP_E_SUCCESS ) { + ixmlDocument_free( *xmlDoc ); + return retVal; + } + if( strlen( temp_str ) > ( LINE_SIZE - 1 ) ) { + ixmlDocument_free( *xmlDoc ); + free( temp_str ); + return UPNP_E_URL_TOO_BIG; + } + strcpy( aliasStr, temp_str ); + } + + get_server_addr( &serverAddr ); + + // config + retVal = configure_urlbase( *xmlDoc, &serverAddr, + aliasStr, last_modified, descURL ); + if( retVal != UPNP_E_SUCCESS ) { + ixmlDocument_free( *xmlDoc ); + return retVal; + } + } else // manual + { + if( strlen( description ) > ( LINE_SIZE - 1 ) ) { + ixmlDocument_free( *xmlDoc ); + return UPNP_E_URL_TOO_BIG; + } + strcpy( descURL, description ); + } + + assert( *xmlDoc != NULL ); + + return UPNP_E_SUCCESS; +} + +#else // no web server + +/************************************************************************** + * Function: GetDescDocumentAndURL ( In the case of control point) + * + * Parameters: + * IN Upnp_DescType descriptionType: pointer to server address + * structure + * IN char* description: + * IN unsigned int bufferLen: + * IN int config_baseURL: + * OUT IXML_Document **xmlDoc: + * OUT char *descURL: + * + * Description: + * This function fills the sockadr_in with miniserver information. + * + * Return Values: VOID + * + ***************************************************************************/ +static int +GetDescDocumentAndURL( IN Upnp_DescType descriptionType, + IN char *description, + IN unsigned int bufferLen, + IN int config_baseURL, + OUT IXML_Document ** xmlDoc, + OUT char *descURL ) +{ + int retVal; + + if( ( descriptionType != UPNPREG_URL_DESC ) || config_baseURL ) { + return UPNP_E_NO_WEB_SERVER; + } + + if( description == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + if( strlen( description ) > ( LINE_SIZE - 1 ) ) { + return UPNP_E_URL_TOO_BIG; + } + strcpy( descURL, description ); + + if( ( retVal = + UpnpDownloadXmlDoc( description, xmlDoc ) ) != UPNP_E_SUCCESS ) { + return retVal; + } + + return UPNP_E_SUCCESS; +} + +#endif // INTERNAL_WEB_SERVER +// ******************************************************** + +/**************************************************************************** + * Function: UpnpRegisterRootDevice2 + * + * Parameters: + * IN Upnp_DescType descriptionType: The type of description document. + * IN const char* description: Treated as a URL, file name or + * memory buffer depending on description type. + * IN size_t bufferLen: Length of memory buffer if passing a description + * in a buffer, otherwize ignored. + * IN int config_baseURL: If nonzero, URLBase of description document is + * configured and the description is served using the internal + * web server. + * IN Upnp_FunPtr Fun: Pointer to the callback function for + * receiving asynchronous events. + * IN const void* Cookie: Pointer to user data returned with the + * callback function when invoked. + * OUT UpnpDevice_Handle* Hnd: Pointer to a variable to store + * the new device handle. + * + * Description: + * This function is similar to UpnpRegisterRootDevice except that + * it also allows the description document to be specified as a file or + * a memory buffer. The description can also be configured to have the + * correct IP and port address. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpRegisterRootDevice2( IN Upnp_DescType descriptionType, + IN const char *description_const, + IN size_t bufferLen, // ignored unless descType == UPNPREG_BUF_DESC + + IN int config_baseURL, + IN Upnp_FunPtr Fun, + IN const void *Cookie, + OUT UpnpDevice_Handle * Hnd ) +{ + struct Handle_Info *HInfo; + int retVal = 0; + char *description = ( char * )description_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Inside UpnpRegisterRootDevice2 \n" ); + ) + + if( Hnd == NULL || Fun == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + HandleLock( ); + + if( UpnpSdkDeviceRegistered ) { + HandleUnlock( ); + return UPNP_E_ALREADY_REGISTERED; + } + + if( ( *Hnd = GetFreeHandle( ) ) == UPNP_E_OUTOF_HANDLE ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + + HInfo = ( struct Handle_Info * )malloc( sizeof( struct Handle_Info ) ); + if( HInfo == NULL ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + HandleTable[*Hnd] = HInfo; + + // prevent accidental removal of a non-existent alias + HInfo->aliasInstalled = 0; + + retVal = GetDescDocumentAndURL( descriptionType, description, + bufferLen, config_baseURL, + &HInfo->DescDocument, HInfo->DescURL ); + //HInfo->DescAlias ); + + if( retVal != UPNP_E_SUCCESS ) { + FreeHandle( *Hnd ); + HandleUnlock( ); + return retVal; + } + + HInfo->aliasInstalled = ( config_baseURL != 0 ); + + HInfo->HType = HND_DEVICE; + HInfo->Callback = Fun; + HInfo->Cookie = ( void * )Cookie; + HInfo->MaxAge = DEFAULT_MAXAGE; + HInfo->DeviceList = NULL; + HInfo->ServiceList = NULL; + CLIENTONLY( HInfo->ClientSubList = NULL; + ) + CLIENTONLY( ListInit( &HInfo->SsdpSearchList, NULL, NULL ); + ); + HInfo->MaxSubscriptions = UPNP_INFINITE; + HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE; + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice2: Valid Description\n" ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice2: DescURL : %s\n", + HInfo->DescURL ); + ) + + HInfo->DeviceList = + ixmlDocument_getElementsByTagName( HInfo->DescDocument, "device" ); + + if( HInfo->DeviceList == NULL ) { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + ixmlDocument_free( HInfo->DescDocument ); + FreeHandle( *Hnd ); + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice2: No devices found for RootDevice\n" ); + ) + return UPNP_E_INVALID_DESC; + } + + HInfo->ServiceList = + ixmlDocument_getElementsByTagName( HInfo->DescDocument, + "serviceList" ); + + if( HInfo->ServiceList == NULL ) { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + ixmlNodeList_free( HInfo->DeviceList ); + ixmlDocument_free( HInfo->DescDocument ); + FreeHandle( *Hnd ); + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice2: No services found for RootDevice\n" ); + ) + return UPNP_E_INVALID_DESC; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice2: Gena Check\n" ); + ) + //******************************* + //GENA SET UP + //******************************* + if( getServiceTable( ( IXML_Node * ) HInfo->DescDocument, + &HInfo->ServiceTable, HInfo->DescURL ) ) { + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "UpnpRegisterRootDevice2: GENA Service Table \n" ); + ) + } else { + CLIENTONLY( ListDestroy( &HInfo->SsdpSearchList, 0 ) ); + FreeHandle( *Hnd ); + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "\nUpnpRegisterRootDevice: Errors retrieving service table \n" ); + ) + return UPNP_E_INVALID_DESC; + } + UpnpSdkDeviceRegistered = 1; + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting RegisterRootDevice2 Successfully\n" ); + ) + return UPNP_E_SUCCESS; + +} /****************** End of UpnpRegisterRootDevice2 *********************/ + +#endif //INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpRegisterClient + * + * Parameters: + * IN Upnp_FunPtr Fun: Pointer to a function for receiving + * asynchronous events. + * IN const void * Cookie: Pointer to user data returned with the + * callback function when invoked. + * OUT UpnpClient_Handle *Hnd: Pointer to a variable to store + * the new control point handle. + * + * Description: + * This function registers a control point application with the + * UPnP Library. A control point application cannot make any other API + * calls until it registers using this function. + * + * Return Values: int + * + ***************************************************************************/ +int +UpnpRegisterClient( IN Upnp_FunPtr Fun, + IN const void *Cookie, + OUT UpnpClient_Handle * Hnd ) +{ + struct Handle_Info *HInfo; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpRegisterClient \n" ); + ) + + if( Fun == NULL || Hnd == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + HandleLock( ); + + if( UpnpSdkClientRegistered ) { + HandleUnlock( ); + return UPNP_E_ALREADY_REGISTERED; + } + + if( ( *Hnd = GetFreeHandle( ) ) == UPNP_E_OUTOF_HANDLE ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + HInfo = ( struct Handle_Info * )malloc( sizeof( struct Handle_Info ) ); + if( HInfo == NULL ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + + HInfo->HType = HND_CLIENT; + HInfo->Callback = Fun; + HInfo->Cookie = ( void * )Cookie; + DEVICEONLY( HInfo->MaxAge = 0; + ) + HInfo->ClientSubList = NULL; + ListInit( &HInfo->SsdpSearchList, NULL, NULL ); + DEVICEONLY( HInfo->MaxSubscriptions = UPNP_INFINITE; + ) + DEVICEONLY( HInfo->MaxSubscriptionTimeOut = UPNP_INFINITE; + ) + + HandleTable[*Hnd] = HInfo; + + UpnpSdkClientRegistered = 1; + + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpRegisterClient \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpRegisterClient *********************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/**************************************************************************** + * Function: UpnpUnRegisterClient + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point instance + * to unregister + * Description: + * This function unregisters a client registered with + * UpnpRegisterclient or UpnpRegisterclient2. After this call, the + * UpnpDevice_Handle Hnd is no longer valid. The UPnP Library generates + * no more callbacks after this function returns. + * + * Return Values: + * UPNP_E_SUCCESS on success, nonzero on failure. + *****************************************************************************/ +int +UpnpUnRegisterClient( IN UpnpClient_Handle Hnd ) +{ + struct Handle_Info *HInfo; + ListNode *node = NULL; + SsdpSearchArg *searchArg = NULL; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpUnRegisterClient \n" ); + ) + HandleLock( ); + if( !UpnpSdkClientRegistered ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + +#if EXCLUDE_GENA == 0 + if( genaUnregisterClient( Hnd ) != UPNP_E_SUCCESS ) + return UPNP_E_INVALID_HANDLE; +#endif + HandleLock( ); + if( GetHandleInfo( Hnd, &HInfo ) == UPNP_E_INVALID_HANDLE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + //clean up search list + node = ListHead( &HInfo->SsdpSearchList ); + while( node != NULL ) { + searchArg = ( SsdpSearchArg * ) node->item; + if( searchArg ) { + free( searchArg->searchTarget ); + free( searchArg ); + } + ListDelNode( &HInfo->SsdpSearchList, node, 0 ); + node = ListHead( &HInfo->SsdpSearchList ); + } + + ListDestroy( &HInfo->SsdpSearchList, 0 ); + FreeHandle( Hnd ); + UpnpSdkClientRegistered = 0; + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpUnRegisterClient \n" ); + ) + return UPNP_E_SUCCESS; + +} /****************** End of UpnpUnRegisterClient *********************/ +#endif // INCLUDE_CLIENT_APIS + +//----------------------------------------------------------------------------- +// +// SSDP interface +// +//----------------------------------------------------------------------------- + +#ifdef INCLUDE_DEVICE_APIS +#if EXCLUDE_SSDP == 0 + +/************************************************************************** + * Function: UpnpSendAdvertisement + * + * Parameters: + * IN UpnpDevice_Handle Hnd: handle of the device instance + * IN int Exp : Timer for resending the advertisement + * + * Description: + * This function sends the device advertisement. It also schedules a + * job for the next advertisement after "Exp" time. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSendAdvertisement( IN UpnpDevice_Handle Hnd, + IN int Exp ) +{ + struct Handle_Info *SInfo = NULL; + int retVal = 0, + *ptrMx; + upnp_timeout *adEvent; + ThreadPoolJob job; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSendAdvertisement \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( Exp < 1 ) + Exp = DEFAULT_MAXAGE; + SInfo->MaxAge = Exp; + HandleUnlock( ); + retVal = AdvertiseAndReply( 1, Hnd, 0, ( struct sockaddr_in * )NULL, + ( char * )NULL, ( char * )NULL, + ( char * )NULL, Exp ); + + if( retVal != UPNP_E_SUCCESS ) + return retVal; + ptrMx = ( int * )malloc( sizeof( int ) ); + if( ptrMx == NULL ) + return UPNP_E_OUTOF_MEMORY; + adEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) ); + + if( adEvent == NULL ) { + free( ptrMx ); + return UPNP_E_OUTOF_MEMORY; + } + *ptrMx = Exp; + adEvent->handle = Hnd; + adEvent->Event = ptrMx; + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + free( adEvent ); + free( ptrMx ); + return UPNP_E_INVALID_HANDLE; + } +#ifdef SSDP_PACKET_DISTRIBUTE + TPJobInit( &job, ( start_routine ) AutoAdvertise, adEvent ); + TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout ); + TPJobSetPriority( &job, MED_PRIORITY ); + if( ( retVal = TimerThreadSchedule( &gTimerThread, + ( ( Exp / 2 ) - + ( AUTO_ADVERTISEMENT_TIME ) ), + REL_SEC, &job, SHORT_TERM, + &( adEvent->eventId ) ) ) + != UPNP_E_SUCCESS ) { + HandleUnlock( ); + free( adEvent ); + free( ptrMx ); + return retVal; + } +#else + TPJobInit( &job, ( start_routine ) AutoAdvertise, adEvent ); + TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout ); + TPJobSetPriority( &job, MED_PRIORITY ); + if( ( retVal = TimerThreadSchedule( &gTimerThread, + Exp - AUTO_ADVERTISEMENT_TIME, + REL_SEC, &job, SHORT_TERM, + &( adEvent->eventId ) ) ) + != UPNP_E_SUCCESS ) { + HandleUnlock( ); + free( adEvent ); + free( ptrMx ); + return retVal; + } +#endif + + HandleUnlock( ); + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSendAdvertisement \n" ); ) + + return retVal; + +} /****************** End of UpnpSendAdvertisement *********************/ +#endif // INCLUDE_DEVICE_APIS +#endif +#if EXCLUDE_SSDP == 0 +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpSendAdvertisement + * + * Parameters: + * IN UpnpClient_Handle Hnd: handle of the control point instance + * IN int Mx : Maximum time to wait for the search reply + * IN const char *Target_const: + * IN const void *Cookie_const: + * + * Description: + * This function searches for the devices for the provided maximum time. + * It is a asynchronous function. It schedules a search job and returns. + * client is notified about the search results after search timer. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSearchAsync( IN UpnpClient_Handle Hnd, + IN int Mx, + IN const char *Target_const, + IN const void *Cookie_const ) +{ + struct Handle_Info *SInfo = NULL; + char *Target = ( char * )Target_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSearchAsync \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( Mx < 1 ) + Mx = DEFAULT_MX; + + if( Target == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + HandleUnlock( ); + SearchByTarget( Mx, Target, ( void * )Cookie_const ); + + //HandleUnlock(); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSearchAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpSearchAsync *********************/ +#endif // INCLUDE_CLIENT_APIS +#endif +//----------------------------------------------------------------------------- +// +// GENA interface +// +//----------------------------------------------------------------------------- + +#if EXCLUDE_GENA == 0 +#ifdef INCLUDE_DEVICE_APIS + +/************************************************************************** + * Function: UpnpSetMaxSubscriptions + * + * Parameters: + * IN UpnpDevice_Handle Hnd: The handle of the device for which + * the maximum subscriptions is being set. + * IN int MaxSubscriptions: The maximum number of subscriptions to be + * allowed per service. + * + * Description: + * This function sets the maximum subscriptions of the control points + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSetMaxSubscriptions( IN UpnpDevice_Handle Hnd, + IN int MaxSubscriptions ) +{ + struct Handle_Info *SInfo = NULL; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSetMaxSubscriptions \n" ); + ) + + HandleLock( ); + if( ( ( MaxSubscriptions != UPNP_INFINITE ) + && ( MaxSubscriptions < 0 ) ) + || ( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + SInfo->MaxSubscriptions = MaxSubscriptions; + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSetMaxSubscriptions \n" ); + ) + + return UPNP_E_SUCCESS; + +} /***************** End of UpnpSetMaxSubscriptions ********************/ +#endif // INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_DEVICE_APIS + +/************************************************************************** + * Function: UpnpSetMaxSubscriptionTimeOut + * + * Parameters: + * IN UpnpDevice_Handle Hnd: The handle of the device for which the + * maximum subscription time-out is being set. + * IN int MaxSubscriptionTimeOut:The maximum subscription time-out + * to be accepted + * + * Description: + * This function sets the maximum subscription timer. Control points + * will require to send the subscription request before timeout. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSetMaxSubscriptionTimeOut( IN UpnpDevice_Handle Hnd, + IN int MaxSubscriptionTimeOut ) +{ + struct Handle_Info *SInfo = NULL; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSetMaxSubscriptionTimeOut \n" ); + ) + + HandleLock( ); + + if( ( ( MaxSubscriptionTimeOut != UPNP_INFINITE ) + && ( MaxSubscriptionTimeOut < 0 ) ) + || ( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + + SInfo->MaxSubscriptionTimeOut = MaxSubscriptionTimeOut; + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSetMaxSubscriptionTimeOut \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpSetMaxSubscriptionTimeOut ******************/ +#endif // INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpSubscribeAsync + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point for which + * the subscription request is to be sent. + * IN const char * EvtUrl_const: URL that control point wants to + * subscribe + * IN int TimeOut: The requested subscription time. Upon + * return, it contains the actual subscription time + * returned from the service + * IN Upnp_FunPtr Fun : callback function to tell result of the + * subscription request + * IN const void * Cookie_const: cookie passed by client to give back + * in the callback function. + * + * Description: + * This function performs the same operation as UpnpSubscribeAsync + * but returns immediately and calls the registered callback function + * when the operation is complete. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSubscribeAsync( IN UpnpClient_Handle Hnd, + IN const char *EvtUrl_const, + IN int TimeOut, + IN Upnp_FunPtr Fun, + IN const void *Cookie_const ) +{ + struct Handle_Info *SInfo = NULL; + struct UpnpNonblockParam *Param; + char *EvtUrl = ( char * )EvtUrl_const; + ThreadPoolJob job; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSubscribeAsync \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( EvtUrl == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( TimeOut != UPNP_INFINITE && TimeOut < 1 ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( Fun == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + Param = + ( struct UpnpNonblockParam * ) + malloc( sizeof( struct UpnpNonblockParam ) ); + if( Param == NULL ) { + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + HandleUnlock( ); + + Param->FunName = SUBSCRIBE; + Param->Handle = Hnd; + strcpy( Param->Url, EvtUrl ); + Param->TimeOut = TimeOut; + Param->Fun = Fun; + Param->Cookie = ( void * )Cookie_const; + + TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + TPJobSetPriority( &job, MED_PRIORITY ); + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSubscribeAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpSubscribeAsync *********************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpSubscribe + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point. + * IN const char *PublisherUrl: The URL of the service to subscribe to. + * INOUT int *TimeOut: Pointer to a variable containing the requested + * subscription time. Upon return, it contains the + * actual subscription time returned from the service. + * OUT Upnp_SID SubsId: Pointer to a variable to receive the + * subscription ID (SID). + * + * Description: + * This function registers a control point to receive event + * notifications from another device. This operation is synchronous + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSubscribe( IN UpnpClient_Handle Hnd, + IN const char *EvtUrl_const, + INOUT int *TimeOut, + OUT Upnp_SID SubsId ) +{ + struct Handle_Info *SInfo = NULL; + int RetVal; + char *EvtUrl = ( char * )EvtUrl_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSubscribe \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( EvtUrl == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( TimeOut == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + HandleUnlock( ); + RetVal = genaSubscribe( Hnd, EvtUrl, TimeOut, SubsId ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSubscribe \n" ); + ) + + return RetVal; + +} /****************** End of UpnpSubscribe *********************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpUnSubscribe + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point. + * IN Upnp_SID SubsId: The ID returned when the control point + * subscribed to the service. + * + * Description: + * This function removes the subscription of a control point from a + * service previously subscribed to using UpnpSubscribe or + * UpnpSubscribeAsync. This is a synchronous call. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpUnSubscribe( IN UpnpClient_Handle Hnd, + IN Upnp_SID SubsId ) +{ + struct Handle_Info *SInfo = NULL; + int RetVal; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpUnSubscribe \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + HandleUnlock( ); + RetVal = genaUnSubscribe( Hnd, SubsId ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpUnSubscribe \n" ); + ) + + return RetVal; + +} /****************** End of UpnpUnSubscribe *********************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpUnSubscribeAsync + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the subscribed control point. + * IN Upnp_SID SubsId: The ID returned when the control point + * subscribed to the service. + * IN Upnp_FunPtr Fun: Pointer to a callback function to be called + * when the operation is complete. + * IN const void *Cookie:Pointer to user data to pass to the + * callback function when invoked. + * + * Description: + * This function removes a subscription of a control point + * from a service previously subscribed to using UpnpSubscribe or + * UpnpSubscribeAsync,generating a callback when the operation is complete. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpUnSubscribeAsync( IN UpnpClient_Handle Hnd, + IN Upnp_SID SubsId, + IN Upnp_FunPtr Fun, + IN const void *Cookie_const ) +{ + ThreadPoolJob job; + struct Handle_Info *SInfo = NULL; + struct UpnpNonblockParam *Param; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpUnSubscribeAsync \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( Fun == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + HandleUnlock( ); + Param = + ( struct UpnpNonblockParam * ) + malloc( sizeof( struct UpnpNonblockParam ) ); + if( Param == NULL ) + return UPNP_E_OUTOF_MEMORY; + + Param->FunName = UNSUBSCRIBE; + Param->Handle = Hnd; + strcpy( Param->SubsId, SubsId ); + Param->Fun = Fun; + Param->Cookie = ( void * )Cookie_const; + TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + TPJobSetPriority( &job, MED_PRIORITY ); + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpUnSubscribeAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpUnSubscribeAsync *********************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpRenewSubscription + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point that + * is renewing the subscription. + * INOUT int *TimeOut: Pointer to a variable containing the + * requested subscription time. Upon return, + * it contains the actual renewal time. + * IN Upnp_SID SubsId: The ID for the subscription to renew. + * + * Description: + * This function renews a subscription that is about to + * expire. This function is synchronous. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpRenewSubscription( IN UpnpClient_Handle Hnd, + INOUT int *TimeOut, + IN Upnp_SID SubsId ) +{ + struct Handle_Info *SInfo = NULL; + int RetVal; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpRenewSubscription \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( TimeOut == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + HandleUnlock( ); + RetVal = genaRenewSubscription( Hnd, SubsId, TimeOut ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpRenewSubscription \n" ); + ) + + return RetVal; + +} /****************** End of UpnpRenewSubscription *********************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpRenewSubscriptionAsync + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point that + * is renewing the subscription. + * IN int TimeOut: The requested subscription time. The + * actual timeout value is returned when + * the callback function is called. + * IN Upnp_SID SubsId: The ID for the subscription to renew. + * IN Upnp_FunPtr Fun: Pointer to a callback function to be + * invoked when the renewal is complete. + * IN const void *Cookie : Pointer to user data passed + * to the callback function when invoked. + * + * Description: + * This function renews a subscription that is about + * to expire, generating a callback when the operation is complete. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpRenewSubscriptionAsync( IN UpnpClient_Handle Hnd, + INOUT int TimeOut, + IN Upnp_SID SubsId, + IN Upnp_FunPtr Fun, + IN const void *Cookie_const ) +{ + ThreadPoolJob job; + struct Handle_Info *SInfo = NULL; + struct UpnpNonblockParam *Param; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpRenewSubscriptionAsync \n" ); + ) + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( TimeOut != UPNP_INFINITE && TimeOut < 1 ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( Fun == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + HandleUnlock( ); + + Param = + ( struct UpnpNonblockParam * ) + malloc( sizeof( struct UpnpNonblockParam ) ); + if( Param == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + Param->FunName = RENEW; + Param->Handle = Hnd; + strcpy( Param->SubsId, SubsId ); + Param->Fun = Fun; + Param->Cookie = ( void * )Cookie_const; + Param->TimeOut = TimeOut; + + TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + TPJobSetPriority( &job, MED_PRIORITY ); + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpRenewSubscriptionAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpRenewSubscriptionAsync *******************/ +#endif // INCLUDE_CLIENT_APIS + +#ifdef INCLUDE_DEVICE_APIS + +/************************************************************************** + * Function: UpnpNotify + * + * Parameters: + * IN UpnpDevice_Handle: The handle to the device sending the event. + * IN const char *DevID: The device ID of the subdevice of the + * service generating the event. + * IN const char *ServID: The unique identifier of the service + * generating the event. + * IN const char **VarName: Pointer to an array of variables that + * have changed. + * IN const char **NewVal: Pointer to an array of new values for + * those variables. + * IN int cVariables: The count of variables included in this + * notification. + * + * Description: + * This function sends out an event change notification to all + * control points subscribed to a particular service. This function is + * synchronous and generates no callbacks. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpNotify( IN UpnpDevice_Handle Hnd, + IN const char *DevID_const, + IN const char *ServName_const, + IN const char **VarName_const, + IN const char **NewVal_const, + IN int cVariables ) +{ + + struct Handle_Info *SInfo = NULL; + int retVal; + char *DevID = ( char * )DevID_const; + char *ServName = ( char * )ServName_const; + char **VarName = ( char ** )VarName_const; + char **NewVal = ( char ** )NewVal_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpNotify \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( DevID == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( ServName == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( VarName == NULL || NewVal == NULL || cVariables < 0 ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + HandleUnlock( ); + retVal = + genaNotifyAll( Hnd, DevID, ServName, VarName, NewVal, cVariables ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpNotify \n" ); + ) + + return retVal; + +} /****************** End of UpnpNotify *********************/ + +/************************************************************************** + * Function: UpnpNotifyExt + * + * Parameters: + * IN UpnpDevice_Handle: The handle to the device sending the + * event. + * IN const char *DevID: The device ID of the subdevice of the + * service generating the event. + * IN const char *ServID: The unique identifier of the service + * generating the event. + * IN IXML_Document *PropSet: The DOM document for the property set. + * Property set documents must conform to the XML schema + * defined in section 4.3 of the Universal Plug and Play + * Device Architecture specification. + * + * Description: + * This function is similar to UpnpNotify except that it takes + * a DOM document for the event rather than an array of strings. This + * function is synchronous and generates no callbacks. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpNotifyExt( IN UpnpDevice_Handle Hnd, + IN const char *DevID_const, + IN const char *ServName_const, + IN IXML_Document * PropSet ) +{ + + struct Handle_Info *SInfo = NULL; + int retVal; + char *DevID = ( char * )DevID_const; + char *ServName = ( char * )ServName_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpNotify \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( DevID == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( ServName == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + HandleUnlock( ); + retVal = genaNotifyAllExt( Hnd, DevID, ServName, PropSet ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpNotify \n" ); + ) + + return retVal; + +} /****************** End of UpnpNotify *********************/ + +#endif // INCLUDE_DEVICE_APIS + +#ifdef INCLUDE_DEVICE_APIS + +/************************************************************************** + * Function: UpnpAcceptSubscription + * + * Parameters: + * IN UpnpDevice_Handle Hnd: The handle of the device. + * IN const char *DevID: The device ID of the subdevice of the + * service generating the event. + * IN const char *ServID: The unique service identifier of the + * service generating the event. + * IN const char **VarName: Pointer to an array of event variables. + * IN const char **NewVal: Pointer to an array of values for + * the event variables. + * IN int cVariables: The number of event variables in VarName. + * IN Upnp_SID SubsId: The subscription ID of the newly + * registered control point. + * + * Description: + * This function accepts a subscription request and sends + * out the current state of the eventable variables for a service. + * The device application should call this function when it receives a + * UPNP_EVENT_SUBSCRIPTION_REQUEST callback. This function is sychronous + * and generates no callbacks. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpAcceptSubscription( IN UpnpDevice_Handle Hnd, + IN const char *DevID_const, + IN const char *ServName_const, + IN const char **VarName_const, + IN const char **NewVal_const, + int cVariables, + IN Upnp_SID SubsId ) +{ + struct Handle_Info *SInfo = NULL; + int retVal; + char *DevID = ( char * )DevID_const; + char *ServName = ( char * )ServName_const; + char **VarName = ( char ** )VarName_const; + char **NewVal = ( char ** )NewVal_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpAcceptSubscription \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( DevID == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( ServName == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( VarName == NULL || NewVal == NULL || cVariables < 0 ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + HandleUnlock( ); + retVal = + genaInitNotify( Hnd, DevID, ServName, VarName, NewVal, cVariables, + SubsId ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpAcceptSubscription \n" ); + ) + return retVal; + +} /***************** End of UpnpAcceptSubscription *********************/ + +/************************************************************************** + * Function: UpnpAcceptSubscriptionExt + * + * Parameters: + * IN UpnpDevice_Handle Hnd: The handle of the device. + * IN const char *DevID: The device ID of the subdevice of the + * service generating the event. + * IN const char *ServID: The unique service identifier of the service + * generating the event. + * IN IXML_Document *PropSet: The DOM document for the property set. + * Property set documents must conform to the XML schema + * defined in section 4.3 of the Universal Plug and Play + * Device Architecture specification. + * IN Upnp_SID SubsId: The subscription ID of the newly + * registered control point. + * + * Description: + * This function is similar to UpnpAcceptSubscription except that it + * takes a DOM document for the variables to event rather than an array + * of strings. This function is sychronous and generates no callbacks. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpAcceptSubscriptionExt( IN UpnpDevice_Handle Hnd, + IN const char *DevID_const, + IN const char *ServName_const, + IN IXML_Document * PropSet, + IN Upnp_SID SubsId ) +{ + struct Handle_Info *SInfo = NULL; + int retVal; + char *DevID = ( char * )DevID_const; + char *ServName = ( char * )ServName_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpAcceptSubscription \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + if( DevID == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( ServName == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + if( SubsId == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + if( PropSet == NULL ) { + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + HandleUnlock( ); + retVal = genaInitNotifyExt( Hnd, DevID, ServName, PropSet, SubsId ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpAcceptSubscription \n" ); + ) + + return retVal; + +} /****************** End of UpnpAcceptSubscription *********************/ + +#endif // INCLUDE_DEVICE_APIS +#endif // EXCLUDE_GENA == 0 + +//--------------------------------------------------------------------------- +// +// SOAP interface +// +//--------------------------------------------------------------------------- +#if EXCLUDE_SOAP == 0 +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpSendAction + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point + * sending the action. + * IN const char *ActionURL: The action URL of the service. + * IN const char *ServiceType: The type of the service. + * IN const char *DevUDN: This parameter is ignored. + * IN IXML_Document *Action: The DOM document for the action. + * OUT IXML_Document **RespNode: The DOM document for the response + * to the action. The UPnP Library allocates this document + * and the caller needs to free it. + * + * Description: + * This function sends a message to change a state variable in a service. + * This is a synchronous call that does not return until the action is + * complete. + * + * Note that a positive return value indicates a SOAP-protocol error code. + * In this case, the error description can be retrieved from RespNode. + * A negative return value indicates a UPnP Library error. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSendAction( IN UpnpClient_Handle Hnd, + IN const char *ActionURL_const, + IN const char *ServiceType_const, + IN const char *DevUDN_const, + IN IXML_Document * Action, + OUT IXML_Document ** RespNodePtr ) +{ + struct Handle_Info *SInfo = NULL; + int retVal = 0; + char *ActionURL = ( char * )ActionURL_const; + char *ServiceType = ( char * )ServiceType_const; + + //char *DevUDN = (char *)DevUDN_const; // udn not used? + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSendAction \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + + if( ActionURL == NULL ) { + return UPNP_E_INVALID_PARAM; + } + if( ServiceType == NULL || Action == NULL || RespNodePtr == NULL + || DevUDN_const != NULL ) { + return UPNP_E_INVALID_PARAM; + } + + retVal = SoapSendAction( ActionURL, ServiceType, Action, RespNodePtr ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSendAction \n" ); + ) + + return retVal; + +} /****************** End of UpnpSendAction *********************/ + +/************************************************************************** + * Function: UpnpSendActionEx + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point sending + * the action. + * IN const char *ActionURL_const: The action URL of the service. + * IN const char *ServiceType_const: The type of the service. + * IN const char *DevUDN_const: This parameter is ignored. + * IN IXML_Document *Header: The DOM document for the SOAP header. + * This may be NULL if the header is not required. + * IN IXML_Document *Action: The DOM document for the action. + * OUT IXML_Document **RespNodePtr: The DOM document for the response to + * the action. The UPnP library allocates this document and the + * caller needs to free it. + * + * Description: + * this function sends a message to change a state variable in a + * service. This is a synchronous call that does not return until the + * action is complete. + * + * Note that a positive return value indicates a SOAP-protocol error code. + * In this case, the error description can be retrieved from {\bf RespNode}. + * A negative return value indicates a UPnP Library error. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSendActionEx( IN UpnpClient_Handle Hnd, + IN const char *ActionURL_const, + IN const char *ServiceType_const, + IN const char *DevUDN_const, + IN IXML_Document * Header, + IN IXML_Document * Action, + OUT IXML_Document ** RespNodePtr ) +{ + + struct Handle_Info *SInfo = NULL; + int retVal = 0; + char *ActionURL = ( char * )ActionURL_const; + char *ServiceType = ( char * )ServiceType_const; + + //char *DevUDN = (char *)DevUDN_const; // udn not used? + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSendActionEx \n" ); + ) + + if( Header == NULL ) { + retVal = UpnpSendAction( Hnd, ActionURL_const, ServiceType_const, + DevUDN_const, Action, RespNodePtr ); + return retVal; + } + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + + if( ActionURL == NULL ) { + return UPNP_E_INVALID_PARAM; + } + if( ServiceType == NULL || Action == NULL || RespNodePtr == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + retVal = SoapSendActionEx( ActionURL, ServiceType, Header, + Action, RespNodePtr ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSendAction \n" ); + ) + + return retVal; + +} /****************** End of UpnpSendActionEx *********************/ + +/************************************************************************** + * Function: UpnpSendActionAsync + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point + * sending the action. + * IN const char *ActionURL: The action URL of the service. + * IN const char *ServiceType: The type of the service. + * IN const char *DevUDN: This parameter is ignored. + * IN IXML_Document *Action: The DOM document for the action to + * perform on this device. + * IN Upnp_FunPtr Fun: Pointer to a callback function to + * be invoked when the operation completes + * IN const void *Cookie: Pointer to user data that to be + * passed to the callback when invoked. + * + * Description: + * this function sends a message to change a state variable + * in a service, generating a callback when the operation is complete. + * See UpnpSendAction for comments on positive return values. These + * positive return values are sent in the event struct associated with the + * UPNP_CONTROL_ACTION_COMPLETE event. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSendActionAsync( IN UpnpClient_Handle Hnd, + IN const char *ActionURL_const, + IN const char *ServiceType_const, + IN const char *DevUDN_const, + IN IXML_Document * Act, + IN Upnp_FunPtr Fun, + IN const void *Cookie_const ) +{ + ThreadPoolJob job; + struct Handle_Info *SInfo = NULL; + struct UpnpNonblockParam *Param; + DOMString tmpStr; + char *ActionURL = ( char * )ActionURL_const; + char *ServiceType = ( char * )ServiceType_const; + + //char *DevUDN = (char *)DevUDN_const; + int rc; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSendActionAsync \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + + if( ActionURL == NULL ) { + return UPNP_E_INVALID_PARAM; + } + if( ServiceType == NULL || + Act == NULL || Fun == NULL || DevUDN_const != NULL ) { + return UPNP_E_INVALID_PARAM; + } + tmpStr = ixmlPrintNode( ( IXML_Node * ) Act ); + if( tmpStr == NULL ) { + return UPNP_E_INVALID_ACTION; + } + + Param = + ( struct UpnpNonblockParam * ) + malloc( sizeof( struct UpnpNonblockParam ) ); + + if( Param == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + Param->FunName = ACTION; + Param->Handle = Hnd; + strcpy( Param->Url, ActionURL ); + strcpy( Param->ServiceType, ServiceType ); + + rc = ixmlParseBufferEx( tmpStr, &( Param->Act ) ); + if( rc != IXML_SUCCESS ) { + free( Param ); + ixmlFreeDOMString( tmpStr ); + if( rc == IXML_INSUFFICIENT_MEMORY ) { + return UPNP_E_OUTOF_MEMORY; + } else { + return UPNP_E_INVALID_ACTION; + } + } + ixmlFreeDOMString( tmpStr ); + Param->Cookie = ( void * )Cookie_const; + Param->Fun = Fun; + + TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + + TPJobSetPriority( &job, MED_PRIORITY ); + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSendActionAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpSendActionAsync *********************/ + +/************************************************************************* + * Function: UpnpSendActionExAsync + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point + * sending the action. + * IN const char *ActionURL_const: The action URL of the service. + * IN const char *ServiceType_const: The type of the service. + * IN const char *DevUDN_const: This parameter is ignored. + * IN IXML_Document *Header: The DOM document for the SOAP header. + * This may be NULL if the header is not required. + * IN IXML_Document *Act: The DOM document for the action to + * perform on this device. + * IN Upnp_FunPtr Fun: Pointer to a callback function to be invoked + * when the operation completes. + * IN const void *Cookie_const: Pointer to user data that to be + * passed to the callback when invoked. + * + * Description: + * this function sends sends a message to change a state variable + * in a service, generating a callback when the operation is complete. + * See UpnpSendAction for comments on positive return values. These + * positive return values are sent in the event struct associated with + * the UPNP_CONTROL_ACTION_COMPLETE event. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpSendActionExAsync( IN UpnpClient_Handle Hnd, + IN const char *ActionURL_const, + IN const char *ServiceType_const, + IN const char *DevUDN_const, + IN IXML_Document * Header, + IN IXML_Document * Act, + IN Upnp_FunPtr Fun, + IN const void *Cookie_const ) +{ + struct Handle_Info *SInfo = NULL; + struct UpnpNonblockParam *Param; + DOMString tmpStr; + DOMString headerStr = NULL; + char *ActionURL = ( char * )ActionURL_const; + char *ServiceType = ( char * )ServiceType_const; + ThreadPoolJob job; + int retVal = 0; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpSendActionExAsync \n" ); + ) + + if( Header == NULL ) { + retVal = UpnpSendActionAsync( Hnd, ActionURL_const, + ServiceType_const, DevUDN_const, Act, + Fun, Cookie_const ); + return retVal; + } + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + + if( ActionURL == NULL ) { + return UPNP_E_INVALID_PARAM; + } + if( ServiceType == NULL || Act == NULL || Fun == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + headerStr = ixmlPrintNode( ( IXML_Node * ) Header ); + + tmpStr = ixmlPrintNode( ( IXML_Node * ) Act ); + if( tmpStr == NULL ) { + return UPNP_E_INVALID_ACTION; + } + + Param = + ( struct UpnpNonblockParam * ) + malloc( sizeof( struct UpnpNonblockParam ) ); + if( Param == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + Param->FunName = ACTION; + Param->Handle = Hnd; + strcpy( Param->Url, ActionURL ); + strcpy( Param->ServiceType, ServiceType ); + retVal = ixmlParseBufferEx( headerStr, &( Param->Header ) ); + if( retVal != IXML_SUCCESS ) { + ixmlFreeDOMString( tmpStr ); + ixmlFreeDOMString( headerStr ); + if( retVal == IXML_INSUFFICIENT_MEMORY ) { + return UPNP_E_OUTOF_MEMORY; + } else { + return UPNP_E_INVALID_ACTION; + } + } + + retVal = ixmlParseBufferEx( tmpStr, &( Param->Act ) ); + if( retVal != IXML_SUCCESS ) { + ixmlFreeDOMString( tmpStr ); + ixmlFreeDOMString( headerStr ); + ixmlDocument_free( Param->Header ); + if( retVal == IXML_INSUFFICIENT_MEMORY ) { + return UPNP_E_OUTOF_MEMORY; + } else { + return UPNP_E_INVALID_ACTION; + } + + } + + ixmlFreeDOMString( tmpStr ); + ixmlFreeDOMString( headerStr ); + + Param->Cookie = ( void * )Cookie_const; + Param->Fun = Fun; + + TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + + TPJobSetPriority( &job, MED_PRIORITY ); + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpSendActionAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpSendActionExAsync *********************/ + +/************************************************************************* + * Function: UpnpGetServiceVarStatusAsync + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point. + * IN const char *ActionURL: The URL of the service. + * IN const char *VarName: The name of the variable to query. + * IN Upnp_FunPtr Fun: Pointer to a callback function to + * be invoked when the operation is complete. + * IN const void *Cookie: Pointer to user data to pass to the + * callback function when invoked. + * + * Description: + * this function queries the state of a variable of a + * service, generating a callback when the operation is complete. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpGetServiceVarStatusAsync( IN UpnpClient_Handle Hnd, + IN const char *ActionURL_const, + IN const char *VarName_const, + IN Upnp_FunPtr Fun, + IN const void *Cookie_const ) +{ + ThreadPoolJob job; + struct Handle_Info *SInfo = NULL; + struct UpnpNonblockParam *Param; + char *ActionURL = ( char * )ActionURL_const; + char *VarName = ( char * )VarName_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpGetServiceVarStatusAsync \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + HandleUnlock( ); + + if( ActionURL == NULL ) { + return UPNP_E_INVALID_PARAM; + } + if( VarName == NULL || Fun == NULL ) + return UPNP_E_INVALID_PARAM; + + Param = + ( struct UpnpNonblockParam * ) + malloc( sizeof( struct UpnpNonblockParam ) ); + if( Param == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + Param->FunName = STATUS; + Param->Handle = Hnd; + strcpy( Param->Url, ActionURL ); + strcpy( Param->VarName, VarName ); + Param->Fun = Fun; + Param->Cookie = ( void * )Cookie_const; + + TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + + TPJobSetPriority( &job, MED_PRIORITY ); + + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpGetServiceVarStatusAsync \n" ); + ) + + return UPNP_E_SUCCESS; + +} /****************** End of UpnpGetServiceVarStatusAsync ****************/ + +/************************************************************************** + * Function: UpnpGetServiceVarStatus + * + * Parameters: + * IN UpnpClient_Handle Hnd: The handle of the control point. + * IN const char *ActionURL: The URL of the service. + * IN const char *VarName: The name of the variable to query. + * OUT DOMString *StVarVal: The pointer to store the value + * for VarName. The UPnP Library allocates this string and + * the caller needs to free it. + * + * Description: + * this function queries the state of a state variable of a service on + * another device. This is a synchronous call. A positive return value + * indicates a SOAP error code, whereas a negative return code indicates + * a UPnP SDK error code. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpGetServiceVarStatus( IN UpnpClient_Handle Hnd, + IN const char *ActionURL_const, + IN const char *VarName_const, + OUT DOMString * StVar ) +{ + struct Handle_Info *SInfo = NULL; + int retVal = 0; + char *StVarPtr; + char *ActionURL = ( char * )ActionURL_const; + char *VarName = ( char * )VarName_const; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpGetServiceVarStatus \n" ); + ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_CLIENT ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + + HandleUnlock( ); + + if( ActionURL == NULL ) { + return UPNP_E_INVALID_PARAM; + } + if( VarName == NULL || StVar == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + retVal = SoapGetServiceVarStatus( ActionURL, VarName, &StVarPtr ); + *StVar = StVarPtr; + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpGetServiceVarStatus \n" ); + ) + + return retVal; + +} /****************** End of UpnpGetServiceVarStatus *********************/ +#endif // INCLUDE_CLIENT_APIS +#endif // EXCLUDE_SOAP + +//--------------------------------------------------------------------------- +// +// Client API's +// +//--------------------------------------------------------------------------- + +/************************************************************************** + * Function: UpnpOpenHttpPost + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ + +int +UpnpOpenHttpPost( IN const char *url, + IN OUT void **handle, + IN const char *contentType, + IN int contentLength, + IN int timeout ) +{ + return http_OpenHttpPost( url, handle, contentType, contentLength, + timeout ); +} + +/************************************************************************** + * Function: UpnpWriteHttpPost + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpWriteHttpPost( IN void *handle, + IN char *buf, + IN unsigned int *size, + IN int timeout ) +{ + return http_WriteHttpPost( handle, buf, size, timeout ); +} + +/************************************************************************** + * Function: UpnpCloseHttpPost + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpCloseHttpPost( IN void *handle, + IN OUT int *httpStatus, + int timeout ) +{ + return http_CloseHttpPost( handle, httpStatus, timeout ); +} + +/************************************************************************** + * Function: UpnpOpenHttpGet + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpOpenHttpGet( IN const char *url_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int timeout ) +{ + return http_OpenHttpGet( url_str, Handle, contentType, contentLength, + httpStatus, timeout ); +} + + + +/************************************************************************** + * Function: UpnpOpenHttpGetProxy + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpOpenHttpGetProxy( IN const char *url_str, + IN const char *proxy_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int timeout ) +{ + return http_OpenHttpGetProxy( url_str, proxy_str, Handle, contentType, contentLength, + httpStatus, timeout ); +} + +/************************************************************************** + * Function: UpnpOpenHttpGetEx + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpOpenHttpGetEx( IN const char *url_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int lowRange, + IN int highRange, + IN int timeout ) +{ + return http_OpenHttpGetEx( url_str, + Handle, + contentType, + contentLength, + httpStatus, lowRange, highRange, timeout ); +} + + + +/************************************************************************** + * Function: UpnpCancelHttpGet + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpCancelHttpGet( IN void *Handle ) +{ + return http_CancelHttpGet( Handle ); +} + +/************************************************************************** + * Function: UpnpCloseHttpGet + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpCloseHttpGet( IN void *Handle ) +{ + return http_CloseHttpGet( Handle ); +} + +/************************************************************************** + * Function: UpnpReadHttpGet + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpReadHttpGet( IN void *Handle, + IN OUT char *buf, + IN OUT unsigned int *size, + IN int timeout ) +{ + return http_ReadHttpGet( Handle, buf, size, timeout ); +} + + + +/************************************************************************** + * Function: UpnpHttpGetProgress + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful. + * UPNP_E_INVALID_PARAM if the provided pointers were invalid. + ***************************************************************************/ +int +UpnpHttpGetProgress( IN void *Handle, + OUT unsigned int *length, + OUT unsigned int *total ) +{ + return http_HttpGetProgress(Handle, length, total); +} + +/************************************************************************** + * Function: UpnpDownloadUrlItem + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpDownloadUrlItem( const char *url, + char **outBuf, + char *contentType ) +{ + int ret_code; + int dummy; + + if( url == NULL || outBuf == NULL || contentType == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + ret_code = http_Download( url, HTTP_DEFAULT_TIMEOUT, outBuf, &dummy, + contentType ); + if( ret_code > 0 ) { + // error reply was received + ret_code = UPNP_E_INVALID_URL; + } + + return ret_code; +} + +/************************************************************************** + * Function: UpnpDownloadXmlDoc + * + * Parameters: + * + * Description: + * + * Return Values: int + * UPNP_E_SUCCESS if successful else sends appropriate error. + ***************************************************************************/ +int +UpnpDownloadXmlDoc( const char *url, + IXML_Document ** xmlDoc ) +{ + int ret_code; + char *xml_buf; + char content_type[LINE_SIZE]; + + if( url == NULL || xmlDoc == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + ret_code = UpnpDownloadUrlItem( url, &xml_buf, content_type ); + if( ret_code != UPNP_E_SUCCESS ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "retCode: %d\n", ret_code ); + ) + return ret_code; + } + + if( strncasecmp( content_type, "text/xml", strlen( "text/xml" ) ) ) { + free( xml_buf ); + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "Not text/xml\n" ); + ) + return UPNP_E_INVALID_DESC; + } + + ret_code = ixmlParseBufferEx( xml_buf, xmlDoc ); + free( xml_buf ); + + if( ret_code != IXML_SUCCESS ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "Invalid desc\n" ); + ) + if( ret_code == IXML_INSUFFICIENT_MEMORY ) { + return UPNP_E_OUTOF_MEMORY; + } else { + return UPNP_E_INVALID_DESC; + } + } else { + DBGONLY( xml_buf = ixmlPrintNode( ( IXML_Node * ) * xmlDoc ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Printing the Parsed xml document \n %s\n", + xml_buf ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "****************** END OF Parsed XML Doc *****************\n" ); + ixmlFreeDOMString( xml_buf ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpDownloadXmlDoc\n" ); ) + + return UPNP_E_SUCCESS; + } +} + +//---------------------------------------------------------------------------- +// +// UPNP-API Internal function implementation +// +//---------------------------------------------------------------------------- + +#ifdef INCLUDE_CLIENT_APIS + +/************************************************************************** + * Function: UpnpThreadDistribution + * + * Parameters: + * + * Description: + * Function to schedule async functions in threadpool. + * + * Return Values: VOID + * + ***************************************************************************/ +void +UpnpThreadDistribution( struct UpnpNonblockParam *Param ) +{ + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpThreadDistribution \n" ); + ) + + switch ( Param->FunName ) { +#if EXCLUDE_GENA == 0 + CLIENTONLY( case SUBSCRIBE: +{ +struct Upnp_Event_Subscribe Evt; +Evt.ErrCode = genaSubscribe( Param->Handle, Param->Url, + ( int * )&( Param->TimeOut ), + ( char * )Evt.Sid ); +strcpy( Evt.PublisherUrl, Param->Url ); Evt.TimeOut = Param->TimeOut; +Param->Fun( UPNP_EVENT_SUBSCRIBE_COMPLETE, &Evt, Param->Cookie ); +free( Param ); break;} + case UNSUBSCRIBE: + { + struct Upnp_Event_Subscribe Evt; + Evt.ErrCode = + genaUnSubscribe( Param->Handle, + Param->SubsId ); + strcpy( ( char * )Evt.Sid, Param->SubsId ); + strcpy( Evt.PublisherUrl, "" ); + Evt.TimeOut = 0; + Param->Fun( UPNP_EVENT_UNSUBSCRIBE_COMPLETE, + &Evt, Param->Cookie ); + free( Param ); break;} + case RENEW: + { + struct Upnp_Event_Subscribe Evt; + Evt.ErrCode = + genaRenewSubscription( Param->Handle, + Param->SubsId, + &( Param->TimeOut ) ); + Evt.TimeOut = Param->TimeOut; + strcpy( ( char * )Evt.Sid, Param->SubsId ); + Param->Fun( UPNP_EVENT_RENEWAL_COMPLETE, &Evt, + Param->Cookie ); free( Param ); + break;} + ) +#endif +#if EXCLUDE_SOAP == 0 + case ACTION: + { + struct Upnp_Action_Complete Evt; + + Evt.ActionResult = NULL; +#ifdef INCLUDE_CLIENT_APIS + + Evt.ErrCode = + SoapSendAction( Param->Url, Param->ServiceType, + Param->Act, &Evt.ActionResult ); +#endif + + Evt.ActionRequest = Param->Act; + strcpy( Evt.CtrlUrl, Param->Url ); + + Param->Fun( UPNP_CONTROL_ACTION_COMPLETE, &Evt, + Param->Cookie ); + + ixmlDocument_free( Evt.ActionRequest ); + ixmlDocument_free( Evt.ActionResult ); + free( Param ); + break; + } + case STATUS: + { + struct Upnp_State_Var_Complete Evt; + +#ifdef INCLUDE_CLIENT_APIS + + Evt.ErrCode = SoapGetServiceVarStatus( Param->Url, + Param->VarName, + &( Evt. + CurrentVal ) ); +#endif + strcpy( Evt.StateVarName, Param->VarName ); + strcpy( Evt.CtrlUrl, Param->Url ); + + Param->Fun( UPNP_CONTROL_GET_VAR_COMPLETE, &Evt, + Param->Cookie ); + free( Evt.CurrentVal ); + free( Param ); + break; + } +#endif //EXCLUDE_SOAP + default: + break; + } // end of switch(Param->FunName) + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting UpnpThreadDistribution \n" ); + ) + +} /****************** End of UpnpThreadDistribution *********************/ +#endif + +/************************************************************************** + * Function: GetCallBackFn + * + * Parameters: + * + * Description: + * This function is to get callback function ptr from a handle + * + * Return Values: Upnp_FunPtr + * + ***************************************************************************/ +Upnp_FunPtr +GetCallBackFn( UpnpClient_Handle Hnd ) +{ + return ( ( struct Handle_Info * )HandleTable[Hnd] )->Callback; + +} /****************** End of GetCallBackFn *********************/ + +/************************************************************************** + * Function: InitHandleList + * + * Parameters: VOID + * + * Description: + * This function is to initialize handle table + * + * Return Values: VOID + * + ***************************************************************************/ +void +InitHandleList( ) +{ + int i; + + for( i = 0; i < NUM_HANDLE; i++ ) + HandleTable[i] = NULL; + +} /****************** End of InitHandleList *********************/ + +/************************************************************************** + * Function: GetFreeHandle + * + * Parameters: VOID + * + * Description: + * This function is to get a free handle + * + * Return Values: VOID + * + ***************************************************************************/ +int +GetFreeHandle( ) +{ + int i = 1; + + /* + Handle 0 is not used as NULL translates to 0 when passed as a handle + */ + while( i < NUM_HANDLE ) { + if( HandleTable[i++] == NULL ) + break; + } + + if( i == NUM_HANDLE ) + return UPNP_E_OUTOF_HANDLE; //Error + else + return --i; + +} /****************** End of GetFreeHandle *********************/ + +/************************************************************************** + * Function: GetClientHandleInfo + * + * Parameters: + * IN UpnpClient_Handle *client_handle_out: client handle pointer ( key + * for the client handle structure). + * OUT struct Handle_Info **HndInfo: Client handle structure passed by + * this function. + * + * Description: + * This function is to get client handle info + * + * Return Values: HND_CLIENT + * + ***************************************************************************/ +//Assumes at most one client +Upnp_Handle_Type +GetClientHandleInfo( IN UpnpClient_Handle * client_handle_out, + OUT struct Handle_Info ** HndInfo ) +{ + ( *client_handle_out ) = 1; + if( GetHandleInfo( 1, HndInfo ) == HND_CLIENT ) { + return HND_CLIENT; + } + ( *client_handle_out ) = 2; + if( GetHandleInfo( 2, HndInfo ) == HND_CLIENT ) { + return HND_CLIENT; + } + ( *client_handle_out ) = -1; + return HND_INVALID; + +} /****************** End of GetClientHandleInfo *********************/ + +/************************************************************************** + * Function: GetDeviceHandleInfo + * + * Parameters: + * IN UpnpDevice_Handle * device_handle_out: device handle pointer + * (key for the client handle structure). + * OUT struct Handle_Info **HndInfo: Device handle structure passed by + * this function. + * + * Description: + * This function is to get device handle info. + * + * Return Values: HND_DEVICE + * + ***************************************************************************/ +Upnp_Handle_Type +GetDeviceHandleInfo( UpnpDevice_Handle * device_handle_out, + struct Handle_Info ** HndInfo ) +{ + ( *device_handle_out ) = 1; + if( GetHandleInfo( 1, HndInfo ) == HND_DEVICE ) + return HND_DEVICE; + + ( *device_handle_out ) = 2; + if( GetHandleInfo( 2, HndInfo ) == HND_DEVICE ) + return HND_DEVICE; + ( *device_handle_out ) = -1; + + return HND_INVALID; + +} /****************** End of GetDeviceHandleInfo *********************/ + +/************************************************************************** + * Function: GetDeviceHandleInfo + * + * Parameters: + * IN UpnpClient_Handle * device_handle_out: handle pointer + * (key for the client handle structure). + * OUT struct Handle_Info **HndInfo: handle structure passed by + * this function. + * + * Description: + * This function is to get handle info. + * + * Return Values: HND_DEVICE + * + ***************************************************************************/ +Upnp_Handle_Type +GetHandleInfo( UpnpClient_Handle Hnd, + struct Handle_Info ** HndInfo ) +{ + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "GetHandleInfo: Handle is %d\n", Hnd ); + ) + + if( Hnd < 1 || Hnd >= NUM_HANDLE ) { + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "GetHandleInfo : Handle out of range\n" ); + ) + return UPNP_E_INVALID_HANDLE; + } + if( HandleTable[Hnd] != NULL ) { + + *HndInfo = ( struct Handle_Info * )HandleTable[Hnd]; + return ( ( struct Handle_Info * )*HndInfo )->HType; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "GetHandleInfo : exiting\n" ); + ) + + return UPNP_E_INVALID_HANDLE; + +} /****************** End of GetHandleInfo *********************/ + +/************************************************************************** + * Function: FreeHandle + * + * Parameters: + * IN int Upnp_Handle: handle index + * + * Description: + * This function is to to free handle info. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else return appropriate error + ***************************************************************************/ +int +FreeHandle( int Upnp_Handle ) +{ + if( Upnp_Handle < 1 || Upnp_Handle >= NUM_HANDLE ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "FreeHandleInfo : Handle out of range\n" ); + ) + return UPNP_E_INVALID_HANDLE; + } + + if( HandleTable[Upnp_Handle] == NULL ) { + return UPNP_E_INVALID_HANDLE; + } + free( HandleTable[Upnp_Handle] ); + HandleTable[Upnp_Handle] = NULL; + return UPNP_E_SUCCESS; + +} /****************** End of FreeHandle *********************/ + +// **DBG**************************************************** +DBGONLY( + +/************************************************************************** + * Function: PrintHandleInfo + * + * Parameters: + * IN UpnpClient_Handle Hnd: handle index + * + * Description: + * This function is to print handle info. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else return appropriate error + ***************************************************************************/ + int PrintHandleInfo( IN UpnpClient_Handle Hnd ) { + struct Handle_Info * HndInfo; if( HandleTable[Hnd] != NULL ) { + HndInfo = HandleTable[Hnd]; + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Printing information for Handle_%d\n", + Hnd ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "HType_%d\n", HndInfo->HType ); + DEVICEONLY( + if( HndInfo->HType != + HND_CLIENT ) UpnpPrintf( UPNP_ALL, API, __FILE__, + __LINE__, "DescURL_%s\n", + HndInfo->DescURL ); ) + ) + } + else + { + return UPNP_E_INVALID_HANDLE;} + + return UPNP_E_SUCCESS;} + /****************** End of PrintHandleInfo *********************/ + + void printNodes( IXML_Node * tmpRoot, int depth ) { + int i; + IXML_NodeList * NodeList1; + IXML_Node * ChildNode1; + unsigned short NodeType; + DOMString NodeValue; + const DOMString NodeName; + NodeList1 = ixmlNode_getChildNodes( tmpRoot ); + for( i = 0; i < 100; i++ ) { + ChildNode1 = ixmlNodeList_item( NodeList1, i ); + if( ChildNode1 == NULL ) { + break;} + + printNodes( ChildNode1, depth + 1 ); + NodeType = ixmlNode_getNodeType( ChildNode1 ); + NodeValue = ixmlNode_getNodeValue( ChildNode1 ); + NodeName = ixmlNode_getNodeName( ChildNode1 ); + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "DEPTH-%2d-IXML_Node Type %d, " + "IXML_Node Name: %s, IXML_Node Value: %s\n", + depth, NodeType, NodeName, NodeValue ); ) + } + + } + /****************** End of printNodes *********************/ + + ) // dbgonly + + //******************************************************** + //* Name: getlocalhostname + //* Description: Function to get local IP address + //* Gets the ip address for the DEFAULT_INTERFACE + //* interface which is up and not a loopback + //* assumes at most MAX_INTERFACES interfaces + //* Called by: UpnpInit + //* In: char *out + //* Out: Ip address + //* Return codes: UPNP_E_SUCCESS + //* Error codes: UPNP_E_INIT + //******************************************************** + + /************************************************************************** + * Function: getlocalhostname + * + * Parameters: + * OUT char *out: IP address of the interface. + * + * Description: + * This function is to get local IP address. It gets the ip address for + * the DEFAULT_INTERFACE interface which is up and not a loopback + * assumes at most MAX_INTERFACES interfaces + * + * Return Values: int + * UPNP_E_SUCCESS if successful else return appropriate error + ***************************************************************************/ + int getlocalhostname( OUT char *out ) { + + char szBuffer[MAX_INTERFACES * sizeof( struct ifreq )]; + struct ifconf ifConf; + struct ifreq ifReq; + int nResult; + int i; + int LocalSock; + struct sockaddr_in LocalAddr; + int j = 0; + + // Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. + if( ( LocalSock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) < 0 ) { + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Can't create addrlist socket\n" ); + ) + return UPNP_E_INIT; + } + // Get the interface configuration information... + ifConf.ifc_len = sizeof szBuffer; + ifConf.ifc_ifcu.ifcu_buf = ( caddr_t ) szBuffer; + nResult = ioctl( LocalSock, SIOCGIFCONF, &ifConf ); + + if( nResult < 0 ) { + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "DiscoverInterfaces: SIOCGIFCONF returned error\n" ); + ) + + return UPNP_E_INIT; + } + // Cycle through the list of interfaces looking for IP addresses. + + for( i = 0; ( ( i < ifConf.ifc_len ) && ( j < DEFAULT_INTERFACE ) ); ) { + struct ifreq *pifReq = + ( struct ifreq * )( ( caddr_t ) ifConf.ifc_req + i ); + i += sizeof *pifReq; + + // See if this is the sort of interface we want to deal with. + strcpy( ifReq.ifr_name, pifReq->ifr_name ); + if( ioctl( LocalSock, SIOCGIFFLAGS, &ifReq ) < 0 ) { + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Can't get interface flags for %s:\n", + ifReq.ifr_name ); + ) + + } + // Skip loopback, point-to-point and down interfaces, + // except don't skip down interfaces + // if we're trying to get a list of configurable interfaces. + if( ( ifReq.ifr_flags & IFF_LOOPBACK ) + || ( !( ifReq.ifr_flags & IFF_UP ) ) ) { + continue; + } + if( pifReq->ifr_addr.sa_family == AF_INET ) { + // Get a pointer to the address... + memcpy( &LocalAddr, &pifReq->ifr_addr, + sizeof pifReq->ifr_addr ); + + // We don't want the loopback interface. + if( LocalAddr.sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) { + continue; + } + + } + //increment j if we found an address which is not loopback + //and is up + j++; + + } + close( LocalSock ); + + strncpy( out, inet_ntoa( LocalAddr.sin_addr ), LINE_SIZE ); + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside getlocalhostname : after strncpy %s\n", + out ); + ) + return UPNP_E_SUCCESS; + } + +#ifdef INCLUDE_DEVICE_APIS +#if EXCLUDE_SSDP == 0 + + /************************************************************************** + * Function: AutoAdvertise + * + * Parameters: + * IN void *input: information provided to the thread. + * + * Description: + * This function is a timer thread scheduled by UpnpSendAdvertisement + * to the send advetisement again. + * + * Return Values: VOID + * + ***************************************************************************/ +void +AutoAdvertise( void *input ) +{ + upnp_timeout *event = ( upnp_timeout * ) input; + + UpnpSendAdvertisement( event->handle, *( ( int * )event->Event ) ); + free_upnp_timeout( event ); +} +#endif //INCLUDE_DEVICE_APIS +#endif + +/* + **************************** */ +#ifdef INTERNAL_WEB_SERVER + + /************************************************************************** + * Function: UpnpSetWebServerRootDir + * + * Parameters: + * IN const char* rootDir:Path of the root directory of the web server. + * + * Description: + * This function sets the document root directory for + * the internal web server. This directory is considered the + * root directory (i.e. "/") of the web server. + * This function also activates or deactivates the web server. + * To disable the web server, pass NULL for rootDir to + * activate, pass a valid directory string. + * + * Note that this function is not available when the web server is not + * compiled into the UPnP Library. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else returns appropriate error + ***************************************************************************/ +int +UpnpSetWebServerRootDir( IN const char *rootDir ) +{ + if( UpnpSdkInit == 0 ) + return UPNP_E_FINISH; + if( ( rootDir == NULL ) || ( strlen( rootDir ) == 0 ) ) { + return UPNP_E_INVALID_PARAM; + } + + membuffer_destroy( &gDocumentRootDir ); + + return ( web_server_set_root_dir( rootDir ) ); +} +#endif // INTERNAL_WEB_SERVER +/* + *************************** */ + + /************************************************************************** + * Function: UpnpAddVirtualDir + * + * Parameters: + * IN const char *newDirName:The name of the new directory mapping to add. + * + * Description: + * This function adds a virtual directory mapping. + * + * All webserver requests containing the given directory are read using + * functions contained in a UpnpVirtualDirCallbacks structure registered + * via UpnpSetVirtualDirCallbacks. + * + * Note that this function is not available when the web server is not + * compiled into the UPnP Library. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else returns appropriate error + ***************************************************************************/ +int +UpnpAddVirtualDir( IN const char *newDirName ) +{ + + virtualDirList *pNewVirtualDir, + *pLast; + virtualDirList *pCurVirtualDir; + char dirName[NAME_SIZE]; + + if( UpnpSdkInit != 1 ) { + // SDK is not initialized + return UPNP_E_FINISH; + } + + if( ( newDirName == NULL ) || ( strlen( newDirName ) == 0 ) ) { + return UPNP_E_INVALID_PARAM; + } + + if( *newDirName != '/' ) { + dirName[0] = '/'; + strcpy( dirName + 1, newDirName ); + } else { + strcpy( dirName, newDirName ); + } + + pCurVirtualDir = pVirtualDirList; + while( pCurVirtualDir != NULL ) { + // already has this entry + if( strcmp( pCurVirtualDir->dirName, dirName ) == 0 ) { + return UPNP_E_SUCCESS; + } + + pCurVirtualDir = pCurVirtualDir->next; + } + + pNewVirtualDir = + ( virtualDirList * ) malloc( sizeof( virtualDirList ) ); + if( pNewVirtualDir == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + pNewVirtualDir->next = NULL; + strcpy( pNewVirtualDir->dirName, dirName ); + *( pNewVirtualDir->dirName + strlen( dirName ) ) = 0; + + if( pVirtualDirList == NULL ) { // first virtual dir + pVirtualDirList = pNewVirtualDir; + } else { + pLast = pVirtualDirList; + while( pLast->next != NULL ) { + pLast = pLast->next; + } + pLast->next = pNewVirtualDir; + } + + return UPNP_E_SUCCESS; +} + + /************************************************************************** + * Function: UpnpRemoveVirtualDir + * + * Parameters: + * IN const char *newDirName:The name of the directory mapping to remove. + * + * Description: + * This function removes a virtual directory mapping. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else returns appropriate error + ***************************************************************************/ +int +UpnpRemoveVirtualDir( IN const char *dirName ) +{ + + virtualDirList *pPrev; + virtualDirList *pCur; + int found = 0; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + if( dirName == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + if( pVirtualDirList == NULL ) { + return UPNP_E_INVALID_PARAM; + } + // + // Handle the special case where the directory that we are + // removing is the first and only one in the list. + // + + if( ( pVirtualDirList->next == NULL ) && + ( strcmp( pVirtualDirList->dirName, dirName ) == 0 ) ) { + free( pVirtualDirList ); + pVirtualDirList = NULL; + return UPNP_E_SUCCESS; + } + + pCur = pVirtualDirList; + pPrev = pCur; + + while( pCur != NULL ) { + if( strcmp( pCur->dirName, dirName ) == 0 ) { + pPrev->next = pCur->next; + free( pCur ); + found = 1; + break; + } else { + pPrev = pCur; + pCur = pCur->next; + } + } + + if( found == 1 ) + return UPNP_E_SUCCESS; + else + return UPNP_E_INVALID_PARAM; + +} + + /************************************************************************** + * Function: UpnpRemoveAllVirtualDirs + * + * Parameters: VOID + * + * Description: + * This function removes all the virtual directory mappings. + * + * Return Values: VOID + * + ***************************************************************************/ +void +UpnpRemoveAllVirtualDirs( ) +{ + + virtualDirList *pCur; + virtualDirList *pNext; + + if( UpnpSdkInit != 1 ) { + return; + } + + pCur = pVirtualDirList; + + while( pCur != NULL ) { + pNext = pCur->next; + free( pCur ); + + pCur = pNext; + } + + pVirtualDirList = NULL; + +} + + /************************************************************************** + * Function: UpnpEnableWebserver + * + * Parameters: + * IN int enable: TRUE to enable, FALSE to disable. + * + * Description: + * This function enables or disables the webserver. A value of + * TRUE enables the webserver, FALSE disables it. + * + * Return Values: int + * UPNP_E_SUCCESS if successful else returns appropriate error + ***************************************************************************/ +int +UpnpEnableWebserver( IN int enable ) +{ + int retVal; + + if( UpnpSdkInit != 1 ) { + return UPNP_E_FINISH; + } + + switch ( enable ) { +#ifdef INTERNAL_WEB_SERVER + case TRUE: + if( ( retVal = web_server_init( ) ) != UPNP_E_SUCCESS ) { + return retVal; + } + bWebServerState = WEB_SERVER_ENABLED; + SetHTTPGetCallback( web_server_callback ); + break; + + case FALSE: + web_server_destroy( ); + bWebServerState = WEB_SERVER_DISABLED; + SetHTTPGetCallback( NULL ); + break; +#endif + default: + return UPNP_E_INVALID_PARAM; + } + + return UPNP_E_SUCCESS; +} + + /************************************************************************** + * Function: UpnpIsWebserverEnabled + * + * Parameters: VOID + * + * Description: + * This function checks if the webserver is enabled or disabled. + * + * Return Values: int + * 1, if webserver enabled + * 0, if webserver disabled + ***************************************************************************/ +int +UpnpIsWebserverEnabled( ) +{ + if( UpnpSdkInit != 1 ) { + return 0; + } + + return ( bWebServerState == WEB_SERVER_ENABLED ); +} + + /************************************************************************** + * Function: UpnpSetVirtualDirCallbacks + * + * Parameters: + * IN struct UpnpVirtualDirCallbacks *callbacks:a structure that + * contains the callback functions. + * + * Description: + * This function sets the callback function to be used to + * access a virtual directory. + * + * Return Values: int + * UPNP_E_SUCCESS on success, or UPNP_E_INVALID_PARAM + ***************************************************************************/ +int +UpnpSetVirtualDirCallbacks( IN struct UpnpVirtualDirCallbacks *callbacks ) +{ + struct UpnpVirtualDirCallbacks *pCallback; + + if( UpnpSdkInit != 1 ) { + // SDK is not initialized + return UPNP_E_FINISH; + } + + pCallback = &virtualDirCallback; + + if( callbacks == NULL ) + return UPNP_E_INVALID_PARAM; + + pCallback->get_info = callbacks->get_info; + pCallback->open = callbacks->open; + pCallback->close = callbacks->close; + pCallback->read = callbacks->read; + pCallback->write = callbacks->write; + pCallback->seek = callbacks->seek; + + return UPNP_E_SUCCESS; +} + + /************************************************************************** + * Function: UpnpFree + * + * Parameters: + * IN void *item:The item to free. + * + * Description: + * This function free the memory allocated by tbe UPnP library + * + * Return Values: VOID + * + ***************************************************************************/ +void +UpnpFree( IN void *item ) +{ + if( item ) + free( item ); +} + + +/************************************************************************** + * Function: UpnpSetContentLength + * OBSOLETE METHOD : use {\bf UpnpSetMaxContentLength} instead. + ***************************************************************************/ +int +UpnpSetContentLength( IN UpnpClient_Handle Hnd, + /** The handle of the device instance + for which the coincoming content length needs + to be set. */ + + IN int contentLength + /** Permissible content length */ + ) +{ + return UpnpSetMaxContentLength (contentLength); +} + +/************************************************************************** + * Function: UpnpSetMaxContentLength + * + * Parameters: + * IN int contentLength: The maximum size to be set + * + * Description: + * Sets the maximum content-length that the SDK will process on an + * incoming SOAP requests or responses. This API allows devices that have + * memory constraints to exhibit consistent behaviour if the size of the + * incoming SOAP message exceeds the memory that device can allocate. + * The default maximum content-length is {\tt DEFAULT_SOAP_CONTENT_LENGTH} + * = 16K bytes. + * + * Return Values: int + * UPNP_E_SUCCESS: The operation completed successfully. + * + ***************************************************************************/ +int +UpnpSetMaxContentLength ( + IN size_t contentLength + /** Permissible content length, in bytes */ + ) +{ + int errCode = UPNP_E_SUCCESS; + + do { + if( UpnpSdkInit != 1 ) { + errCode = UPNP_E_FINISH; + break; + } + + g_maxContentLength = contentLength; + + } while( 0 ); + + return errCode; + +} + +/*********************** END OF FILE upnpapi.c :) ************************/ diff --git a/libupnp/upnp/src/api/upnpdebug.c b/libupnp/upnp/src/api/upnpdebug.c new file mode 100644 index 0000000..593c4d2 --- /dev/null +++ b/libupnp/upnp/src/api/upnpdebug.c @@ -0,0 +1,378 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#include "upnpdebug.h" +#include +#include +#include "ithread.h" +#include "upnp.h" +#include +#include + + + +//Mutex to synchronize all the log file opeartions in the debug mode +static ithread_mutex_t GlobalDebugMutex; + +// Global log level +static Upnp_LogLevel g_log_level = UPNP_DEFAULT_LOG_LEVEL; + +//File handle for the error log file +static FILE *ErrFileHnd = NULL; + +//File handle for the information log file +static FILE *InfoFileHnd = NULL; + +//Name of the error file +static const char *errFileName = "IUpnpErrFile.txt"; + +//Name of the info file +static const char *infoFileName = "IUpnpInfoFile.txt"; + + + +/*************************************************************************** + * Function : UpnpSetLogFileNames + * + * Parameters: + * IN const char* ErrFileName: name of the error file + * IN const char *InfoFileName: name of the information file + * IN int size: Size of the buffer + * IN int starLength: This parameter provides the width of the banner + * + * Description: + * This functions takes the buffer and writes the buffer in the file as + * per the requested banner + * Returns: void + ***************************************************************************/ +void +UpnpSetLogFileNames ( IN const char *ErrFileName, + IN const char *InfoFileName ) +{ + if( ErrFileName ) { + errFileName = ErrFileName; + } + if( InfoFileName ) { + infoFileName = InfoFileName; + } +} + + +/*************************************************************************** + * Function : UpnpInitLog + * + * Parameters: void + * + * Description: + * This functions initializes the log files + * Returns: int + * -1 : If fails + * UPNP_E_SUCCESS : if success + ***************************************************************************/ +int +UpnpInitLog( ) +{ + ithread_mutex_init( &GlobalDebugMutex, NULL ); + + if( DEBUG_TARGET == 1 ) { + if( ( ErrFileHnd = fopen( errFileName, "a" ) ) == NULL ) + return -1; + if( ( InfoFileHnd = fopen( infoFileName, "a" ) ) == NULL ) + return -1; + } + return UPNP_E_SUCCESS; +} + + +/*************************************************************************** + * Function : UpnpSetLogLevel + * + * Parameters: void + * + * Description: + * This functions set the log level (see {\tt Upnp_LogLevel} + * Returns: void + ***************************************************************************/ +void +UpnpSetLogLevel (Upnp_LogLevel log_level) +{ + g_log_level = log_level; +} + + +/*************************************************************************** + * Function : UpnpCloseLog + * + * Parameters: void + * + * Description: + * This functions closes the log files + * Returns: void + ***************************************************************************/ +void +UpnpCloseLog( ) +{ + if( DEBUG_TARGET == 1 ) { + fflush( ErrFileHnd ); + fflush( InfoFileHnd ); + fclose( ErrFileHnd ); + fclose( InfoFileHnd ); + } + ithread_mutex_destroy( &GlobalDebugMutex ); + +} + + +/*************************************************************************** + * Function : UpnpPrintf + * + * Parameters: + * IN Dbg_Level DLevel: The level of the debug logging. It will decide + * whether debug statement will go to standard output, + * or any of the log files. + * IN Dbg_Module Module: debug will go in the name of this module + * IN char *DbgFileName: Name of the file from where debug statement is + * coming + * IN int DbgLineNo : Line number of the file from where debug statement + * is coming + * IN char * FmtStr, ...: Variable number of arguments that will go + * in the debug statement + * + * Description: + * This functions prints the debug statement either on the startdard + * output or log file along with the information from where this debug + * statement is coming + * Returns: void + ***************************************************************************/ +DBGONLY( void UpnpPrintf( IN Upnp_LogLevel DLevel, + IN Dbg_Module Module, + IN const char *DbgFileName, + IN int DbgLineNo, + IN const char *FmtStr, + ... ) { + + va_list ArgList; + va_start( ArgList, FmtStr ); + if( g_log_level < DLevel ) return; if( DEBUG_ALL == 0 ) { + switch ( Module ) { +case SSDP: + if( DEBUG_SSDP == 1 ) break; + else +return; case SOAP: + if( DEBUG_SOAP == 1 ) break; + else +return; case GENA: + if( DEBUG_GENA == 1 ) break; + else +return; case TPOOL: + if( DEBUG_TPOOL == 1 ) break; + else +return; case MSERV: + if( DEBUG_MSERV == 1 ) break; + else +return; case DOM: + if( DEBUG_DOM == 1 ) break; + else +return; case HTTP: + if( DEBUG_HTTP == 1 ) break; + else +return; case API: + if( DEBUG_API == 1 ) break; + else +return; default: + return;} + } + + ithread_mutex_lock( &GlobalDebugMutex ); if( DEBUG_TARGET == 0 ) { + if( DbgFileName ) { + UpnpDisplayFileAndLine( stdout, DbgFileName, DbgLineNo );} + vfprintf( stdout, FmtStr, ArgList ); fflush( stdout );} + else + { + if( DLevel == 0 ) { + if( DbgFileName ) { + UpnpDisplayFileAndLine( ErrFileHnd, DbgFileName, DbgLineNo );} + vfprintf( ErrFileHnd, FmtStr, ArgList ); fflush( ErrFileHnd );} + else + { + if( DbgFileName ) { + UpnpDisplayFileAndLine( InfoFileHnd, DbgFileName, DbgLineNo );} + vfprintf( InfoFileHnd, FmtStr, ArgList ); fflush( InfoFileHnd );} + } + va_end( ArgList ); ithread_mutex_unlock( &GlobalDebugMutex );} + + ) + + +/*************************************************************************** + * Function : UpnpGetDebugFile + * + * Parameters: + * IN Dbg_Level DLevel: The level of the debug logging. It will decide + * whether debug statement will go to standard output, + * or any of the log files. + * IN Dbg_Module Module: debug will go in the name of this module + * + * Description: + * This function checks if the module is turned on for debug + * and returns the file descriptor corresponding to the debug level + * Returns: FILE * + * NULL : if the module is turn off for debug + * else returns the right file descriptor + ***************************************************************************/ + DBGONLY( FILE * GetDebugFile( Upnp_LogLevel DLevel, Dbg_Module Module ) { + if( g_log_level < DLevel ) return NULL; if( DEBUG_ALL == 0 ) { + switch ( Module ) { +case SSDP: + if( DEBUG_SSDP == 1 ) break; + else +return NULL; case SOAP: + if( DEBUG_SOAP == 1 ) break; + else +return NULL; case GENA: + if( DEBUG_GENA == 1 ) break; + else +return NULL; case TPOOL: + if( DEBUG_TPOOL == 1 ) break; + else +return NULL; case MSERV: + if( DEBUG_MSERV == 1 ) break; + else +return NULL; case DOM: + if( DEBUG_DOM == 1 ) break; + else +return NULL; case API: + if( DEBUG_API == 1 ) break; + else +return NULL; default: + return NULL;} + } + + if( DEBUG_TARGET == 0 ) { + return stdout;} + else + { + if( DLevel == 0 ) { + return ErrFileHnd;} + else + { + return InfoFileHnd;} + } + } + ) + + +/*************************************************************************** + * Function : UpnpDisplayFileAndLine + * + * Parameters: + * IN FILE *fd: File descriptor where line number and file name will be + * written + * IN char *DbgFileName: Name of the file + * IN int DbgLineNo : Line number of the file + * + * Description: + * This function writes the file name and file number from where + * debug statement is coming to the log file + * Returns: void + ***************************************************************************/ + DBGONLY( void UpnpDisplayFileAndLine( IN FILE * fd, + IN const char *DbgFileName, + IN int DbgLineNo ) { + int starlength = 66; + const char *lines[2]; + char FileAndLine[500]; lines[0] = "DEBUG"; if( DbgFileName ) { + sprintf( FileAndLine, "FILE: %s, LINE: %d", DbgFileName, + DbgLineNo ); lines[1] = FileAndLine;} + + UpnpDisplayBanner( fd, lines, 2, starlength ); fflush( fd );} + ) + + +/*************************************************************************** + * Function : UpnpDisplayBanner + * + * Parameters: + * IN FILE *fd: file descriptor where the banner will be written + * IN char **lines: The buffer that will be written + * IN int size: Size of the buffer + * IN int starLength: This parameter provides the width of the banner + * + * Description: + * This functions takes the buffer and writes the buffer in the file as + * per the requested banner + * Returns: void + ***************************************************************************/ + DBGONLY( void UpnpDisplayBanner( IN FILE * fd, + IN const char **lines, + IN size_t size, + IN int starLength ) { + char *stars = ( char * )malloc( starLength + 1 ); + const char *line = NULL; + int leftMarginLength = starLength / 2 + 1; + int rightMarginLength = starLength / 2 + 1; + char *leftMargin = ( char * )malloc( leftMarginLength ); + char *rightMargin = ( char * )malloc( rightMarginLength ); + int i = 0; + int LineSize = 0; + char *currentLine = ( char * )malloc( starLength + 1 ); + memset( stars, '*', starLength ); + stars[starLength] = 0; + memset( leftMargin, 0, leftMarginLength ); + memset( rightMargin, 0, rightMarginLength ); + fprintf( fd, "\n%s\n", stars ); for( i = 0; i < size; i++ ) { + LineSize = strlen( lines[i] ); + line = lines[i]; while( LineSize > ( starLength - 2 ) ) { + memcpy( currentLine, line, ( starLength - 2 ) ); + currentLine[( starLength - 2 )] = 0; + fprintf( fd, "*%s*\n", currentLine ); + LineSize -= ( starLength - 2 ); line += ( starLength - 2 );} + + if( LineSize % 2 == 0 ) { + leftMarginLength = rightMarginLength = + ( ( starLength - 2 ) - LineSize ) / 2;} + else + { + leftMarginLength = ( ( starLength - 2 ) - LineSize ) / 2; + rightMarginLength = + ( ( starLength - 2 ) - LineSize ) / 2 + 1;} + + memset( leftMargin, ' ', leftMarginLength ); + memset( rightMargin, ' ', rightMarginLength ); + leftMargin[leftMarginLength] = 0; + rightMargin[rightMarginLength] = 0; + fprintf( fd, "*%s%s%s*\n", leftMargin, line, rightMargin );} + + fprintf( fd, "%s\n\n", stars ); + free( leftMargin ); + free( rightMargin ); free( stars ); free( currentLine );} + ) diff --git a/libupnp/upnp/src/api/upnptools.c b/libupnp/upnp/src/api/upnptools.c new file mode 100644 index 0000000..d4ba365 --- /dev/null +++ b/libupnp/upnp/src/api/upnptools.c @@ -0,0 +1,587 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#if EXCLUDE_DOM == 0 +#include +#include "upnptools.h" +#include "uri.h" +#define HEADER_LENGTH 2000 + +//Structure to maintain a error code and string associated with the +// error code +struct ErrorString { + int rc; /* error code */ + const char *rcError; /* error description */ + +}; + +//Intializing the array of error structures. +struct ErrorString ErrorMessages[] = { {UPNP_E_SUCCESS, "UPNP_E_SUCCESS"}, +{UPNP_E_INVALID_HANDLE, "UPNP_E_INVALID_HANDLE"}, +{UPNP_E_INVALID_PARAM, "UPNP_E_INVALID_PARAM"}, +{UPNP_E_OUTOF_HANDLE, "UPNP_E_OUTOF_HANDLE"}, +{UPNP_E_OUTOF_CONTEXT, "UPNP_E_OUTOF_CONTEXT"}, +{UPNP_E_OUTOF_MEMORY, "UPNP_E_OUTOF_MEMOR"}, +{UPNP_E_INIT, "UPNP_E_INIT"}, +{UPNP_E_BUFFER_TOO_SMALL, "UPNP_E_BUFFER_TOO_SMALL"}, +{UPNP_E_INVALID_DESC, "UPNP_E_INVALID_DESC"}, +{UPNP_E_INVALID_URL, "UPNP_E_INVALID_URL"}, +{UPNP_E_INVALID_SID, "UPNP_E_INVALID_SID"}, +{UPNP_E_INVALID_DEVICE, "UPNP_E_INVALID_DEVICE"}, +{UPNP_E_INVALID_SERVICE, "UPNP_E_INVALID_SERVICE"}, +{UPNP_E_BAD_RESPONSE, "UPNP_E_BAD_RESPONSE"}, +{UPNP_E_BAD_REQUEST, "UPNP_E_BAD_REQUEST"}, +{UPNP_E_INVALID_ACTION, "UPNP_E_INVALID_ACTION"}, +{UPNP_E_FINISH, "UPNP_E_FINISH"}, +{UPNP_E_INIT_FAILED, "UPNP_E_INIT_FAILED"}, +{UPNP_E_BAD_HTTPMSG, "UPNP_E_BAD_HTTPMSG"}, +{UPNP_E_NETWORK_ERROR, "UPNP_E_NETWORK_ERROR"}, +{UPNP_E_SOCKET_WRITE, "UPNP_E_SOCKET_WRITE"}, +{UPNP_E_SOCKET_READ, "UPNP_E_SOCKET_READ"}, +{UPNP_E_SOCKET_BIND, "UPNP_E_SOCKET_BIND"}, +{UPNP_E_SOCKET_CONNECT, "UPNP_E_SOCKET_CONNECT"}, +{UPNP_E_OUTOF_SOCKET, "UPNP_E_OUTOF_SOCKET"}, +{UPNP_E_LISTEN, "UPNP_E_LISTEN"}, +{UPNP_E_EVENT_PROTOCOL, "UPNP_E_EVENT_PROTOCOL"}, +{UPNP_E_SUBSCRIBE_UNACCEPTED, "UPNP_E_SUBSCRIBE_UNACCEPTED"}, +{UPNP_E_UNSUBSCRIBE_UNACCEPTED, "UPNP_E_UNSUBSCRIBE_UNACCEPTED"}, +{UPNP_E_NOTIFY_UNACCEPTED, "UPNP_E_NOTIFY_UNACCEPTED"}, +{UPNP_E_INTERNAL_ERROR, "UPNP_E_INTERNAL_ERROR"}, +{UPNP_E_INVALID_ARGUMENT, "UPNP_E_INVALID_ARGUMENT"}, +{UPNP_E_OUTOF_BOUNDS, "UPNP_E_OUTOF_BOUNDS"} +}; + +/************************************************************************ +* Function : UpnpGetErrorMessage +* +* Parameters: +* IN int rc: error code +* +* Description: +* This functions returns the error string mapped to the error code +* Returns: const char * +* return either the right string or "Unknown Error" +***************************************************************************/ +const char * +UpnpGetErrorMessage( IN int rc ) +{ + int i; + + for( i = 0; i < sizeof( ErrorMessages ) / sizeof( ErrorMessages[0] ); + i++ ) { + if( rc == ErrorMessages[i].rc ) + return ErrorMessages[i].rcError; + + } + + return "Unknown Error"; + +} + +/************************************************************************ +* Function : UpnpResolveURL +* +* Parameters: +* IN char * BaseURL: Base URL string +* IN char * RelURL: relative URL string +* OUT char * AbsURL: Absolute URL string +* Description: +* This functions concatinates the base URL and relative URL to generate +* the absolute URL +* Returns: int +* return either UPNP_E_SUCCESS or appropriate error +***************************************************************************/ +int +UpnpResolveURL( IN const char *BaseURL, + IN const char *RelURL, + OUT char *AbsURL ) +{ + // There is some unnecessary allocation and + // deallocation going on here because of the way + // resolve_rel_url was originally written and used + // in the future it would be nice to clean this up + + char *tempRel; + + if( RelURL == NULL ) + return UPNP_E_INVALID_PARAM; + + tempRel = NULL; + + tempRel = resolve_rel_url((char*) BaseURL, (char*) RelURL ); + + if( tempRel ) { + strcpy( AbsURL, tempRel ); + free( tempRel ); + } else { + return UPNP_E_INVALID_URL; + } + + return UPNP_E_SUCCESS; + +} + +/************************************************************************ +* Function : addToAction +* +* Parameters: +* IN int response: flag to tell if the ActionDoc is for response +* or request +* INOUT IXML_Document **ActionDoc: request or response document +* IN char *ActionName: Name of the action request or response +* IN char *ServType: Service type +* IN char * ArgName: Name of the argument +* IN char * ArgValue: Value of the argument +* +* Description: +* This function adds the argument in the action request or response. +* This function creates the action request or response if it is a first +* argument else it will add the argument in the document +* +* Returns: int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +static int +addToAction( IN int response, + INOUT IXML_Document ** ActionDoc, + IN const char *ActionName, + IN const char *ServType, + IN const char *ArgName, + IN const char *ArgValue ) +{ + char *ActBuff = NULL; + IXML_Node *node = NULL; + IXML_Element *Ele = NULL; + IXML_Node *Txt = NULL; + int rc = 0; + + if( ActionName == NULL || ServType == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + if( *ActionDoc == NULL ) { + ActBuff = ( char * )malloc( HEADER_LENGTH ); + if( ActBuff == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + if( response ) { + sprintf( ActBuff, + "", + ActionName, ServType, ActionName ); + } else { + sprintf( ActBuff, "", + ActionName, ServType, ActionName ); + } + + rc = ixmlParseBufferEx( ActBuff, ActionDoc ); + free( ActBuff ); + if( rc != IXML_SUCCESS ) { + if( rc == IXML_INSUFFICIENT_MEMORY ) { + return UPNP_E_OUTOF_MEMORY; + } else { + return UPNP_E_INVALID_DESC; + } + } + } + + if( ArgName != NULL /*&& ArgValue != NULL */ ) { + node = ixmlNode_getFirstChild( ( IXML_Node * ) * ActionDoc ); + Ele = ixmlDocument_createElement( *ActionDoc, ArgName ); + if( ArgValue ) { + Txt = ixmlDocument_createTextNode( *ActionDoc, ArgValue ); + ixmlNode_appendChild( ( IXML_Node * ) Ele, Txt ); + } + + ixmlNode_appendChild( node, ( IXML_Node * ) Ele ); + } + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : makeAction +* +* Parameters: +* IN int response: flag to tell if the ActionDoc is for response +* or request +* IN char * ActionName: Name of the action request or response +* IN char * ServType: Service type +* IN int NumArg :Number of arguments in the action request or response +* IN char * Arg : pointer to the first argument +* IN va_list ArgList: Argument list +* +* Description: +* This function creates the action request or response from the argument +* list. +* Returns: IXML_Document * +* returns action request or response document if successful +* else returns NULL +***************************************************************************/ +static IXML_Document * +makeAction( IN int response, + IN const char *ActionName, + IN const char *ServType, + IN int NumArg, + IN const char *Arg, + IN va_list ArgList ) +{ + const char *ArgName, + *ArgValue; + char *ActBuff; + int Idx = 0; + IXML_Document *ActionDoc; + IXML_Node *node; + IXML_Element *Ele; + IXML_Node *Txt = NULL; + + if( ActionName == NULL || ServType == NULL ) { + return NULL; + } + + ActBuff = ( char * )malloc( HEADER_LENGTH ); + if( ActBuff == NULL ) { + return NULL; + } + + if( response ) { + sprintf( ActBuff, "", + ActionName, ServType, ActionName ); + } else { + sprintf( ActBuff, "", + ActionName, ServType, ActionName ); + } + + if( ixmlParseBufferEx( ActBuff, &ActionDoc ) != IXML_SUCCESS ) { + free( ActBuff ); + return NULL; + } + + free( ActBuff ); + + if( ActionDoc == NULL ) { + return NULL; + } + + if( NumArg > 0 ) { + //va_start(ArgList, Arg); + ArgName = Arg; + while( Idx++ != NumArg ) { + ArgValue = va_arg( ArgList, const char * ); + + if( ArgName != NULL ) { + node = ixmlNode_getFirstChild( ( IXML_Node * ) ActionDoc ); + Ele = ixmlDocument_createElement( ActionDoc, ArgName ); + if( ArgValue ) { + Txt = + ixmlDocument_createTextNode( ActionDoc, ArgValue ); + ixmlNode_appendChild( ( IXML_Node * ) Ele, Txt ); + } + + ixmlNode_appendChild( node, ( IXML_Node * ) Ele ); + } + + ArgName = va_arg( ArgList, const char * ); + } + //va_end(ArgList); + } + + return ActionDoc; +} + +/************************************************************************ +* Function : UpnpMakeAction +* +* Parameters: +* IN char * ActionName: Name of the action request or response +* IN char * ServType: Service type +* IN int NumArg :Number of arguments in the action request or response +* IN char * Arg : pointer to the first argument +* IN ... : variable argument list +* IN va_list ArgList: Argument list +* +* Description: +* This function creates the action request from the argument +* list. Its a wrapper function that calls makeAction function to create +* the action request. +* +* Returns: IXML_Document * +* returns action request document if successful +* else returns NULL +***************************************************************************/ +IXML_Document * +UpnpMakeAction( const char *ActionName, + const char *ServType, + int NumArg, + const char *Arg, + ... ) +{ + va_list ArgList=NULL; + IXML_Document *out = NULL; + + if( NumArg > 0 ) { + va_start( ArgList, Arg ); + } + + out = makeAction( 0, ActionName, ServType, NumArg, Arg, ArgList ); + if( NumArg > 0 ) { + va_end( ArgList ); + } + + return out; +} + +/************************************************************************ +* Function : UpnpMakeActionResponse +* +* Parameters: +* IN char * ActionName: Name of the action request or response +* IN char * ServType: Service type +* IN int NumArg :Number of arguments in the action request or response +* IN char * Arg : pointer to the first argument +* IN ... : variable argument list +* IN va_list ArgList: Argument list +* +* Description: +* This function creates the action response from the argument +* list. Its a wrapper function that calls makeAction function to create +* the action response. +* +* Returns: IXML_Document * +* returns action response document if successful +* else returns NULL +***************************************************************************/ +IXML_Document * +UpnpMakeActionResponse( const char *ActionName, + const char *ServType, + int NumArg, + const char *Arg, + ... ) +{ + va_list ArgList=NULL; + IXML_Document *out = NULL; + + if( NumArg > 0 ) { + va_start( ArgList, Arg ); + } + + out = makeAction( 1, ActionName, ServType, NumArg, Arg, ArgList ); + if( NumArg > 0 ) { + va_end( ArgList ); + } + + return out; +} + +/************************************************************************ +* Function : UpnpAddToActionResponse +* +* Parameters: +* INOUT IXML_Document **ActionResponse: action response document +* IN char * ActionName: Name of the action request or response +* IN char * ServType: Service type +* IN int ArgName :Name of argument to be added in the action response +* IN char * ArgValue : value of the argument +* +* Description: +* This function adds the argument in the action response. Its a wrapper +* function that calls addToAction function to add the argument in the +* action response. +* +* Returns: int +* returns UPNP_E_SUCCESS if successful +* else returns appropriate error +***************************************************************************/ +int +UpnpAddToActionResponse( INOUT IXML_Document ** ActionResponse, + IN const char *ActionName, + IN const char *ServType, + IN const char *ArgName, + IN const char *ArgValue ) +{ + return addToAction( 1, ActionResponse, ActionName, ServType, ArgName, + ArgValue ); +} + +/************************************************************************ +* Function : UpnpAddToAction +* +* Parameters: +* INOUT IXML_Document **ActionDoc: action request document +* IN char * ActionName: Name of the action request or response +* IN char * ServType: Service type +* IN int ArgName :Name of argument to be added in the action response +* IN char * ArgValue : value of the argument +* +* Description: +* This function adds the argument in the action request. Its a wrapper +* function that calls addToAction function to add the argument in the +* action request. +* +* Returns: int +* returns UPNP_E_SUCCESS if successful +* else returns appropriate error +***************************************************************************/ +int +UpnpAddToAction( IXML_Document ** ActionDoc, + const char *ActionName, + const char *ServType, + const char *ArgName, + const char *ArgValue ) +{ + + return addToAction( 0, ActionDoc, ActionName, ServType, ArgName, + ArgValue ); +} + +/************************************************************************ +* Function : UpnpAddToPropertySet +* +* Parameters: +* INOUT IXML_Document **PropSet: propertyset document +* IN char *ArgName: Name of the argument +* IN char *ArgValue: value of the argument +* +* Description: +* This function adds the argument in the propertyset node +* +* Returns: int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +int +UpnpAddToPropertySet( INOUT IXML_Document ** PropSet, + IN const char *ArgName, + IN const char *ArgValue ) +{ + + char BlankDoc[] = ""; + IXML_Node *node; + IXML_Element *Ele; + IXML_Element *Ele1; + IXML_Node *Txt; + int rc; + + if( ArgName == NULL ) { + return UPNP_E_INVALID_PARAM; + } + + if( *PropSet == NULL ) { + rc = ixmlParseBufferEx( BlankDoc, PropSet ); + if( rc != IXML_SUCCESS ) { + return UPNP_E_OUTOF_MEMORY; + } + } + + node = ixmlNode_getFirstChild( ( IXML_Node * ) * PropSet ); + + Ele1 = ixmlDocument_createElement( *PropSet, "e:property" ); + Ele = ixmlDocument_createElement( *PropSet, ArgName ); + + if( ArgValue ) { + Txt = ixmlDocument_createTextNode( *PropSet, ArgValue ); + ixmlNode_appendChild( ( IXML_Node * ) Ele, Txt ); + } + + ixmlNode_appendChild( ( IXML_Node * ) Ele1, ( IXML_Node * ) Ele ); + ixmlNode_appendChild( node, ( IXML_Node * ) Ele1 ); + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : UpnpCreatePropertySet +* +* Parameters: +* IN int NumArg: Number of argument that will go in the propertyset node +* IN char * Args: argument strings +* +* Description: +* This function creates a propertyset node and put all the input +* parameters in the node as elements +* +* Returns: IXML_Document * +* returns the document containing propertyset node. +***************************************************************************/ +IXML_Document * +UpnpCreatePropertySet( IN int NumArg, + IN const char *Arg, + ... ) +{ + va_list ArgList; + int Idx = 0; + char BlankDoc[] = ""; + const char *ArgName, + *ArgValue; + IXML_Node *node; + IXML_Element *Ele; + IXML_Element *Ele1; + IXML_Node *Txt; + IXML_Document *PropSet; + + if( ixmlParseBufferEx( BlankDoc, &PropSet ) != IXML_SUCCESS ) { + return NULL; + } + + if( NumArg < 1 ) { + return NULL; + } + + va_start( ArgList, Arg ); + ArgName = Arg; + + while( Idx++ != NumArg ) { + ArgValue = va_arg( ArgList, const char * ); + + if( ArgName != NULL /*&& ArgValue != NULL */ ) { + node = ixmlNode_getFirstChild( ( IXML_Node * ) PropSet ); + Ele1 = ixmlDocument_createElement( PropSet, "e:property" ); + Ele = ixmlDocument_createElement( PropSet, ArgName ); + if( ArgValue ) { + Txt = ixmlDocument_createTextNode( PropSet, ArgValue ); + ixmlNode_appendChild( ( IXML_Node * ) Ele, Txt ); + } + + ixmlNode_appendChild( ( IXML_Node * ) Ele1, + ( IXML_Node * ) Ele ); + ixmlNode_appendChild( node, ( IXML_Node * ) Ele1 ); + } + + ArgName = va_arg( ArgList, const char * ); + + } + va_end( ArgList ); + return PropSet; +} + +#endif diff --git a/libupnp/upnp/src/gena/gena_callback2.c b/libupnp/upnp/src/gena/gena_callback2.c new file mode 100644 index 0000000..4ecc152 --- /dev/null +++ b/libupnp/upnp/src/gena/gena_callback2.c @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#if EXCLUDE_GENA == 0 +#include "gena.h" +#include "gena_device.h" +#include "gena_ctrlpt.h" + +#include "httpparser.h" +#include "httpreadwrite.h" +#include "statcodes.h" +#include "unixutil.h" + +/************************************************************************ +* Function : error_respond +* +* Parameters: +* IN SOCKINFO *info: Structure containing information about the socket +* IN int error_code: error code that will be in the GENA response +* IN http_message_t* hmsg: GENA request Packet +* +* Description: +* This function send an error message to the control point in the case +* incorrect GENA requests. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +void +error_respond( IN SOCKINFO * info, + IN int error_code, + IN http_message_t * hmsg ) +{ + int major, + minor; + + // retrieve the minor and major version from the GENA request + http_CalcResponseVersion( hmsg->major_version, + hmsg->minor_version, &major, &minor ); + + http_SendStatusResponse( info, error_code, major, minor ); +} + +/************************************************************************ +* Function : genaCallback +* +* Parameters: +* IN http_parser_t *parser: represents the parse state of the request +* IN http_message_t* request: HTTP message containing GENA request +* INOUT SOCKINFO *info: Structure containing information about the socket +* +* Description: +* This is the callback function called by the miniserver to handle +* incoming GENA requests. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +void +genaCallback( IN http_parser_t * parser, + IN http_message_t * request, + INOUT SOCKINFO * info ) +{ + xboolean found_function = FALSE; + + if( request->method == HTTPMETHOD_SUBSCRIBE ) { + DEVICEONLY( found_function = TRUE; + if( httpmsg_find_hdr( request, HDR_NT, NULL ) == NULL ) + { + // renew subscription + gena_process_subscription_renewal_request + ( info, request );} + else + { + // subscribe + gena_process_subscription_request( info, request );} + + DBGONLY( UpnpPrintf + ( UPNP_ALL, GENA, __FILE__, __LINE__, + "got subscription request\n" ); ) + ) + } + else + if( request->method == HTTPMETHOD_UNSUBSCRIBE ) { + DEVICEONLY( found_function = TRUE; + // unsubscribe + gena_process_unsubscribe_request( info, + request ); ) + } else if( request->method == HTTPMETHOD_NOTIFY ) { + CLIENTONLY( found_function = TRUE; + // notify + gena_process_notification_event( info, request ); ) + } + + if( !found_function ) { + // handle missing functions of device or ctrl pt + error_respond( info, HTTP_NOT_IMPLEMENTED, request ); + } + } +#endif // EXCLUDE_GENA diff --git a/libupnp/upnp/src/gena/gena_ctrlpt.c b/libupnp/upnp/src/gena/gena_ctrlpt.c new file mode 100644 index 0000000..0a2bbf8 --- /dev/null +++ b/libupnp/upnp/src/gena/gena_ctrlpt.c @@ -0,0 +1,875 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#if EXCLUDE_GENA == 0 +#ifdef INCLUDE_CLIENT_APIS + +#include "gena.h" +#include "sysdep.h" +#include "uuid.h" +#include "upnpapi.h" +#include "parsetools.h" +#include "statcodes.h" +#include "httpparser.h" +#include "httpreadwrite.h" + +extern ithread_mutex_t GlobalClientSubscribeMutex; + +/************************************************************************ +* Function : GenaAutoRenewSubscription +* +* Parameters: +* IN void *input: Thread data(upnp_timeout *) needed to send the renewal +* +* Description: +* This is a thread function to send the renewal just before the +* subscription times out. +* +* Returns: VOID +* +***************************************************************************/ +static void +GenaAutoRenewSubscription( IN void *input ) +{ + upnp_timeout *event = ( upnp_timeout * ) input; + void *cookie; + Upnp_FunPtr callback_fun; + struct Handle_Info *handle_info; + struct Upnp_Event_Subscribe *sub_struct = + ( struct Upnp_Event_Subscribe * ) + event->Event; + + int send_callback = 0; + int eventType = 0; + + if( AUTO_RENEW_TIME == 0 ) { + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENA SUB EXPIRED" ) ); + sub_struct->ErrCode = UPNP_E_SUCCESS; + send_callback = 1; + eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED; + } else { + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENA AUTO RENEW" ) ); + if( ( ( sub_struct->ErrCode = genaRenewSubscription( event->handle, + sub_struct-> + Sid, + &sub_struct-> + TimeOut ) ) != + UPNP_E_SUCCESS ) + && ( sub_struct->ErrCode != GENA_E_BAD_SID ) + && ( sub_struct->ErrCode != GENA_E_BAD_HANDLE ) ) { + send_callback = 1; + eventType = UPNP_EVENT_AUTORENEWAL_FAILED; + } + } + if( send_callback ) { + HandleLock( ); + if( GetHandleInfo( event->handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + free_upnp_timeout( event ); + return; + } + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "HANDLE IS VALID" ) ); + callback_fun = handle_info->Callback; + cookie = handle_info->Cookie; + HandleUnlock( ); + //make callback + + callback_fun( eventType, event->Event, cookie ); + } + + free_upnp_timeout( event ); +} + +/************************************************************************ +* Function : ScheduleGenaAutoRenew +* +* Parameters: +* IN int client_handle: Handle that also contains the subscription list +* IN int TimeOut: The time out value of the subscription +* IN client_subscription * sub: Subscription being renewed +* +* Description: +* This function schedules a job to renew the subscription just before +* time out. +* +* Returns: int +* return GENA_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +static int +ScheduleGenaAutoRenew( IN int client_handle, + IN int TimeOut, + IN client_subscription * sub ) +{ + struct Upnp_Event_Subscribe *RenewEventStruct = NULL; + upnp_timeout *RenewEvent = NULL; + int return_code = GENA_SUCCESS; + ThreadPoolJob job; + + if( TimeOut == UPNP_INFINITE ) { + return GENA_SUCCESS; + } + + RenewEventStruct = ( struct Upnp_Event_Subscribe * )malloc( sizeof + ( struct + Upnp_Event_Subscribe ) ); + + if( RenewEventStruct == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + RenewEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) ); + + if( RenewEvent == NULL ) { + free( RenewEventStruct ); + return UPNP_E_OUTOF_MEMORY; + } + //schedule expire event + strcpy( RenewEventStruct->Sid, sub->sid ); + RenewEventStruct->ErrCode = UPNP_E_SUCCESS; + strncpy( RenewEventStruct->PublisherUrl, sub->EventURL, + NAME_SIZE - 1 ); + RenewEventStruct->TimeOut = TimeOut; + + //RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE; + RenewEvent->handle = client_handle; + RenewEvent->Event = RenewEventStruct; + + TPJobInit( &job, ( start_routine ) GenaAutoRenewSubscription, + RenewEvent ); + TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout ); + TPJobSetPriority( &job, MED_PRIORITY ); + + //Schedule the job + if( ( return_code = TimerThreadSchedule( &gTimerThread, + TimeOut - AUTO_RENEW_TIME, + REL_SEC, &job, SHORT_TERM, + &( RenewEvent-> + eventId ) ) ) != + UPNP_E_SUCCESS ) { + free( RenewEvent ); + free( RenewEventStruct ); + return return_code; + } + + sub->RenewEventId = RenewEvent->eventId; + return GENA_SUCCESS; +} + +/************************************************************************ +* Function : gena_unsubscribe +* +* Parameters: +* IN char *url: Event URL of the service +* IN char *sid: The subcription ID. +* OUT http_parser_t* response: The UNSUBCRIBE response from the device +* +* Description: +* This function sends the UNSUBCRIBE gena request and recieves the +* response from the device and returns it as a parameter +* +* Returns: int +* return 0 if successful else returns appropriate error +***************************************************************************/ +static int +gena_unsubscribe( IN char *url, + IN char *sid, + OUT http_parser_t * response ) +{ + int return_code; + uri_type dest_url; + membuffer request; + + // parse url + return_code = http_FixStrUrl( url, strlen( url ), &dest_url ); + if( return_code != 0 ) { + return return_code; + } + // make request msg + membuffer_init( &request ); + request.size_inc = 30; + return_code = http_MakeMessage( &request, 1, 1, + "q" "ssc" "U" "c", + HTTPMETHOD_UNSUBSCRIBE, &dest_url, + "SID: ", sid ); + + //Not able to make the message so destroy the existing buffer + if( return_code != 0 ) { + membuffer_destroy( &request ); + return return_code; + } + // send request and get reply + return_code = http_RequestAndResponse( &dest_url, request.buf, + request.length, + HTTPMETHOD_UNSUBSCRIBE, + HTTP_DEFAULT_TIMEOUT, + response ); + + membuffer_destroy( &request ); + + if( return_code != 0 ) + httpmsg_destroy( &response->msg ); + + if( return_code == 0 && response->msg.status_code != HTTP_OK ) { + return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED; + httpmsg_destroy( &response->msg ); + } + + return return_code; +} + +/************************************************************************ +* Function : gena_subscribe +* +* Parameters: +* IN char *url: url of service to subscribe +* INOUT int* timeout:subscription time desired (in secs) +* IN char* renewal_sid:for renewal, this contains a currently h +* held subscription SID. For first time +* subscription, this must be NULL +* OUT char** sid: SID returned by the subscription or renew msg +* +* Description: +* This function subscribes or renew subscription +* +* Returns: int +* return 0 if successful else returns appropriate error +***************************************************************************/ +static int +gena_subscribe( IN char *url, + INOUT int *timeout, + IN char *renewal_sid, + OUT char **sid ) +{ + int return_code; + memptr sid_hdr, + timeout_hdr; + char timeout_str[25]; + membuffer request; + uri_type dest_url; + http_parser_t response; + + *sid = NULL; // init + + // request timeout to string + if( ( timeout == NULL ) || + ( ( *timeout > 0 ) + && ( *timeout < CP_MINIMUM_SUBSCRIPTION_TIME ) ) ) { + sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME ); + } else if( *timeout >= 0 ) { + sprintf( timeout_str, "%d", *timeout ); + } else { + strcpy( timeout_str, "infinite" ); + } + + // parse url + return_code = http_FixStrUrl( url, strlen( url ), &dest_url ); + if( return_code != 0 ) { + return return_code; + } + // make request msg + membuffer_init( &request ); + request.size_inc = 30; + if( renewal_sid ) { + // renew subscription + return_code = http_MakeMessage( &request, 1, 1, + "q" "ssc" "ssc" "c", + HTTPMETHOD_SUBSCRIBE, &dest_url, + "SID: ", renewal_sid, + "TIMEOUT: Second-", timeout_str ); + } else { + // subscribe + return_code = http_MakeMessage( &request, 1, 1, + "q" "sssdsscc", + HTTPMETHOD_SUBSCRIBE, &dest_url, + "CALLBACK: \r\n" "NT: upnp:event\r\n" + "TIMEOUT: Second-", timeout_str ); + } + if( return_code != 0 ) { + return return_code; + } + // send request and get reply + return_code = http_RequestAndResponse( &dest_url, request.buf, + request.length, + HTTPMETHOD_SUBSCRIBE, + HTTP_DEFAULT_TIMEOUT, + &response ); + + membuffer_destroy( &request ); + + if( return_code != 0 ) { + httpmsg_destroy( &response.msg ); + return return_code; + } + if( response.msg.status_code != HTTP_OK ) { + httpmsg_destroy( &response.msg ); + return UPNP_E_SUBSCRIBE_UNACCEPTED; + } + // get SID and TIMEOUT + if( httpmsg_find_hdr( &response.msg, HDR_SID, &sid_hdr ) == NULL || + sid_hdr.length == 0 || + httpmsg_find_hdr( &response.msg, + HDR_TIMEOUT, &timeout_hdr ) == NULL || + timeout_hdr.length == 0 ) { + httpmsg_destroy( &response.msg ); + return UPNP_E_BAD_RESPONSE; + } + // save timeout + if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", + timeout ) == PARSE_OK ) { + // nothing + } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { + *timeout = -1; + } else { + httpmsg_destroy( &response.msg ); + return UPNP_E_BAD_RESPONSE; + } + + // save SID + *sid = str_alloc( sid_hdr.buf, sid_hdr.length ); + if( *sid == NULL ) { + httpmsg_destroy( &response.msg ); + return UPNP_E_OUTOF_MEMORY; + } + + httpmsg_destroy( &response.msg ); + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : genaUnregisterClient +* +* Parameters: +* IN UpnpClient_Handle client_handle: Handle containing all the control +* point related information +* +* Description: +* This function unsubcribes all the outstanding subscriptions and cleans +* the subscription list. This function is called when control point +* unregisters. +* +* Returns: int +* return UPNP_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +int +genaUnregisterClient( IN UpnpClient_Handle client_handle ) +{ + client_subscription sub_copy; + int return_code = UPNP_E_SUCCESS; + struct Handle_Info *handle_info = NULL; + http_parser_t response; + + while( TRUE ) { + HandleLock( ); + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + if( handle_info->ClientSubList == NULL ) { + return_code = UPNP_E_SUCCESS; + break; + } + + return_code = copy_client_subscription( handle_info->ClientSubList, + &sub_copy ); + if( return_code != HTTP_SUCCESS ) { + break; + } + + RemoveClientSubClientSID( &handle_info->ClientSubList, + sub_copy.sid ); + + HandleUnlock( ); + + return_code = gena_unsubscribe( sub_copy.EventURL, + sub_copy.ActualSID, &response ); + if( return_code == 0 ) { + httpmsg_destroy( &response.msg ); + } + + free_client_subscription( &sub_copy ); + } + + freeClientSubList( handle_info->ClientSubList ); + HandleUnlock( ); + return return_code; +} + +/************************************************************************ +* Function : genaUnSubscribe +* +* Parameters: +* IN UpnpClient_Handle client_handle: UPnP client handle +* IN SID in_sid: The subscription ID +* +* Description: +* This function unsubscribes a SID. It first validates the SID and +* client_handle,copies the subscription, sends UNSUBSCRIBE http request +* to service processes request and finally removes the subscription +* +* Returns: int +* return UPNP_E_SUCCESS if service response is OK else +* returns appropriate error +***************************************************************************/ +int +genaUnSubscribe( IN UpnpClient_Handle client_handle, + IN const Upnp_SID in_sid ) +{ + client_subscription *sub; + int return_code = GENA_SUCCESS; + struct Handle_Info *handle_info; + client_subscription sub_copy; + http_parser_t response; + + HandleLock( ); + + // validate handle and sid + + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + if( ( sub = + GetClientSubClientSID( handle_info->ClientSubList, in_sid ) ) + == NULL ) { + HandleUnlock( ); + return GENA_E_BAD_SID; + } + + return_code = copy_client_subscription( sub, &sub_copy ); + + HandleUnlock( ); + + return_code = gena_unsubscribe( sub_copy.EventURL, sub_copy.ActualSID, + &response ); + + if( return_code == 0 ) { + httpmsg_destroy( &response.msg ); + } + + free_client_subscription( &sub_copy ); + + HandleLock( ); + + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid ); + + HandleUnlock( ); + + return return_code; +} + +/************************************************************************ +* Function : genaSubscribe +* +* Parameters: +* IN UpnpClient_Handle client_handle: +* IN char * PublisherURL: NULL Terminated, of the form : +* "http://134.134.156.80:4000/RedBulb/Event" +* INOUT int * TimeOut: requested Duration, if -1, then "infinite". +* in the OUT case: actual Duration granted +* by Service, -1 for infinite +* OUT Upnp_SID out_sid:sid of subscription, memory passed in by caller +* +* Description: +* This function subscribes to a PublisherURL ( also mentioned as EventURL +* some places). It sends SUBSCRIBE http request to service processes +* request. Finally adds a Subscription to +* the clients subscription list, if service responds with OK +* +* Returns: int +* return UPNP_E_SUCCESS if service response is OK else +* returns appropriate error +***************************************************************************/ +int +genaSubscribe( IN UpnpClient_Handle client_handle, + IN char *PublisherURL, + INOUT int *TimeOut, + OUT Upnp_SID out_sid ) +{ + int return_code = GENA_SUCCESS; + client_subscription *newSubscription = NULL; + uuid_upnp uid; + Upnp_SID temp_sid; + char *ActualSID = NULL; + struct Handle_Info *handle_info; + char *EventURL = NULL; + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENA SUBSCRIBE BEGIN" ) ); + HandleLock( ); + + memset( out_sid, 0, sizeof( Upnp_SID ) ); + + // validate handle + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + HandleUnlock( ); + + // subscribe + SubscribeLock( ); + return_code = + gena_subscribe( PublisherURL, TimeOut, NULL, &ActualSID ); + HandleLock( ); + if( return_code != UPNP_E_SUCCESS ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__, + "SUBSCRIBE FAILED in transfer error code: %d returned\n", + return_code ) ); + goto error_handler; + } + + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + return_code = GENA_E_BAD_HANDLE; + goto error_handler; + } + // generate client SID + uuid_create( &uid ); + uuid_unpack( &uid, temp_sid ); + sprintf( out_sid, "uuid:%s", temp_sid ); + + // create event url + EventURL = ( char * )malloc( strlen( PublisherURL ) + 1 ); + if( EventURL == NULL ) { + return_code = UPNP_E_OUTOF_MEMORY; + goto error_handler; + } + + strcpy( EventURL, PublisherURL ); + + // fill subscription + newSubscription = + ( client_subscription * ) malloc( sizeof( client_subscription ) ); + if( newSubscription == NULL ) { + return_code = UPNP_E_OUTOF_MEMORY; + goto error_handler; + } + newSubscription->EventURL = EventURL; + newSubscription->ActualSID = ActualSID; + strcpy( newSubscription->sid, out_sid ); + newSubscription->RenewEventId = -1; + newSubscription->next = handle_info->ClientSubList; + handle_info->ClientSubList = newSubscription; + + // schedule expiration event + return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut, + newSubscription ); + + error_handler: + if( return_code != UPNP_E_SUCCESS ) { + free( ActualSID ); + free( EventURL ); + free( newSubscription ); + } + HandleUnlock( ); + SubscribeUnlock( ); + return return_code; +} + +/************************************************************************ +* Function : genaRenewSubscription +* +* Parameters: +* IN UpnpClient_Handle client_handle: Client handle +* IN const Upnp_SID in_sid: subscription ID +* INOUT int * TimeOut: requested Duration, if -1, then "infinite". +* in the OUT case: actual Duration granted +* by Service, -1 for infinite +* +* Description: +* This function renews a SID. It first validates the SID and +* client_handle and copies the subscription. It sends RENEW +* (modified SUBSCRIBE) http request to service and processes +* the response. +* +* Returns: int +* return UPNP_E_SUCCESS if service response is OK else +* returns appropriate error +***************************************************************************/ +int +genaRenewSubscription( IN UpnpClient_Handle client_handle, + IN const Upnp_SID in_sid, + INOUT int *TimeOut ) +{ + int return_code = GENA_SUCCESS; + client_subscription *sub; + client_subscription sub_copy; + struct Handle_Info *handle_info; + + char *ActualSID; + ThreadPoolJob tempJob; + + HandleLock( ); + + // validate handle and sid + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + if( ( sub = GetClientSubClientSID( handle_info->ClientSubList, + in_sid ) ) == NULL ) { + HandleUnlock( ); + return GENA_E_BAD_SID; + } + // remove old events + if( TimerThreadRemove( &gTimerThread, sub->RenewEventId, &tempJob ) == + 0 ) { + + free_upnp_timeout( ( upnp_timeout * ) tempJob.arg ); + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "REMOVED AUTO RENEW EVENT" ) ); + + sub->RenewEventId = -1; + return_code = copy_client_subscription( sub, &sub_copy ); + + HandleUnlock( ); + + if( return_code != HTTP_SUCCESS ) { + return return_code; + } + + return_code = gena_subscribe( sub_copy.EventURL, TimeOut, + sub_copy.ActualSID, &ActualSID ); + HandleLock( ); + + if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { + HandleUnlock( ); + if( return_code == UPNP_E_SUCCESS ) { + free( ActualSID ); + } + return GENA_E_BAD_HANDLE; + } + // we just called GetHandleInfo, so we don't check for return value + //GetHandleInfo(client_handle, &handle_info); + + if( return_code != UPNP_E_SUCCESS ) { + // network failure (remove client sub) + RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid ); + free_client_subscription( &sub_copy ); + HandleUnlock( ); + return return_code; + } + // get subscription + if( ( sub = GetClientSubClientSID( handle_info->ClientSubList, + in_sid ) ) == NULL ) { + free( ActualSID ); + free_client_subscription( &sub_copy ); + HandleUnlock( ); + return GENA_E_BAD_SID; + } + // store actual sid + free( sub->ActualSID ); + sub->ActualSID = ActualSID; + + // start renew subscription timer + return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut, sub ); + if( return_code != GENA_SUCCESS ) { + RemoveClientSubClientSID( &handle_info->ClientSubList, sub->sid ); + } + free_client_subscription( &sub_copy ); + HandleUnlock( ); + return return_code; +} + +/************************************************************************ +* Function : gena_process_notification_event +* +* Parameters: +* IN SOCKINFO *info: Socket structure containing the device socket +* information +* IN http_message_t* event: The http message contains the GENA +* notification +* +* Description: +* This function processes NOTIFY events that are sent by devices. +* called by genacallback() +* +* Returns: void +* +* Note : called by genacallback() +****************************************************************************/ +void +gena_process_notification_event( IN SOCKINFO * info, + IN http_message_t * event ) +{ + struct Upnp_Event event_struct; + int eventKey; + token sid; + client_subscription *subscription; + IXML_Document *ChangedVars; + struct Handle_Info *handle_info; + void *cookie; + Upnp_FunPtr callback; + UpnpClient_Handle client_handle; + + memptr sid_hdr; + memptr nt_hdr, + nts_hdr; + memptr seq_hdr; + + // get SID + if( httpmsg_find_hdr( event, HDR_SID, &sid_hdr ) == NULL ) { + error_respond( info, HTTP_PRECONDITION_FAILED, event ); + + return; + } + sid.buff = sid_hdr.buf; + sid.size = sid_hdr.length; + + // get event key + if( httpmsg_find_hdr( event, HDR_SEQ, &seq_hdr ) == NULL || + matchstr( seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey ) + != PARSE_OK ) { + error_respond( info, HTTP_BAD_REQUEST, event ); + + return; + } + // get NT and NTS headers + if( httpmsg_find_hdr( event, HDR_NT, &nt_hdr ) == NULL || + httpmsg_find_hdr( event, HDR_NTS, &nts_hdr ) == NULL ) { + error_respond( info, HTTP_BAD_REQUEST, event ); + + return; + } + // verify NT and NTS headers + if( memptr_cmp( &nt_hdr, "upnp:event" ) != 0 || + memptr_cmp( &nts_hdr, "upnp:propchange" ) != 0 ) { + error_respond( info, HTTP_PRECONDITION_FAILED, event ); + + return; + } + // parse the content (should be XML) + if( !has_xml_content_type( event ) || + event->msg.length == 0 || + ( ixmlParseBufferEx( event->entity.buf, &ChangedVars ) ) != + IXML_SUCCESS ) { + error_respond( info, HTTP_BAD_REQUEST, event ); + + return; + } + + HandleLock( ); + + // get client info + if( GetClientHandleInfo( &client_handle, &handle_info ) != HND_CLIENT ) { + error_respond( info, HTTP_PRECONDITION_FAILED, event ); + HandleUnlock( ); + ixmlDocument_free( ChangedVars ); + + return; + } + // get subscription based on SID + if( ( subscription = GetClientSubActualSID( handle_info->ClientSubList, + &sid ) ) == NULL ) { + if( eventKey == 0 ) { + // wait until we've finished processing a subscription + // (if we are in the middle) + // this is to avoid mistakenly rejecting the first event if we + // receive it before the subscription response + HandleUnlock( ); + + // try and get Subscription Lock + // (in case we are in the process of subscribing) + SubscribeLock( ); + + // get HandleLock again + HandleLock( ); + + if( GetClientHandleInfo( &client_handle, &handle_info ) + != HND_CLIENT ) { + error_respond( info, HTTP_PRECONDITION_FAILED, event ); + SubscribeUnlock( ); + HandleUnlock( ); + ixmlDocument_free( ChangedVars ); + + return; + } + + if( ( subscription = + GetClientSubActualSID( handle_info->ClientSubList, + &sid ) ) == NULL ) { + error_respond( info, HTTP_PRECONDITION_FAILED, event ); + SubscribeUnlock( ); + HandleUnlock( ); + ixmlDocument_free( ChangedVars ); + + return; + } + + SubscribeUnlock( ); + } else { + error_respond( info, HTTP_PRECONDITION_FAILED, event ); + HandleUnlock( ); + ixmlDocument_free( ChangedVars ); + + return; + } + } + + error_respond( info, HTTP_OK, event ); // success + + // fill event struct + strcpy( event_struct.Sid, subscription->sid ); + event_struct.EventKey = eventKey; + event_struct.ChangedVariables = ChangedVars; + + // copy callback + callback = handle_info->Callback; + cookie = handle_info->Cookie; + + HandleUnlock( ); + + // make callback with event struct + // In future, should find a way of mainting + // that the handle is not unregistered in the middle of a + // callback + callback( UPNP_EVENT_RECEIVED, &event_struct, cookie ); + + ixmlDocument_free( ChangedVars ); +} + +#endif // INCLUDE_CLIENT_APIS +#endif // EXCLUDE_GENA diff --git a/libupnp/upnp/src/gena/gena_device.c b/libupnp/upnp/src/gena/gena_device.c new file mode 100644 index 0000000..ea2c866 --- /dev/null +++ b/libupnp/upnp/src/gena/gena_device.c @@ -0,0 +1,1676 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#if EXCLUDE_GENA == 0 +#ifdef INCLUDE_DEVICE_APIS + +#include "gena.h" +#include "sysdep.h" +#include "uuid.h" +#include "upnpapi.h" +#include "parsetools.h" +#include "statcodes.h" +#include "httpparser.h" +#include "httpreadwrite.h" + +#include "unixutil.h" + +/************************************************************************ +* Function : genaUnregisterDevice +* +* Parameters: +* IN UpnpDevice_Handle device_handle: Handle of the root device +* +* Description: +* This function cleans the service table of the device. +* +* Returns: int +* returns UPNP_E_SUCCESS if successful else returns GENA_E_BAD_HANDLE +****************************************************************************/ +int +genaUnregisterDevice( IN UpnpDevice_Handle device_handle ) +{ + struct Handle_Info *handle_info; + + HandleLock( ); + if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) { + + DBGONLY( UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__, + "genaUnregisterDevice : BAD Handle : %d\n", + device_handle ) ); + + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + freeServiceTable( &handle_info->ServiceTable ); + HandleUnlock( ); + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : GeneratePropertySet +* +* Parameters: +* IN char **names : Array of variable names (go in the event notify) +* IN char ** values : Array of variable values (go in the event notify) +* IN int count : number of variables +* OUT DOMString *out: PropertySet node in the string format +* +* Description: +* This function to generate XML propery Set for notifications +* +* Returns: int +* returns UPNP_E_SUCCESS if successful else returns GENA_E_BAD_HANDLE +* +* Note: XML_VERSION comment is NOT sent due to interop issues with other +* UPnP vendors +****************************************************************************/ +static int +GeneratePropertySet( IN char **names, + IN char **values, + IN int count, + OUT DOMString * out ) +{ + char *buffer; + int counter = 0; + int size = 0; + int temp_counter = 0; + + //size+=strlen(XML_VERSION); the XML_VERSION is not interopeable with + //other vendors + size += strlen( XML_PROPERTYSET_HEADER ); + size += strlen( "\n\n" ); + + for( temp_counter = 0, counter = 0; counter < count; counter++ ) { + size += strlen( "\n\n" ); + size += + ( 2 * strlen( names[counter] ) + strlen( values[counter] ) + + ( strlen( "<>\n" ) ) ); + + } + buffer = ( char * )malloc( size + 1 ); + + if( buffer == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + memset( buffer, 0, size + 1 ); + + //strcpy(buffer,XML_VERSION); the XML_VERSION is not interopeable with + //other vendors + strcpy( buffer, XML_PROPERTYSET_HEADER ); + + for( counter = 0; counter < count; counter++ ) { + strcat( buffer, "\n" ); + sprintf( &buffer[strlen( buffer )], + "<%s>%s\n\n", names[counter], + values[counter], names[counter] ); + } + strcat( buffer, "\n\n" ); + ( *out ) = ixmlCloneDOMString( buffer ); + free( buffer ); + return XML_SUCCESS; +} + +/************************************************************************ +* Function : free_notify_struct +* +* Parameters: +* IN notify_thread_struct * input : Notify structure +* +* Description: +* This function frees memory used in notify_threads if the reference +* count is 0 otherwise decrements the refrence count +* +* Returns: VOID +* +****************************************************************************/ +static void +free_notify_struct( IN notify_thread_struct * input ) +{ + ( *input->reference_count )--; + if( ( *input->reference_count ) == 0 ) { + free( input->headers ); + ixmlFreeDOMString( input->propertySet ); + free( input->servId ); + free( input->UDN ); + free( input->reference_count ); + } + free( input ); +} + +/**************************************************************************** +* Function : notify_send_and_recv +* +* Parameters : +* IN uri_type* destination_url : subscription callback URL +* (URL of the control point) +* IN membuffer* mid_msg : Common HTTP headers +* IN char* propertySet : The evented XML +* OUT http_parser_t* response : The response from the control point. +* +* Description : This function sends the notify message and returns a +* reply. +* +* Return : int +* on success: returns UPNP_E_SUCCESS; else returns a UPNP error +* +* Note : called by genaNotify +****************************************************************************/ +static XINLINE int +notify_send_and_recv( IN uri_type * destination_url, + IN membuffer * mid_msg, + IN char *propertySet, + OUT http_parser_t * response ) +{ + uri_type url; + int conn_fd; + membuffer start_msg; + int ret_code; + int err_code; + int timeout; + SOCKINFO info; + + // connect + DBGONLY( UpnpPrintf( UPNP_ALL, GENA, __FILE__, __LINE__, + "gena notify to: %.*s\n", + destination_url->hostport.text.size, + destination_url->hostport.text.buff ); ) + + conn_fd = http_Connect( destination_url, &url ); + if( conn_fd < 0 ) { + return conn_fd; // return UPNP error + } + + if( ( ret_code = sock_init( &info, conn_fd ) ) != 0 ) { + sock_destroy( &info, SD_BOTH ); + return ret_code; + } + // make start line and HOST header + membuffer_init( &start_msg ); + if( http_MakeMessage( &start_msg, 1, 1, + "q" "s", + HTTPMETHOD_NOTIFY, &url, mid_msg->buf ) != 0 ) { + membuffer_destroy( &start_msg ); + sock_destroy( &info, SD_BOTH ); + return UPNP_E_OUTOF_MEMORY; + } + + timeout = HTTP_DEFAULT_TIMEOUT; + + // send msg (note +1 for propertyset; null-terminator is also sent) + if( ( ret_code = http_SendMessage( &info, &timeout, + "bb", + start_msg.buf, start_msg.length, + propertySet, + strlen( propertySet ) + 1 ) ) != + 0 ) { + membuffer_destroy( &start_msg ); + sock_destroy( &info, SD_BOTH ); + return ret_code; + } + + if( ( ret_code = http_RecvMessage( &info, response, + HTTPMETHOD_NOTIFY, &timeout, + &err_code ) ) != 0 ) { + membuffer_destroy( &start_msg ); + sock_destroy( &info, SD_BOTH ); + httpmsg_destroy( &response->msg ); + return ret_code; + } + + sock_destroy( &info, SD_BOTH ); //should shutdown completely + //when closing socket + // sock_destroy( &info,SD_RECEIVE); + membuffer_destroy( &start_msg ); + + return UPNP_E_SUCCESS; +} + +/**************************************************************************** +* Function : genaNotify +* +* Parameters : +* IN char *headers : (null terminated) (includes all headers +* (including \r\n) except SID and SEQ) +* IN char *propertySet : The evented XML +* IN subscription* sub : subscription to be Notified, +* Assumes this is valid for life of function) +* +* Description : Function to Notify a particular subscription of a +* particular event. In general the service should NOT be +* blocked around this call. (this may cause deadlock +* with a client) NOTIFY http request is sent and the +* reply is processed. +* +* Return : int +* GENA_SUCCESS if the event was delivered else returns appropriate +* error +* +* Note : +****************************************************************************/ +int +genaNotify( IN char *headers, + IN char *propertySet, + IN subscription * sub ) +{ + int i; + membuffer mid_msg; + membuffer endmsg; + uri_type *url; + http_parser_t response; + int return_code = -1; + + membuffer_init( &mid_msg ); + + // make 'end' msg (the part that won't vary with the destination) + endmsg.size_inc = 30; + if( http_MakeMessage( &mid_msg, 1, 1, + "s" "ssc" "sdcc", + headers, + "SID: ", sub->sid, + "SEQ: ", sub->ToSendEventKey ) != 0 ) { + membuffer_destroy( &mid_msg ); + return UPNP_E_OUTOF_MEMORY; + } + // send a notify to each url until one goes thru + for( i = 0; i < sub->DeliveryURLs.size; i++ ) { + url = &sub->DeliveryURLs.parsedURLs[i]; + + if( ( return_code = notify_send_and_recv( url, + &mid_msg, propertySet, + &response ) ) == + UPNP_E_SUCCESS ) { + break; + } + } + + membuffer_destroy( &mid_msg ); + + if( return_code == UPNP_E_SUCCESS ) { + if( response.msg.status_code == HTTP_OK ) { + return_code = GENA_SUCCESS; + } else { + if( response.msg.status_code == HTTP_PRECONDITION_FAILED ) { + //Invalid SID gets removed + return_code = GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB; + } else { + return_code = GENA_E_NOTIFY_UNACCEPTED; + } + } + httpmsg_destroy( &response.msg ); + } + + return return_code; +} + +/**************************************************************************** +* Function : genaNotifyThread +* +* Parameters : +* IN void * input : notify thread structure containing all the +* headers and property set info +* +* Description : Thread job to Notify a control point. It validates the +* subscription and copies the subscription. Also make sure that +* events are sent in order. +* +* Return : void +* +* Note : calls the genaNotify to do the actual work +****************************************************************************/ +static void +genaNotifyThread( IN void *input ) +{ + + subscription *sub; + service_info *service; + subscription sub_copy; + notify_thread_struct *in = ( notify_thread_struct * ) input; + int return_code; + struct Handle_Info *handle_info; + ThreadPoolJob job; + + HandleLock( ); + //validate context + + if( GetHandleInfo( in->device_handle, &handle_info ) != HND_DEVICE ) { + free_notify_struct( in ); + HandleUnlock( ); + return; + } + + if( ( ( service = FindServiceId( &handle_info->ServiceTable, + in->servId, in->UDN ) ) == NULL ) + || ( !service->active ) + || ( ( sub = GetSubscriptionSID( in->sid, service ) ) == NULL ) + || ( ( copy_subscription( sub, &sub_copy ) != HTTP_SUCCESS ) ) ) { + free_notify_struct( in ); + HandleUnlock( ); + return; + } + //If the event is out of order push it back to the job queue + if( in->eventKey != sub->ToSendEventKey ) { + + TPJobInit( &job, ( start_routine ) genaNotifyThread, input ); + TPJobSetFreeFunction( &job, ( free_function ) free_notify_struct ); + TPJobSetPriority( &job, MED_PRIORITY ); + ThreadPoolAdd( &gSendThreadPool, &job, NULL ); + + freeSubscription( &sub_copy ); + HandleUnlock( ); + return; + } + + HandleUnlock( ); + + //send the notify + return_code = genaNotify( in->headers, in->propertySet, &sub_copy ); + + freeSubscription( &sub_copy ); + + HandleLock( ); + + if( GetHandleInfo( in->device_handle, &handle_info ) != HND_DEVICE ) { + free_notify_struct( in ); + HandleUnlock( ); + return; + } + //validate context + if( ( ( service = FindServiceId( &handle_info->ServiceTable, + in->servId, in->UDN ) ) == NULL ) + || ( !service->active ) + || ( ( sub = GetSubscriptionSID( in->sid, service ) ) == NULL ) ) { + free_notify_struct( in ); + HandleUnlock( ); + return; + } + + sub->ToSendEventKey++; + + if( sub->ToSendEventKey < 0 ) //wrap to 1 for overflow + sub->ToSendEventKey = 1; + + if( return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB ) { + RemoveSubscriptionSID( in->sid, service ); + } + + free_notify_struct( in ); + HandleUnlock( ); +} + +/**************************************************************************** +* Function : genaInitNotify +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN char **VarNames : Array of variable names +* IN char **VarValues : Array of variable values +* IN int var_count : array size +* IN Upnp_SID sid : subscription ID +* +* Description : This function sends the intial state table dump to +* newly subscribed control point. +* +* Return : int +* returns GENA_E_SUCCESS if successful else returns appropriate error +* +* Note : No other event will be sent to this control point before the +* intial state table dump. +****************************************************************************/ +int +genaInitNotify( IN UpnpDevice_Handle device_handle, + IN char *UDN, + IN char *servId, + IN char **VarNames, + IN char **VarValues, + IN int var_count, + IN Upnp_SID sid ) +{ + char *UDN_copy = NULL; + char *servId_copy = NULL; + char *propertySet = NULL; + char *headers = NULL; + subscription *sub = NULL; + service_info *service = NULL; + int return_code = GENA_SUCCESS; + int headers_size; + int *reference_count = NULL; + struct Handle_Info *handle_info; + ThreadPoolJob job; + + notify_thread_struct *thread_struct = NULL; + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENA BEGIN INITIAL NOTIFY " ) ); + + reference_count = ( int * )malloc( sizeof( int ) ); + + if( reference_count == NULL ) + return UPNP_E_OUTOF_MEMORY; + + ( *reference_count ) = 0; + + UDN_copy = ( char * )malloc( strlen( UDN ) + 1 ); + + if( UDN_copy == NULL ) { + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + servId_copy = ( char * )malloc( strlen( servId ) + 1 ); + + if( servId_copy == NULL ) { + free( UDN_copy ); + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + strcpy( UDN_copy, UDN ); + strcpy( servId_copy, servId ); + + HandleLock( ); + + if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + if( ( service = FindServiceId( &handle_info->ServiceTable, + servId, UDN ) ) == NULL ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return GENA_E_BAD_SERVICE; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "FOUND SERVICE IN INIT NOTFY: UDN %s, ServID: %s ", + UDN, servId ) ); + + if( ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) || + ( sub->active ) ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return GENA_E_BAD_SID; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "FOUND SUBSCRIPTION IN INIT NOTIFY: SID %s ", + sid ) ); + + sub->active = 1; + + if( ( return_code = GeneratePropertySet( VarNames, VarValues, + var_count, + &propertySet ) ) != + XML_SUCCESS ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return return_code; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENERATED PROPERY SET IN INIT NOTIFY: \n'%s'\n", + propertySet ) ); + + headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) + + strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH + + strlen( "NT: upnp:event\r\n" ) + + strlen( "NTS: upnp:propchange\r\n" ) + 1; + + headers = ( char * )malloc( headers_size ); + + if( headers == NULL ) { + ixmlFreeDOMString( propertySet ); + free( UDN_copy ); + free( servId_copy ); + free( reference_count ); + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + + sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: " + "%d\r\nNT: upnp:event\r\nNTS: upnp:propchange\r\n", + strlen( propertySet ) + 1 ); + + //schedule thread for initial notification + + thread_struct = + ( notify_thread_struct * ) + malloc( sizeof( notify_thread_struct ) ); + + if( thread_struct == NULL ) { + return_code = UPNP_E_OUTOF_MEMORY; + } else { + ( *reference_count ) = 1; + thread_struct->servId = servId_copy; + thread_struct->UDN = UDN_copy; + thread_struct->headers = headers; + thread_struct->propertySet = propertySet; + strcpy( thread_struct->sid, sid ); + thread_struct->eventKey = sub->eventKey++; + thread_struct->reference_count = reference_count; + thread_struct->device_handle = device_handle; + + TPJobInit( &job, ( start_routine ) genaNotifyThread, + thread_struct ); + TPJobSetFreeFunction( &job, ( free_routine ) free_notify_struct ); + TPJobSetPriority( &job, MED_PRIORITY ); + + if( ( return_code = + ThreadPoolAdd( &gSendThreadPool, &job, NULL ) ) != 0 ) { + if( return_code == EOUTOFMEM ) { + return_code = UPNP_E_OUTOF_MEMORY; + } + } else { + return_code = GENA_SUCCESS; + } + } + + if( return_code != GENA_SUCCESS ) { + + free( reference_count ); + free( UDN_copy ); + free( servId_copy ); + free( thread_struct ); + ixmlFreeDOMString( propertySet ); + free( headers ); + } + + HandleUnlock( ); + + return return_code; +} + +/**************************************************************************** +* Function : genaInitNotifyExt +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN IXML_Document *PropSet : Document of the state table +* IN Upnp_SID sid : subscription ID +* +* Description : This function is similar to the genaInitNofity. The only +* difference is that it takes the xml document for the state table and +* sends the intial state table dump to newly subscribed control point. +* +* Return : int +* returns GENA_E_SUCCESS if successful else returns appropriate error +* +* Note : No other event will be sent to this control point before the +* intial state table dump. +****************************************************************************/ +int +genaInitNotifyExt( IN UpnpDevice_Handle device_handle, + IN char *UDN, + IN char *servId, + IN IXML_Document * PropSet, + IN Upnp_SID sid ) +{ + char *UDN_copy = NULL; + char *servId_copy = NULL; + char *headers = NULL; + subscription *sub = NULL; + service_info *service = NULL; + int return_code = GENA_SUCCESS; + int headers_size; + int *reference_count = NULL; + struct Handle_Info *handle_info; + DOMString propertySet = NULL; + + ThreadPoolJob job; + + notify_thread_struct *thread_struct = NULL; + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENA BEGIN INITIAL NOTIFY EXT" ) ); + reference_count = ( int * )malloc( sizeof( int ) ); + + if( reference_count == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + ( *reference_count ) = 0; + + UDN_copy = ( char * )malloc( strlen( UDN ) + 1 ); + if( UDN_copy == NULL ) { + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + servId_copy = ( char * )malloc( strlen( servId ) + 1 ); + if( servId_copy == NULL ) { + free( UDN_copy ); + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + strcpy( UDN_copy, UDN ); + strcpy( servId_copy, servId ); + + HandleLock( ); + + if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return GENA_E_BAD_HANDLE; + } + + if( ( service = FindServiceId( &handle_info->ServiceTable, + servId, UDN ) ) == NULL ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return GENA_E_BAD_SERVICE; + } + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "FOUND SERVICE IN INIT NOTFY EXT: UDN %s, ServID: %s\n", + UDN, servId ) ); + + if( ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) || + ( sub->active ) ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return GENA_E_BAD_SID; + } + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "FOUND SUBSCRIPTION IN INIT NOTIFY EXT: SID %s", + sid ) ); + + sub->active = 1; + + propertySet = ixmlPrintNode( ( IXML_Node * ) PropSet ); + if( propertySet == NULL ) { + free( UDN_copy ); + free( reference_count ); + free( servId_copy ); + HandleUnlock( ); + return UPNP_E_INVALID_PARAM; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "GENERATED PROPERY SET IN INIT EXT NOTIFY: %s", + propertySet ) ); + + headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) + + strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH + + strlen( "NT: upnp:event\r\n" ) + + strlen( "NTS: upnp:propchange\r\n" ) + 1; + + headers = ( char * )malloc( headers_size ); + if( headers == NULL ) { + free( UDN_copy ); + free( servId_copy ); + free( reference_count ); + ixmlFreeDOMString( propertySet ); + HandleUnlock( ); + return UPNP_E_OUTOF_MEMORY; + } + + sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: " + "%d\r\nNT: upnp:event\r\nNTS: upnp:propchange\r\n", + strlen( propertySet ) + 1 ); + + //schedule thread for initial notification + + thread_struct = + ( notify_thread_struct * ) + malloc( sizeof( notify_thread_struct ) ); + + if( thread_struct == NULL ) { + return_code = UPNP_E_OUTOF_MEMORY; + } else { + ( *reference_count ) = 1; + thread_struct->servId = servId_copy; + thread_struct->UDN = UDN_copy; + thread_struct->headers = headers; + thread_struct->propertySet = propertySet; + strcpy( thread_struct->sid, sid ); + thread_struct->eventKey = sub->eventKey++; + thread_struct->reference_count = reference_count; + thread_struct->device_handle = device_handle; + + TPJobInit( &job, ( start_routine ) genaNotifyThread, + thread_struct ); + TPJobSetFreeFunction( &job, ( free_routine ) free_notify_struct ); + TPJobSetPriority( &job, MED_PRIORITY ); + + if( ( return_code = + ThreadPoolAdd( &gSendThreadPool, &job, NULL ) ) != 0 ) { + if( return_code == EOUTOFMEM ) { + return_code = UPNP_E_OUTOF_MEMORY; + } + } else { + return_code = GENA_SUCCESS; + } + } + + if( return_code != GENA_SUCCESS ) { + ixmlFreeDOMString( propertySet ); + free( reference_count ); + free( UDN_copy ); + free( servId_copy ); + free( thread_struct ); + free( headers ); + } + HandleUnlock( ); + + return return_code; +} + +/**************************************************************************** +* Function : genaNotifyAllExt +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN IXML_Document *PropSet : XML document Event varible property set +* +* Description : This function sends a notification to all the subscribed +* control points +* +* Return : int +* +* Note : This function is similar to the genaNotifyAll. the only difference +* is it takes the document instead of event variable array +****************************************************************************/ +int +genaNotifyAllExt( IN UpnpDevice_Handle device_handle, + IN char *UDN, + IN char *servId, + IN IXML_Document * PropSet ) +{ + char *headers = NULL; + int headers_size; + int return_code = GENA_SUCCESS; + char *UDN_copy = NULL; + char *servId_copy = NULL; + int *reference_count = NULL; + struct Handle_Info *handle_info; + DOMString propertySet = NULL; + ThreadPoolJob job; + subscription *finger = NULL; + + notify_thread_struct *thread_struct = NULL; + + service_info *service = NULL; + + reference_count = ( int * )malloc( sizeof( int ) ); + + if( reference_count == NULL ) + return UPNP_E_OUTOF_MEMORY; + + ( *reference_count = 0 ); + + UDN_copy = ( char * )malloc( strlen( UDN ) + 1 ); + + if( UDN_copy == NULL ) { + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + servId_copy = ( char * )malloc( strlen( servId ) + 1 ); + + if( servId_copy == NULL ) { + free( UDN_copy ); + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + strcpy( UDN_copy, UDN ); + strcpy( servId_copy, servId ); + + propertySet = ixmlPrintNode( ( IXML_Node * ) PropSet ); + if( propertySet == NULL ) { + free( UDN_copy ); + free( servId_copy ); + free( reference_count ); + return UPNP_E_INVALID_PARAM; + } + + headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) + + strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH + + strlen( "NT: upnp:event\r\n" ) + + strlen( "NTS: upnp:propchange\r\n" ) + 1; + + headers = ( char * )malloc( headers_size ); + if( headers == NULL ) { + free( UDN_copy ); + free( servId_copy ); + ixmlFreeDOMString( propertySet ); + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + //changed to add null terminator at end of content + //content length = (length in bytes of property set) + null char + sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: " + "%d\r\nNT: upnp:event\r\nNTS: upnp:propchange\r\n", + strlen( propertySet ) + 1 ); + + HandleLock( ); + + if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) + return_code = GENA_E_BAD_HANDLE; + else { + if( ( service = FindServiceId( &handle_info->ServiceTable, + servId, UDN ) ) != NULL ) { + finger = GetFirstSubscription( service ); + + while( finger ) { + thread_struct = + ( notify_thread_struct * ) + malloc( sizeof( notify_thread_struct ) ); + if( thread_struct == NULL ) { + break; + return_code = UPNP_E_OUTOF_MEMORY; + } + + ( *reference_count )++; + thread_struct->reference_count = reference_count; + thread_struct->UDN = UDN_copy; + thread_struct->servId = servId_copy; + thread_struct->headers = headers; + thread_struct->propertySet = propertySet; + strcpy( thread_struct->sid, finger->sid ); + thread_struct->eventKey = finger->eventKey++; + thread_struct->device_handle = device_handle; + //if overflow, wrap to 1 + if( finger->eventKey < 0 ) { + finger->eventKey = 1; + } + + TPJobInit( &job, ( start_routine ) genaNotifyThread, + thread_struct ); + TPJobSetFreeFunction( &job, + ( free_routine ) + free_notify_struct ); + TPJobSetPriority( &job, MED_PRIORITY ); + if( ( return_code = ThreadPoolAdd( &gSendThreadPool, + &job, NULL ) ) != 0 ) { + if( return_code == EOUTOFMEM ) { + return_code = UPNP_E_OUTOF_MEMORY; + } + break; + } + + finger = GetNextSubscription( service, finger ); + } + } else + return_code = GENA_E_BAD_SERVICE; + } + + if( ( *reference_count ) == 0 ) { + free( reference_count ); + free( headers ); + ixmlFreeDOMString( propertySet ); + free( UDN_copy ); + free( servId_copy ); + } + + HandleUnlock( ); + + return return_code; +} + +/**************************************************************************** +* Function : genaNotifyAll +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN char **VarNames : array of varible names +* IN char **VarValues : array of variable values +* IN int var_count : number of variables +* +* Description : This function sends a notification to all the subscribed +* control points +* +* Return : int +* +* Note : This function is similar to the genaNotifyAllExt. The only difference +* is it takes event variable array instead of xml document. +****************************************************************************/ +int +genaNotifyAll( IN UpnpDevice_Handle device_handle, + IN char *UDN, + IN char *servId, + IN char **VarNames, + IN char **VarValues, + IN int var_count ) +{ + char *headers = NULL; + char *propertySet = NULL; + int headers_size; + int return_code = GENA_SUCCESS; + char *UDN_copy = NULL; + char *servId_copy = NULL; + int *reference_count = NULL; + struct Handle_Info *handle_info; + ThreadPoolJob job; + + subscription *finger = NULL; + + notify_thread_struct *thread_struct = NULL; + + service_info *service = NULL; + + reference_count = ( int * )malloc( sizeof( int ) ); + + if( reference_count == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + ( *reference_count = 0 ); + + UDN_copy = ( char * )malloc( strlen( UDN ) + 1 ); + + if( UDN_copy == NULL ) { + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + servId_copy = ( char * )malloc( strlen( servId ) + 1 ); + if( servId_copy == NULL ) { + free( UDN_copy ); + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + + strcpy( UDN_copy, UDN ); + strcpy( servId_copy, servId ); + + if( ( return_code = GeneratePropertySet( VarNames, VarValues, + var_count, + &propertySet ) ) != + XML_SUCCESS ) { + free( UDN_copy ); + free( servId_copy ); + free( reference_count ); + return return_code; + } + + headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) + + strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH + + strlen( "NT: upnp:event\r\n" ) + + strlen( "NTS: upnp:propchange\r\n" ) + 1; + + headers = ( char * )malloc( headers_size ); + if( headers == NULL ) { + free( UDN_copy ); + free( servId_copy ); + ixmlFreeDOMString( propertySet ); + free( reference_count ); + return UPNP_E_OUTOF_MEMORY; + } + //changed to add null terminator at end of content + //content length = (length in bytes of property set) + null char + sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\nNT:" + " upnp:event\r\nNTS: upnp:propchange\r\n", + strlen( propertySet ) + 1 ); + + HandleLock( ); + + if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) { + return_code = GENA_E_BAD_HANDLE; + } else { + if( ( service = FindServiceId( &handle_info->ServiceTable, + servId, UDN ) ) != NULL ) { + finger = GetFirstSubscription( service ); + + while( finger ) { + thread_struct = + ( notify_thread_struct * ) + malloc( sizeof( notify_thread_struct ) ); + if( thread_struct == NULL ) { + return_code = UPNP_E_OUTOF_MEMORY; + break; + } + ( *reference_count )++; + thread_struct->reference_count = reference_count; + thread_struct->UDN = UDN_copy; + thread_struct->servId = servId_copy; + thread_struct->headers = headers; + thread_struct->propertySet = propertySet; + strcpy( thread_struct->sid, finger->sid ); + thread_struct->eventKey = finger->eventKey++; + thread_struct->device_handle = device_handle; + //if overflow, wrap to 1 + if( finger->eventKey < 0 ) { + finger->eventKey = 1; + } + + TPJobInit( &job, ( start_routine ) genaNotifyThread, + thread_struct ); + TPJobSetFreeFunction( &job, + ( free_routine ) + free_notify_struct ); + TPJobSetPriority( &job, MED_PRIORITY ); + + if( ( return_code = + ThreadPoolAdd( &gSendThreadPool, &job, NULL ) ) + != 0 ) { + if( return_code == EOUTOFMEM ) { + return_code = UPNP_E_OUTOF_MEMORY; + break; + } + } + + finger = GetNextSubscription( service, finger ); + + } + } else { + return_code = GENA_E_BAD_SERVICE; + } + } + + if( ( *reference_count ) == 0 ) { + free( reference_count ); + free( headers ); + ixmlFreeDOMString( propertySet ); + free( UDN_copy ); + free( servId_copy ); + } + HandleUnlock( ); + + return return_code; +} + +/**************************************************************************** +* Function : respond_ok +* +* Parameters : +* IN SOCKINFO *info : socket connection of request +* IN int time_out : accepted duration +* IN subscription *sub : accepted subscription +* IN http_message_t* request : http request +* +* Description : Function to return OK message in the case +* of a subscription request. +* +* Return : static int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +static int +respond_ok( IN SOCKINFO * info, + IN int time_out, + IN subscription * sub, + IN http_message_t * request ) +{ + int major, + minor; + membuffer response; + int return_code; + char timeout_str[100]; + int upnp_timeout = UPNP_TIMEOUT; + + http_CalcResponseVersion( request->major_version, + request->minor_version, &major, &minor ); + + if( time_out >= 0 ) { + sprintf( timeout_str, "TIMEOUT: Second-%d", time_out ); + } else { + strcpy( timeout_str, "TIMEOUT: Second-infinite" ); + } + + membuffer_init( &response ); + response.size_inc = 30; + if( http_MakeMessage( &response, major, minor, + "R" "D" "S" "N" "Xc" "ssc" "sc" "c", + HTTP_OK, 0, + "SID: ", sub->sid, timeout_str ) != 0 ) { + membuffer_destroy( &response ); + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + return UPNP_E_OUTOF_MEMORY; + } + + return_code = http_SendMessage( info, &upnp_timeout, "b", + response.buf, response.length ); + + membuffer_destroy( &response ); + + return return_code; +} + +/**************************************************************************** +* Function : create_url_list +* +* Parameters : +* IN memptr* url_list : +* OUT URL_list *out : +* +* Description : Function to parse the Callback header value in +* subscription requests takes in a buffer containing URLS delimited by +* '<' and '>'. The entire buffer is copied into dynamic memory +* and stored in the URL_list. Pointers to the individual urls within +* this buffer are allocated and stored in the URL_list. Only URLs with +* network addresses are considered(i.e. host:port or domain name) +* +* Return : int +* if successful returns the number of URLs parsed +* else UPNP_E_OUTOF_MEMORY +* Note : +****************************************************************************/ +static int +create_url_list( IN memptr * url_list, + OUT URL_list * out ) +{ + int URLcount = 0; + int i; + int return_code = 0; + uri_type temp; + token urls; + token *URLS; + + urls.buff = url_list->buf; + urls.size = url_list->length; + URLS = &urls; + + out->size = 0; + out->URLs = NULL; + out->parsedURLs = NULL; + + for( i = 0; i < URLS->size; i++ ) { + if( ( URLS->buff[i] == '<' ) && ( i + 1 < URLS->size ) ) { + if( ( ( return_code = parse_uri( &URLS->buff[i + 1], + URLS->size - i + 1, + &temp ) ) == HTTP_SUCCESS ) + && ( temp.hostport.text.size != 0 ) ) { + URLcount++; + } else { + if( return_code == UPNP_E_OUTOF_MEMORY ) { + return return_code; + } + } + } + } + + if( URLcount > 0 ) { + out->URLs = ( char * )malloc( URLS->size + 1 ); + out->parsedURLs = + ( uri_type * ) malloc( sizeof( uri_type ) * URLcount ); + if( ( out->URLs == NULL ) || ( out->parsedURLs == NULL ) ) { + free( out->URLs ); + free( out->parsedURLs ); + out->URLs = NULL; + out->parsedURLs = NULL; + return UPNP_E_OUTOF_MEMORY; + } + memcpy( out->URLs, URLS->buff, URLS->size ); + out->URLs[URLS->size] = 0; + URLcount = 0; + for( i = 0; i < URLS->size; i++ ) { + if( ( URLS->buff[i] == '<' ) && ( i + 1 < URLS->size ) ) { + if( ( ( return_code = + parse_uri( &out->URLs[i + 1], URLS->size - i + 1, + &out->parsedURLs[URLcount] ) ) == + HTTP_SUCCESS ) + && ( out->parsedURLs[URLcount].hostport.text.size != + 0 ) ) { + URLcount++; + } else { + if( return_code == UPNP_E_OUTOF_MEMORY ) { + free( out->URLs ); + free( out->parsedURLs ); + out->URLs = NULL; + out->parsedURLs = NULL; + return return_code; + } + } + } + } + } + out->size = URLcount; + + return URLcount; +} + +/**************************************************************************** +* Function : gena_process_subscription_request +* +* Parameters : +* IN SOCKINFO *info : socket info of the device +* IN http_message_t* request : SUBSCRIPTION request from the control +* point +* +* Description : This function handles a subscription request from a +* ctrl point. The socket is not closed on return. +* +* Return : void +* +* Note : +****************************************************************************/ +void +gena_process_subscription_request( IN SOCKINFO * info, + IN http_message_t * request ) +{ + Upnp_SID temp_sid; + int return_code = 1; + int time_out = 1801; + service_info *service; + struct Upnp_Subscription_Request request_struct; + subscription *sub; + uuid_upnp uid; + struct Handle_Info *handle_info; + void *cookie; + Upnp_FunPtr callback_fun; + UpnpDevice_Handle device_handle; + memptr nt_hdr; + char *event_url_path = NULL; + memptr callback_hdr; + memptr timeout_hdr; + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "Subscription Request Received:\n" ) ); + + if( httpmsg_find_hdr( request, HDR_NT, &nt_hdr ) == NULL ) { + error_respond( info, HTTP_BAD_REQUEST, request ); + return; + } + + // check NT header + //Windows Millenium Interoperability: + //we accept either upnp:event, or upnp:propchange for the NT header + if( memptr_cmp_nocase( &nt_hdr, "upnp:event" ) != 0 ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + return; + } + + // if a SID is present then the we have a bad request + // "incompatible headers" + if( httpmsg_find_hdr( request, HDR_SID, NULL ) != NULL ) { + error_respond( info, HTTP_BAD_REQUEST, request ); + return; + } + //look up service by eventURL + if( ( event_url_path = str_alloc( request->uri.pathquery.buff, + request->uri.pathquery.size ) ) == + NULL ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + return; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "SubscriptionRequest for event URL path: %s\n", + event_url_path ); + ) + + HandleLock( ); + + // CURRENTLY, ONLY ONE DEVICE + if( GetDeviceHandleInfo( &device_handle, &handle_info ) != HND_DEVICE ) { + free( event_url_path ); + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + HandleUnlock( ); + return; + } + service = FindServiceEventURLPath( &handle_info->ServiceTable, + event_url_path ); + free( event_url_path ); + + if( service == NULL || !service->active ) { + error_respond( info, HTTP_NOT_FOUND, request ); + HandleUnlock( ); + return; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "Subscription Request: Number of Subscriptions already %d\n " + "Max Subscriptions allowed: %d\n", + service->TotalSubscriptions, + handle_info->MaxSubscriptions ) ); + + // too many subscriptions + if( handle_info->MaxSubscriptions != -1 && + service->TotalSubscriptions >= handle_info->MaxSubscriptions ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + HandleUnlock( ); + return; + } + // generate new subscription + sub = ( subscription * ) malloc( sizeof( subscription ) ); + if( sub == NULL ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + HandleUnlock( ); + return; + } + sub->eventKey = 0; + sub->ToSendEventKey = 0; + sub->active = 0; + sub->next = NULL; + sub->DeliveryURLs.size = 0; + sub->DeliveryURLs.URLs = NULL; + sub->DeliveryURLs.parsedURLs = NULL; + + // check for valid callbacks + if( httpmsg_find_hdr( request, HDR_CALLBACK, &callback_hdr ) == NULL || + ( return_code = create_url_list( &callback_hdr, + &sub->DeliveryURLs ) ) == 0 ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + freeSubscriptionList( sub ); + HandleUnlock( ); + return; + } + if( return_code == UPNP_E_OUTOF_MEMORY ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + freeSubscriptionList( sub ); + HandleUnlock( ); + return; + } + // set the timeout + if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) { + if( matchstr( timeout_hdr.buf, timeout_hdr.length, + "%iSecond-%d%0", &time_out ) == PARSE_OK ) { + // nothing + } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == + 0 ) { + time_out = -1; // infinite timeout + } else { + time_out = DEFAULT_TIMEOUT; // default is > 1800 seconds + } + } + // replace infinite timeout with max timeout, if possible + if( handle_info->MaxSubscriptionTimeOut != -1 ) { + if( time_out == -1 || + time_out > handle_info->MaxSubscriptionTimeOut ) { + time_out = handle_info->MaxSubscriptionTimeOut; + } + } + if( time_out >= 0 ) { + sub->expireTime = time( NULL ) + time_out; + } else { + sub->expireTime = 0; // infinite time + } + + //generate SID + uuid_create( &uid ); + uuid_unpack( &uid, temp_sid ); + sprintf( sub->sid, "uuid:%s", temp_sid ); + + // respond OK + if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) { + freeSubscriptionList( sub ); + HandleUnlock( ); + return; + } + //add to subscription list + sub->next = service->subscriptionList; + service->subscriptionList = sub; + service->TotalSubscriptions++; + + //finally generate callback for init table dump + request_struct.ServiceId = service->serviceId; + request_struct.UDN = service->UDN; + strcpy( ( char * )request_struct.Sid, sub->sid ); + + //copy callback + callback_fun = handle_info->Callback; + cookie = handle_info->Cookie; + + HandleUnlock( ); + + //make call back with request struct + //in the future should find a way of mainting + //that the handle is not unregistered in the middle of a + //callback + + callback_fun( UPNP_EVENT_SUBSCRIPTION_REQUEST, + &request_struct, cookie ); +} + +/**************************************************************************** +* Function : gena_process_subscription_renewal_request +* +* Parameters : +* IN SOCKINFO *info : socket info of the device +* IN http_message_t* request : subscription renewal request from the +* control point +* +* Description : This function handles a subscription renewal request +* from a ctrl point. The connection is not destroyed on return. +* +* Return : void +* +* Note : +****************************************************************************/ +void +gena_process_subscription_renewal_request( IN SOCKINFO * info, + IN http_message_t * request ) +{ + Upnp_SID sid; + subscription *sub; + int time_out = 1801; + service_info *service; + struct Handle_Info *handle_info; + UpnpDevice_Handle device_handle; + memptr temp_hdr; + membuffer event_url_path; + memptr timeout_hdr; + + // if a CALLBACK or NT header is present, then it is an error + if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || + httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { + error_respond( info, HTTP_BAD_REQUEST, request ); + return; + } + // get SID + if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || + temp_hdr.length > SID_SIZE ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + return; + } + memcpy( sid, temp_hdr.buf, temp_hdr.length ); + sid[temp_hdr.length] = '\0'; + + // lookup service by eventURL + membuffer_init( &event_url_path ); + if( membuffer_append( &event_url_path, request->uri.pathquery.buff, + request->uri.pathquery.size ) != 0 ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + return; + } + + HandleLock( ); + + // CURRENTLY, ONLY SUPPORT ONE DEVICE + if( GetDeviceHandleInfo( &device_handle, &handle_info ) != HND_DEVICE ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + membuffer_destroy( &event_url_path ); + return; + } + service = FindServiceEventURLPath( &handle_info->ServiceTable, + event_url_path.buf ); + membuffer_destroy( &event_url_path ); + + // get subscription + if( service == NULL || + !service->active || + ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + HandleUnlock( ); + return; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, + "Renew request: Number of subscriptions already: %d\n " + "Max Subscriptions allowed:%d\n", + service->TotalSubscriptions, + handle_info->MaxSubscriptions ); + ) + // too many subscriptions + if( handle_info->MaxSubscriptions != -1 && + service->TotalSubscriptions > handle_info->MaxSubscriptions ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + RemoveSubscriptionSID( sub->sid, service ); + HandleUnlock( ); + return; + } + // set the timeout + if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) { + if( matchstr( timeout_hdr.buf, timeout_hdr.length, + "%iSecond-%d%0", &time_out ) == PARSE_OK ) { + + //nothing + + } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == + 0 ) { + + time_out = -1; // inifinite timeout + + } else { + time_out = DEFAULT_TIMEOUT; // default is > 1800 seconds + + } + } + + // replace infinite timeout with max timeout, if possible + if( handle_info->MaxSubscriptionTimeOut != -1 ) { + if( time_out == -1 || + time_out > handle_info->MaxSubscriptionTimeOut ) { + time_out = handle_info->MaxSubscriptionTimeOut; + } + } + + if( time_out == -1 ) { + sub->expireTime = 0; + } else { + sub->expireTime = time( NULL ) + time_out; + } + + if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) { + RemoveSubscriptionSID( sub->sid, service ); + } + + HandleUnlock( ); +} + +/**************************************************************************** +* Function : gena_process_unsubscribe_request +* +* Parameters : +* IN SOCKINFO *info : socket info of the device +* IN http_message_t* request : UNSUBSCRIBE request from the control +* point +* +* Description : This function Handles a subscription cancellation request +* from a ctrl point. The connection is not destroyed on return. +* +* Return : void +* +* Note : +****************************************************************************/ +void +gena_process_unsubscribe_request( IN SOCKINFO * info, + IN http_message_t * request ) +{ + Upnp_SID sid; + service_info *service; + struct Handle_Info *handle_info; + UpnpDevice_Handle device_handle; + + memptr temp_hdr; + membuffer event_url_path; + + // if a CALLBACK or NT header is present, then it is an error + if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || + httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { + error_respond( info, HTTP_BAD_REQUEST, request ); + return; + } + // get SID + if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || + temp_hdr.length > SID_SIZE ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + return; + } + memcpy( sid, temp_hdr.buf, temp_hdr.length ); + sid[temp_hdr.length] = '\0'; + + // lookup service by eventURL + membuffer_init( &event_url_path ); + if( membuffer_append( &event_url_path, request->uri.pathquery.buff, + request->uri.pathquery.size ) != 0 ) { + error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); + return; + } + + HandleLock( ); + + // CURRENTLY, ONLY SUPPORT ONE DEVICE + if( GetDeviceHandleInfo( &device_handle, &handle_info ) != HND_DEVICE ) { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + membuffer_destroy( &event_url_path ); + HandleUnlock( ); + return; + } + service = FindServiceEventURLPath( &handle_info->ServiceTable, + event_url_path.buf ); + membuffer_destroy( &event_url_path ); + + // validate service + if( service == NULL || + !service->active || GetSubscriptionSID( sid, service ) == NULL ) + //CheckSubscriptionSID(sid, service) == NULL ) + { + error_respond( info, HTTP_PRECONDITION_FAILED, request ); + HandleUnlock( ); + return; + } + + RemoveSubscriptionSID( sid, service ); + error_respond( info, HTTP_OK, request ); // success + + HandleUnlock( ); +} + +#endif // INCLUDE_DEVICE_APIS +#endif // EXCLUDE_GENA diff --git a/libupnp/upnp/src/genlib/client_table/client_table.c b/libupnp/upnp/src/genlib/client_table/client_table.c new file mode 100644 index 0000000..81741cf --- /dev/null +++ b/libupnp/upnp/src/genlib/client_table/client_table.c @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file defines the functions for clients. It defines +* functions for adding and removing clients to and from the client table, +* adding and accessing subscription and other attributes pertaining to the +* client +************************************************************************/ + +#include "config.h" +#include "client_table.h" + +/************************************************************************ +* Function : copy_client_subscription +* +* Parameters : +* client_subscription * in ; - source client subscription +* client_subscription * out ; - destination client subscription +* +* Description : Make a copy of the client subscription data +* +* Return : int ; +* UPNP_E_OUTOF_MEMORY - On Failure to allocate memory +* HTTP_SUCCESS - On Success +* +* Note : +************************************************************************/ +CLIENTONLY( int copy_client_subscription( client_subscription * in, + client_subscription * out ) { + int len = strlen( in->ActualSID ) + 1; + int len1 = strlen( in->EventURL ) + 1; + memcpy( out->sid, in->sid, SID_SIZE ); + out->sid[SID_SIZE] = 0; + out->ActualSID = ( char * )malloc( len ); + out->EventURL = ( char * )malloc( len1 ); + if( ( out->EventURL == NULL ) || ( out->ActualSID == NULL ) ) + return UPNP_E_OUTOF_MEMORY; + memcpy( out->ActualSID, in->ActualSID, len ); + memcpy( out->EventURL, in->EventURL, len1 ); + //copies do not get RenewEvent Ids or next + out->RenewEventId = -1; out->next = NULL; return HTTP_SUCCESS;} + +/************************************************************************ +* Function : free_client_subscription +* +* Parameters : +* client_subscription * sub ; - Client subscription to be freed +* +* Description : Free memory allocated for client subscription data. +* Remove timer thread associated with this subscription event. +* +* Return : void ; +* +* Note : +************************************************************************/ + void free_client_subscription( client_subscription * sub ) { + upnp_timeout * event; ThreadPoolJob tempJob; if( sub ) { + if( sub->ActualSID ) + free( sub->ActualSID ); if( sub->EventURL ) + free( sub->EventURL ); if( sub->RenewEventId != -1 ) //do not remove timer event of copy + //invalid timer event id + { + if( TimerThreadRemove + ( &gTimerThread, sub->RenewEventId, &tempJob ) == 0 ) { + event = ( upnp_timeout * ) tempJob.arg; + free_upnp_timeout( event );} + } + + sub->RenewEventId = -1;} + } + +/************************************************************************ +* Function : freeClientSubList +* +* Parameters : +* client_subscription * list ; Client subscription +* +* Description : Free the client subscription table. +* +* Return : void ; +* +* Note : +************************************************************************/ + void freeClientSubList( client_subscription * list ) { + client_subscription * next; while( list ) { + free_client_subscription( list ); + next = list->next; free( list ); list = next;} + } + +/************************************************************************ +* Function : RemoveClientSubClientSID +* +* Parameters : +* client_subscription **head ; Head of the subscription list +* const Upnp_SID sid ; Subscription ID to be mactched +* +* Description : Remove the client subscription matching the +* subscritpion id represented by the const Upnp_SID sid parameter +* from the table and update the table. +* +* Return : void ; +* +* Note : +************************************************************************/ + void RemoveClientSubClientSID( client_subscription ** head, + const Upnp_SID sid ) { + client_subscription * finger = ( *head ); + client_subscription * previous = NULL; while( finger ) { + if( !( strcmp( sid, finger->sid ) ) ) { + if( previous ) + previous->next = finger->next; + else + ( *head ) = finger->next; + finger->next = NULL; + freeClientSubList( finger ); finger = NULL;} + else + { + previous = finger; finger = finger->next;} + } + } + +/************************************************************************ +* Function : GetClientSubClientSID +* +* Parameters : +* client_subscription *head ; Head of the subscription list +* const Upnp_SID sid ; Subscription ID to be matched +* +* Description : Return the client subscription from the client table +* that matches const Upnp_SID sid subscrition id value. +* +* Return : client_subscription * ; The matching subscription +* +* Note : +************************************************************************/ + client_subscription * + GetClientSubClientSID( client_subscription * head, + const Upnp_SID sid ) { + client_subscription * next = head; while( next ) { + if( !strcmp( next->sid, sid ) ) + break; + else + { + next = next->next;} + } + return next;} + +/************************************************************************ +* Function : GetClientSubActualSID +* +* Parameters : +* client_subscription *head ; Head of the subscription list +* token * sid ; Subscription ID to be matched +* +* Description : Returns the client subscription from the client +* subscription table that has the matching token * sid buffer +* value. +* +* Return : client_subscription * ; The matching subscription +* +* Note : +************************************************************************/ + client_subscription * + GetClientSubActualSID( client_subscription * head, + token * sid ) { + client_subscription * next = head; while( next ) { + + if( !memcmp( next->ActualSID, sid->buff, sid->size ) ) + break; + else + { + next = next->next;} + } + return next;} + + ) diff --git a/libupnp/upnp/src/genlib/miniserver/miniserver.c b/libupnp/upnp/src/genlib/miniserver/miniserver.c new file mode 100644 index 0000000..150b0be --- /dev/null +++ b/libupnp/upnp/src/genlib/miniserver/miniserver.c @@ -0,0 +1,893 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file implements the functionality and utility functions +* used by the Miniserver module. +************************************************************************/ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unixutil.h" +#include "ithread.h" + +#include +#include +#include +#include +#include +#include + +#include "ssdplib.h" + +#include "util.h" +#include "miniserver.h" +#include "ThreadPool.h" +#include "httpreadwrite.h" +#include "statcodes.h" +#include "upnp.h" +#include "upnpapi.h" + +#define APPLICATION_LISTENING_PORT 49152 + +struct mserv_request_t { + int connfd; // connection handle + struct in_addr foreign_ip_addr; + unsigned short foreign_ip_port; +}; + +typedef enum { MSERV_IDLE, MSERV_RUNNING, MSERV_STOPPING } MiniServerState; + +unsigned short miniStopSockPort; + +//////////////////////////////////////////////////////////////////////////// +// module vars +static MiniServerCallback gGetCallback = NULL; +static MiniServerCallback gSoapCallback = NULL; +static MiniServerCallback gGenaCallback = NULL; +static MiniServerState gMServState = MSERV_IDLE; + +/************************************************************************ +* Function : SetHTTPGetCallback +* +* Parameters : +* MiniServerCallback callback ; - HTTP Callback to be invoked +* +* Description : Set HTTP Get Callback +* +* Return : void +* +* Note : +************************************************************************/ +void +SetHTTPGetCallback( MiniServerCallback callback ) +{ + gGetCallback = callback; +} + +/************************************************************************ +* Function : SetSoapCallback +* +* Parameters : +* MiniServerCallback callback ; - SOAP Callback to be invoked +* +* Description : Set SOAP Callback +* +* Return : void +* +* Note : +************************************************************************/ +void +SetSoapCallback( MiniServerCallback callback ) +{ + gSoapCallback = callback; +} + +/************************************************************************ +* Function : SetGenaCallback +* +* Parameters : +* MiniServerCallback callback ; - GENA Callback to be invoked +* +* Description : Set GENA Callback +* +* Return : void +* +* Note : +************************************************************************/ +void +SetGenaCallback( MiniServerCallback callback ) +{ + gGenaCallback = callback; +} + +/************************************************************************ +* Function : dispatch_request +* +* Parameters : +* IN SOCKINFO *info ; Socket Information object. +* http_parser_t* hparser ; HTTP parser object. +* +* Description : Based on the type pf message, appropriate callback +* is issued +* +* Return : int ; +* 0 - On Success +* HTTP_INTERNAL_SERVER_ERROR - Callback is NULL +* +* Note : +************************************************************************/ +static int +dispatch_request( IN SOCKINFO * info, + http_parser_t * hparser ) +{ + MiniServerCallback callback; + + switch ( hparser->msg.method ) { + //Soap Call + case SOAPMETHOD_POST: + case HTTPMETHOD_MPOST: + callback = gSoapCallback; + break; + + //Gena Call + case HTTPMETHOD_NOTIFY: + case HTTPMETHOD_SUBSCRIBE: + case HTTPMETHOD_UNSUBSCRIBE: + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "miniserver %d: got GENA msg\n", info->socket ); + ) + callback = gGenaCallback; + break; + + //HTTP server call + case HTTPMETHOD_GET: + case HTTPMETHOD_POST: + case HTTPMETHOD_HEAD: + case HTTPMETHOD_SIMPLEGET: + callback = gGetCallback; + break; + + default: + callback = NULL; + } + + if( callback == NULL ) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + callback( hparser, &hparser->msg, info ); + return 0; +} + +/************************************************************************ +* Function : handle_error +* +* Parameters : +* +* IN SOCKINFO *info ; Socket Inforamtion Object +* int http_error_code ; HTTP Error Code +* int major ; Major Version Number +* int minor ; Minor Version Number +* +* Description : Send Error Message +* +* Return : void; +* +* Note : +************************************************************************/ +static XINLINE void +handle_error( IN SOCKINFO * info, + int http_error_code, + int major, + int minor ) +{ + http_SendStatusResponse( info, http_error_code, major, minor ); +} + +/************************************************************************ +* Function : free_handle_request_arg +* +* Parameters : +* void *args ; Request Message to be freed +* +* Description : Free memory assigned for handling request and unitial- +* -ize socket functionality +* +* Return : void +* +* Note : +************************************************************************/ +static void +free_handle_request_arg( void *args ) +{ + struct mserv_request_t *request = ( struct mserv_request_t * )args; + + shutdown( request->connfd, SD_BOTH ); + UpnpCloseSocket( request->connfd ); + free( request ); +} + +/************************************************************************ +* Function : handle_request +* +* Parameters : +* void *args ; Request Message to be handled +* +* Description : Receive the request and dispatch it for handling +* +* Return : void +* +* Note : +************************************************************************/ +static void +handle_request( void *args ) +{ + SOCKINFO info; + int http_error_code; + int ret_code; + int major = 1; + int minor = 1; + http_parser_t parser; + http_message_t *hmsg = NULL; + int timeout = HTTP_DEFAULT_TIMEOUT; + struct mserv_request_t *request = ( struct mserv_request_t * )args; + int connfd = request->connfd; + + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "miniserver %d: READING\n", connfd ); + ) + //parser_request_init( &parser ); ////LEAK_FIX_MK + hmsg = &parser.msg; + + if( sock_init_with_ip( &info, connfd, request->foreign_ip_addr, + request->foreign_ip_port ) != UPNP_E_SUCCESS ) { + free( request ); + httpmsg_destroy( hmsg ); + return; + } + // read + ret_code = http_RecvMessage( &info, &parser, HTTPMETHOD_UNKNOWN, + &timeout, &http_error_code ); + if( ret_code != 0 ) { + goto error_handler; + } + + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "miniserver %d: PROCESSING...\n", connfd ); + ) + // dispatch + http_error_code = dispatch_request( &info, &parser ); + if( http_error_code != 0 ) { + goto error_handler; + } + + http_error_code = 0; + + error_handler: + if( http_error_code > 0 ) { + if( hmsg ) { + major = hmsg->major_version; + minor = hmsg->minor_version; + } + handle_error( &info, http_error_code, major, minor ); + } + + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "miniserver %d: COMPLETE\n", connfd ); + ) + sock_destroy( &info, SD_BOTH ); //should shutdown completely + + httpmsg_destroy( hmsg ); + free( request ); +} + +/************************************************************************ +* Function : schedule_request_job +* +* Parameters : +* IN int connfd ; Socket Descriptor on which connection is accepted +* IN struct sockaddr_in* clientAddr ; Clients Address information +* +* Description : Initilize the thread pool to handle a request. +* Sets priority for the job and adds the job to the thread pool +* +* +* Return : void +* +* Note : +************************************************************************/ +static XINLINE void +schedule_request_job( IN int connfd, + IN struct sockaddr_in *clientAddr ) +{ + struct mserv_request_t *request; + ThreadPoolJob job; + + request = + ( struct mserv_request_t * ) + malloc( sizeof( struct mserv_request_t ) ); + if( request == NULL ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "mserv %d: out of memory\n", connfd ); + ) + shutdown( request->connfd, SD_BOTH ); + UpnpCloseSocket( connfd ); + return; + } + + request->connfd = connfd; + request->foreign_ip_addr = clientAddr->sin_addr; + request->foreign_ip_port = ntohs( clientAddr->sin_port ); + + TPJobInit( &job, ( start_routine ) handle_request, ( void * )request ); + TPJobSetFreeFunction( &job, free_handle_request_arg ); + TPJobSetPriority( &job, MED_PRIORITY ); + + if( ThreadPoolAdd( &gRecvThreadPool, &job, NULL ) != 0 ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "mserv %d: cannot schedule request\n", connfd ); + ) + free( request ); + shutdown( connfd, SD_BOTH ); + UpnpCloseSocket( connfd ); + return; + } + +} + +/************************************************************************ +* Function : RunMiniServer +* +* Parameters : +* MiniServerSockArray *miniSock ; Socket Array +* +* Description : Function runs the miniserver. The MiniServer accepts a +* new request and schedules a thread to handle the new request. +* Checks for socket state and invokes appropriate read and shutdown +* actions for the Miniserver and SSDP sockets +* +* Return : void +* +* Note : +************************************************************************/ +static void +RunMiniServer( MiniServerSockArray * miniSock ) +{ + struct sockaddr_in clientAddr; + socklen_t clientLen; + SOCKET miniServSock, + connectHnd; + SOCKET miniServStopSock; + SOCKET ssdpSock; + + CLIENTONLY( SOCKET ssdpReqSock; + ) + + fd_set expSet; + fd_set rdSet; + unsigned int maxMiniSock; + int byteReceived; + char requestBuf[256]; + + miniServSock = miniSock->miniServerSock; + miniServStopSock = miniSock->miniServerStopSock; + + ssdpSock = miniSock->ssdpSock; + + CLIENTONLY( ssdpReqSock = miniSock->ssdpReqSock; + ); + + gMServState = MSERV_RUNNING; + maxMiniSock = max( miniServSock, miniServStopSock ); + maxMiniSock = max( maxMiniSock, ( SOCKET ) ( ssdpSock ) ); + + CLIENTONLY( maxMiniSock = + max( maxMiniSock, ( SOCKET ) ( ssdpReqSock ) ) ); + + ++maxMiniSock; + + while( TRUE ) { + FD_ZERO( &rdSet ); + FD_ZERO( &expSet ); + + FD_SET( miniServStopSock, &expSet ); + + FD_SET( miniServSock, &rdSet ); + FD_SET( miniServStopSock, &rdSet ); + FD_SET( ssdpSock, &rdSet ); + CLIENTONLY( FD_SET( ssdpReqSock, &rdSet ) ); + + if( select( maxMiniSock, &rdSet, NULL, &expSet, NULL ) == + UPNP_SOCKETERROR ) { + DBGONLY( UpnpPrintf + ( UPNP_CRITICAL, SSDP, __FILE__, __LINE__, + "Error in select call !!!\n" ); + ) + continue; + } else { + + if( FD_ISSET( miniServSock, &rdSet ) ) { + clientLen = sizeof( struct sockaddr_in ); + connectHnd = accept( miniServSock, + ( struct sockaddr * )&clientAddr, + &clientLen ); + if( connectHnd == UPNP_INVALID_SOCKET ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "miniserver: Error" + " in accepting connection\n" ); + ) + continue; + } + schedule_request_job( connectHnd, &clientAddr ); + } + //ssdp + CLIENTONLY( if( FD_ISSET( ssdpReqSock, &rdSet ) ) { + + readFromSSDPSocket( ssdpReqSock );} + ) + + if( FD_ISSET( ssdpSock, &rdSet ) ) { + readFromSSDPSocket( ssdpSock ); + } + + if( FD_ISSET( miniServStopSock, &rdSet ) ) { + + clientLen = sizeof( struct sockaddr_in ); + memset( ( char * )&clientAddr, 0, + sizeof( struct sockaddr_in ) ); + byteReceived = + recvfrom( miniServStopSock, requestBuf, 25, 0, + ( struct sockaddr * )&clientAddr, + &clientLen ); + if( byteReceived > 0 ) { + requestBuf[byteReceived] = '\0'; + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "Received response !!! %s From host %s \n", + requestBuf, + inet_ntoa( clientAddr.sin_addr ) ); + ) + DBGONLY( UpnpPrintf + ( UPNP_PACKET, MSERV, __FILE__, __LINE__, + "Received multicast packet: \n %s\n", + requestBuf ); + ) + + if( NULL != strstr( requestBuf, "ShutDown" ) ) + break; + } + } + } + + } + + shutdown( miniServSock, SD_BOTH ); + UpnpCloseSocket( miniServSock ); + shutdown( miniServStopSock, SD_BOTH ); + UpnpCloseSocket( miniServStopSock ); + shutdown( ssdpSock, SD_BOTH ); + UpnpCloseSocket( ssdpSock ); + CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ) ); + CLIENTONLY( UpnpCloseSocket( ssdpReqSock ) ); + + free( miniSock ); + + gMServState = MSERV_IDLE; + + return; + +} + +/************************************************************************ +* Function : get_port +* +* Parameters : +* int sockfd ; Socket Descriptor +* +* Description : Returns port to which socket, sockfd, is bound. +* +* Return : int, +* -1 on error; check errno +* > 0 means port number +* +* Note : +************************************************************************/ +static int +get_port( int sockfd ) +{ + struct sockaddr_in sockinfo; + socklen_t len; + int code; + int port; + + len = sizeof( struct sockaddr_in ); + code = getsockname( sockfd, ( struct sockaddr * )&sockinfo, &len ); + if( code == -1 ) { + return -1; + } + + port = ntohs( sockinfo.sin_port ); + DBGONLY( UpnpPrintf + ( UPNP_INFO, MSERV, __FILE__, __LINE__, + "sockfd = %d, .... port = %d\n", sockfd, port ); + ) + + return port; +} + +/************************************************************************ +* Function : get_miniserver_sockets +* +* Parameters : +* MiniServerSockArray *out ; Socket Array +* unsigned short listen_port ; port on which the server is listening +* for incoming connections +* +* Description : Creates a STREAM socket, binds to INADDR_ANY and +* listens for incoming connecttions. Returns the actual port which +* the sockets sub-system returned. +* Also creates a DGRAM socket, binds to the loop back address and +* returns the port allocated by the socket sub-system. +* +* Return : int : +* UPNP_E_OUTOF_SOCKET - Failed to create a socket +* UPNP_E_SOCKET_BIND - Bind() failed +* UPNP_E_LISTEN - Listen() failed +* UPNP_E_INTERNAL_ERROR - Port returned by the socket layer is < 0 +* UPNP_E_SUCCESS - Success +* +* Note : +************************************************************************/ +int +get_miniserver_sockets( MiniServerSockArray * out, + unsigned short listen_port ) +{ + struct sockaddr_in serverAddr; + int listenfd; + int success; + unsigned short actual_port; + int reuseaddr_on = 0; + int sockError = UPNP_E_SUCCESS; + int errCode = 0; + int miniServerStopSock; + + listenfd = socket( AF_INET, SOCK_STREAM, 0 ); + if( listenfd < 0 ) { + return UPNP_E_OUTOF_SOCKET; // error creating socket + } + // As per the IANA specifications for the use of ports by applications + // override the listen port passed in with the first available + if( listen_port < APPLICATION_LISTENING_PORT ) + listen_port = APPLICATION_LISTENING_PORT; + + memset( &serverAddr, 0, sizeof( serverAddr ) ); + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = htonl( INADDR_ANY ); + + // Getting away with implementation of re-using address:port and instead + // choosing to increment port numbers. + // Keeping the re-use address code as an optional behaviour that can be + // turned on if necessary. + // TURN ON the reuseaddr_on option to use the option. + if( reuseaddr_on ) { + //THIS IS ALLOWS US TO BIND AGAIN IMMEDIATELY + //AFTER OUR SERVER HAS BEEN CLOSED + //THIS MAY CAUSE TCP TO BECOME LESS RELIABLE + //HOWEVER IT HAS BEEN SUGESTED FOR TCP SERVERS + + DBGONLY( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, + "mserv start: resuseaddr set\n" ); + ) + + sockError = setsockopt( listenfd, + SOL_SOCKET, + SO_REUSEADDR, + ( const char * )&reuseaddr_on, + sizeof( int ) + ); + if( sockError == UPNP_SOCKETERROR ) { + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( listenfd ); + return UPNP_E_SOCKET_BIND; + } + + sockError = bind( listenfd, + ( struct sockaddr * )&serverAddr, + sizeof( struct sockaddr_in ) + ); + } else { + do { + serverAddr.sin_port = htons( listen_port++ ); + sockError = bind( listenfd, + ( struct sockaddr * )&serverAddr, + sizeof( struct sockaddr_in ) + ); + if( sockError == UPNP_SOCKETERROR ) { + if( errno == EADDRINUSE ) + errCode = 1; + } else + errCode = 0; + + } while( errCode != 0 ); + } + + if( sockError == UPNP_SOCKETERROR ) { + DBGONLY( perror( "mserv start: bind failed" ); + ) + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( listenfd ); + return UPNP_E_SOCKET_BIND; // bind failed + } + + DBGONLY( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, + "mserv start: bind success\n" ); + ) + + success = listen( listenfd, SOMAXCONN ); + if( success == UPNP_SOCKETERROR ) { + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( listenfd ); + return UPNP_E_LISTEN; // listen failed + } + + actual_port = get_port( listenfd ); + if( actual_port <= 0 ) { + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( listenfd ); + return UPNP_E_INTERNAL_ERROR; + } + + out->miniServerPort = actual_port; + + if( ( miniServerStopSock = socket( AF_INET, SOCK_DGRAM, 0 ) ) == + UPNP_INVALID_SOCKET ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, + MSERV, __FILE__, __LINE__, + "Error in socket operation !!!\n" ); + ) + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( listenfd ); + return UPNP_E_OUTOF_SOCKET; + } + + // bind to local socket + memset( ( char * )&serverAddr, 0, sizeof( struct sockaddr_in ) ); + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = inet_addr( "127.0.0.1" ); + + if( bind( miniServerStopSock, ( struct sockaddr * )&serverAddr, + sizeof( serverAddr ) ) == UPNP_SOCKETERROR ) { + + DBGONLY( UpnpPrintf( UPNP_CRITICAL, + MSERV, __FILE__, __LINE__, + "Error in binding localhost!!!\n" ); + ) + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( listenfd ); + shutdown( miniServerStopSock, SD_BOTH ); + UpnpCloseSocket( miniServerStopSock ); + return UPNP_E_SOCKET_BIND; + } + + miniStopSockPort = get_port( miniServerStopSock ); + if( miniStopSockPort <= 0 ) { + shutdown( miniServerStopSock, SD_BOTH ); + shutdown( listenfd, SD_BOTH ); + UpnpCloseSocket( miniServerStopSock ); + UpnpCloseSocket( listenfd ); + return UPNP_E_INTERNAL_ERROR; + } + + out->stopPort = miniStopSockPort; + + out->miniServerSock = listenfd; + out->miniServerStopSock = miniServerStopSock; + + return UPNP_E_SUCCESS; + +} + +/************************************************************************ +* Function : StartMiniServer +* +* Parameters : +* unsigned short listen_port ; Port on which the server listens for +* incoming connections +* +* Description : Initialize the sockets functionality for the +* Miniserver. Initialize a thread pool job to run the MiniServer +* and the job to the thread pool. If listen port is 0, port is +* dynamically picked +* +* Use timer mechanism to start the MiniServer, failure to meet the +* allowed delay aborts the attempt to launch the MiniServer. +* +* Return : int ; +* Actual port socket is bound to - On Success: +* A negative number UPNP_E_XXX - On Error +* Note : +************************************************************************/ +int +StartMiniServer( unsigned short listen_port ) +{ + + int success; + + int count; + int max_count = 10000; + + MiniServerSockArray *miniSocket; + ThreadPoolJob job; + + if( gMServState != MSERV_IDLE ) { + return UPNP_E_INTERNAL_ERROR; // miniserver running + } + + miniSocket = + ( MiniServerSockArray * ) malloc( sizeof( MiniServerSockArray ) ); + if( miniSocket == NULL ) + return UPNP_E_OUTOF_MEMORY; + + if( ( success = get_miniserver_sockets( miniSocket, listen_port ) ) + != UPNP_E_SUCCESS ) { + free( miniSocket ); + return success; + } + + if( ( success = get_ssdp_sockets( miniSocket ) ) != UPNP_E_SUCCESS ) { + + free( miniSocket ); + shutdown( miniSocket->miniServerSock, SD_BOTH ); + UpnpCloseSocket( miniSocket->miniServerSock ); + shutdown( miniSocket->miniServerStopSock, SD_BOTH ); + UpnpCloseSocket( miniSocket->miniServerStopSock ); + return success; + } + + TPJobInit( &job, ( start_routine ) RunMiniServer, + ( void * )miniSocket ); + TPJobSetPriority( &job, MED_PRIORITY ); + + TPJobSetFreeFunction( &job, ( free_routine ) free ); + + success = ThreadPoolAddPersistent( &gRecvThreadPool, &job, NULL ); + + if( success < 0 ) { + shutdown( miniSocket->miniServerSock, SD_BOTH ); + shutdown( miniSocket->miniServerStopSock, SD_BOTH ); + shutdown( miniSocket->ssdpSock, SD_BOTH ); + CLIENTONLY( shutdown( miniSocket->ssdpReqSock, SD_BOTH ) ); + UpnpCloseSocket( miniSocket->miniServerSock ); + UpnpCloseSocket( miniSocket->miniServerStopSock ); + UpnpCloseSocket( miniSocket->ssdpSock ); + + CLIENTONLY( UpnpCloseSocket( miniSocket->ssdpReqSock ) ); + + return UPNP_E_OUTOF_MEMORY; + } + // wait for miniserver to start + count = 0; + while( gMServState != MSERV_RUNNING && count < max_count ) { + usleep( 50 * 1000 ); // 0.05s + count++; + } + + // taking too long to start that thread + if( count >= max_count ) { + + shutdown( miniSocket->miniServerSock, SD_BOTH ); + shutdown( miniSocket->miniServerStopSock, SD_BOTH ); + shutdown( miniSocket->ssdpSock, SD_BOTH ); + CLIENTONLY( shutdown( miniSocket->ssdpReqSock, SD_BOTH ) ); + + UpnpCloseSocket( miniSocket->miniServerSock ); + UpnpCloseSocket( miniSocket->miniServerStopSock ); + UpnpCloseSocket( miniSocket->ssdpSock ); + CLIENTONLY( UpnpCloseSocket( miniSocket->ssdpReqSock ) ); + + return UPNP_E_INTERNAL_ERROR; + } + + return miniSocket->miniServerPort; +} + +/************************************************************************ +* Function : StopMiniServer +* +* Parameters : +* void ; +* +* Description : Stop and Shutdown the MiniServer and free socket +* resources. +* +* Return : int ; +* Always returns 0 +* +* Note : +************************************************************************/ +int +StopMiniServer( void ) +{ + + int socklen = sizeof( struct sockaddr_in ), + sock; + struct sockaddr_in ssdpAddr; + char buf[256] = "ShutDown"; + int bufLen = strlen( buf ); + + if( gMServState == MSERV_RUNNING ) + gMServState = MSERV_STOPPING; + else + return 0; + + sock = socket( AF_INET, SOCK_DGRAM, 0 ); + if( sock == UPNP_INVALID_SOCKET ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP_SERVER:StopSSDPServer: Error in socket operation !!!\n" ); + ) + return 0; + } + + while( gMServState != MSERV_IDLE ) { + ssdpAddr.sin_family = AF_INET; + ssdpAddr.sin_addr.s_addr = inet_addr( "127.0.0.1" ); + ssdpAddr.sin_port = htons( miniStopSockPort ); + sendto( sock, buf, bufLen, 0, ( struct sockaddr * )&ssdpAddr, + socklen ); + usleep( 1000 ); + if( gMServState == MSERV_IDLE ) + break; + isleep( 1 ); + } + shutdown( sock, SD_BOTH ); + UpnpCloseSocket( sock ); + return 0; +} diff --git a/libupnp/upnp/src/genlib/net/http/httpparser.c b/libupnp/upnp/src/genlib/net/http/httpparser.c new file mode 100644 index 0000000..a2131e2 --- /dev/null +++ b/libupnp/upnp/src/genlib/net/http/httpparser.c @@ -0,0 +1,2516 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file contains functions for scanner and parser for http +* messages. +************************************************************************/ + +#include "config.h" +#include +#include +#include +#include +#include +#include "strintmap.h" +#include "httpparser.h" +#include "statcodes.h" +#include "unixutil.h" + +// entity positions + +#define NUM_HTTP_METHODS 9 +static str_int_entry Http_Method_Table[NUM_HTTP_METHODS] = { + {"GET", HTTPMETHOD_GET}, + {"HEAD", HTTPMETHOD_HEAD}, + {"M-POST", HTTPMETHOD_MPOST}, + {"M-SEARCH", HTTPMETHOD_MSEARCH}, + {"NOTIFY", HTTPMETHOD_NOTIFY}, + {"POST", HTTPMETHOD_POST}, + {"SUBSCRIBE", HTTPMETHOD_SUBSCRIBE}, + {"UNSUBSCRIBE", HTTPMETHOD_UNSUBSCRIBE}, + {"POST", SOAPMETHOD_POST}, + +}; + +#define NUM_HTTP_HEADER_NAMES 33 +str_int_entry Http_Header_Names[NUM_HTTP_HEADER_NAMES] = { + {"ACCEPT", HDR_ACCEPT}, + {"ACCEPT-CHARSET", HDR_ACCEPT_CHARSET}, + {"ACCEPT-ENCODING", HDR_ACCEPT_ENCODING}, + {"ACCEPT-LANGUAGE", HDR_ACCEPT_LANGUAGE}, + {"ACCEPT-RANGES", HDR_ACCEPT_RANGE}, + {"CACHE-CONTROL", HDR_CACHE_CONTROL}, + {"CALLBACK", HDR_CALLBACK}, + {"CONTENT-ENCODING", HDR_CONTENT_ENCODING}, + {"CONTENT-LANGUAGE", HDR_CONTENT_LANGUAGE}, + {"CONTENT-LENGTH", HDR_CONTENT_LENGTH}, + {"CONTENT-LOCATION", HDR_CONTENT_LOCATION}, + {"CONTENT-RANGE", HDR_CONTENT_RANGE}, + {"CONTENT-TYPE", HDR_CONTENT_TYPE}, + {"DATE", HDR_DATE}, + {"EXT", HDR_EXT}, + {"HOST", HDR_HOST}, + {"IF-RANGE", HDR_IF_RANGE}, + {"LOCATION", HDR_LOCATION}, + {"MAN", HDR_MAN}, + {"MX", HDR_MX}, + {"NT", HDR_NT}, + {"NTS", HDR_NTS}, + {"RANGE", HDR_RANGE}, + {"SEQ", HDR_SEQ}, + {"SERVER", HDR_SERVER}, + {"SID", HDR_SID}, + {"SOAPACTION", HDR_SOAPACTION}, + {"ST", HDR_ST}, + {"TE", HDR_TE}, + {"TIMEOUT", HDR_TIMEOUT}, + {"TRANSFER-ENCODING", HDR_TRANSFER_ENCODING}, + {"USER-AGENT", HDR_USER_AGENT}, + {"USN", HDR_USN} +}; + +/***********************************************************************/ + +/************* scanner **************/ + +/***********************************************************************/ + +#define TOKCHAR_CR 0xD +#define TOKCHAR_LF 0xA + +/************************************************************************ +* Function : scanner_init +* +* Parameters : +* OUT scanner_t* scanner ; Scanner Object to be initialized +* IN membuffer* bufptr ; Buffer to be copied +* +* Description : Intialize scanner +* +* Return : void ; +* +* Note : +************************************************************************/ +static XINLINE void +scanner_init( OUT scanner_t * scanner, + IN membuffer * bufptr ) +{ + scanner->cursor = 0; + scanner->msg = bufptr; + scanner->entire_msg_loaded = FALSE; +} + +/************************************************************************ +* Function : is_separator_char +* +* Parameters : +* IN char c ; character to be tested against used separator values +* +* Description : Finds the separator character. +* +* Return : xboolean ; +* +* Note : +************************************************************************/ +static XINLINE xboolean +is_separator_char( IN char c ) +{ + return strchr( " \t()<>@,;:\\\"/[]?={}", c ) != NULL; +} + +/************************************************************************ +* Function : is_identifier_char +* +* Parameters : +* IN char c ; character to be tested for separator values +* +* Description : Calls the function to indentify separator character +* +* Return : xboolean ; +* +* Note : +************************************************************************/ +static XINLINE xboolean +is_identifier_char( IN char c ) +{ + return ( c >= 32 && c <= 126 ) && !is_separator_char( c ); +} + +/************************************************************************ +* Function : is_control_char +* +* Parameters : +* IN char c ; character to be tested for a control character +* +* Description : Determines if the passed value is a control character +* +* Return : xboolean ; +* +* Note : +************************************************************************/ +static XINLINE xboolean +is_control_char( IN char c ) +{ + return ( ( c >= 0 && c <= 31 ) || ( c == 127 ) ); +} + +/************************************************************************ +* Function : is_qdtext_char +* +* Parameters : +* IN char cc ; character to be tested for CR/LF +* +* Description : Checks to see if the passed in value is CR/LF +* +* Return : xboolean ; +* +* Note : +************************************************************************/ +static XINLINE xboolean +is_qdtext_char( IN char cc ) +{ + unsigned char c = ( unsigned char )cc; + + // we don't check for this; it's checked in get_token() + assert( c != '"' ); + + if( ( c >= 32 && c != 127 ) || + ( c == TOKCHAR_CR || c == TOKCHAR_LF || c == '\t' ) + ) { + return TRUE; + } else { + return FALSE; + } +} + +/************************************************************************ +* Function : scanner_get_token +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* OUT memptr* token ; Token +* OUT token_type_t* tok_type ; Type of token +* +* Description : reads next token from the input stream +* note: 0 and is used as a marker, and will not be valid in a quote +* +* Return : parse_status_t ; +* PARSE_OK +* PARSE_INCOMPLETE -- not enuf chars to get a token +* PARSE_FAILURE -- bad msg format +* +* Note : +************************************************************************/ +static parse_status_t +scanner_get_token( INOUT scanner_t * scanner, + OUT memptr * token, + OUT token_type_t * tok_type ) +{ + char *cursor; + char *null_terminator; // point to null-terminator in buffer + char c; + token_type_t token_type; + xboolean got_end_quote; + + assert( scanner ); + assert( token ); + assert( tok_type ); + + // point to next char in buffer + cursor = scanner->msg->buf + scanner->cursor; + null_terminator = scanner->msg->buf + scanner->msg->length; + + // not enough chars in input to parse + if( cursor == null_terminator ) { + return PARSE_INCOMPLETE; + } + + c = *cursor; + if( is_identifier_char( c ) ) { + // scan identifier + + token->buf = cursor++; + token_type = TT_IDENTIFIER; + + while( is_identifier_char( *cursor ) ) { + cursor++; + } + + if( !scanner->entire_msg_loaded && cursor == null_terminator ) { + // possibly more valid chars + return PARSE_INCOMPLETE; + } + // calc token length + token->length = cursor - token->buf; + } else if( c == ' ' || c == '\t' ) { + token->buf = cursor++; + token_type = TT_WHITESPACE; + + while( *cursor == ' ' || *cursor == '\t' ) { + cursor++; + } + + if( !scanner->entire_msg_loaded && cursor == null_terminator ) { + // possibly more chars + return PARSE_INCOMPLETE; + } + token->length = cursor - token->buf; + } else if( c == TOKCHAR_CR ) { + // scan CRLF + + token->buf = cursor++; + if( cursor == null_terminator ) { + // not enuf info to determine CRLF + return PARSE_INCOMPLETE; + } + if( *cursor != TOKCHAR_LF ) { + // couldn't match CRLF; match as CR + token_type = TT_CTRL; // ctrl char + token->length = 1; + } else { + // got CRLF + token->length = 2; + token_type = TT_CRLF; + cursor++; + } + } else if( c == TOKCHAR_LF ) // accept \n as CRLF + { + token->buf = cursor++; + token->length = 1; + token_type = TT_CRLF; + } else if( c == '"' ) { + // quoted text + token->buf = cursor++; + token_type = TT_QUOTEDSTRING; + got_end_quote = FALSE; + + while( cursor < null_terminator ) { + c = *cursor++; + if( c == '"' ) { + got_end_quote = TRUE; + break; + } else if( c == '\\' ) { + if( cursor < null_terminator ) { + c = *cursor++; + //if ( !(c > 0 && c <= 127) ) + if( c == 0 ) { + return PARSE_FAILURE; + } + } + // else, while loop handles incomplete buf + } else if( is_qdtext_char( c ) ) { + // just accept char + } else { + // bad quoted text + return PARSE_FAILURE; + } + } + if( got_end_quote ) { + token->length = cursor - token->buf; + } else // incomplete + { + assert( cursor == null_terminator ); + return PARSE_INCOMPLETE; + } + } else if( is_separator_char( c ) ) { + // scan separator + + token->buf = cursor++; + token_type = TT_SEPARATOR; + token->length = 1; + } else if( is_control_char( c ) ) { + // scan ctrl char + + token->buf = cursor++; + token_type = TT_CTRL; + token->length = 1; + } else { + return PARSE_FAILURE; + } + + scanner->cursor += token->length; // move to next token + *tok_type = token_type; + return PARSE_OK; +} + +/************************************************************************ +* Function : scanner_get_str +* +* Parameters : +* IN scanner_t* scanner ; Scanner Object +* +* Description : returns ptr to next char in string +* +* Return : char* ; +* +* Note : +************************************************************************/ +static XINLINE char * +scanner_get_str( IN scanner_t * scanner ) +{ + return scanner->msg->buf + scanner->cursor; +} + +/************************************************************************ +* Function : scanner_pushback +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* IN size_t pushback_bytes ; Bytes to be moved back +* +* Description : Move back by a certain number of bytes. +* This is used to put back one or more tokens back into the input +* +* Return : void ; +* +* Note : +************************************************************************/ +static XINLINE void +scanner_pushback( INOUT scanner_t * scanner, + IN size_t pushback_bytes ) +{ + scanner->cursor -= pushback_bytes; +} + +/***********************************************************************/ + +/************* end of scanner **************/ + +/***********************************************************************/ + +/***********************************************************************/ + +/************* parser **************/ + +/***********************************************************************/ + +/***********************************************************************/ + +/************* http_message_t **************/ + +/***********************************************************************/ + +/************************************************************************ +* Function : httpmsg_compare +* +* Parameters : +* void* param1 ; +* void* param2 ; +* +* Description : Compares name id in the http headers. +* +* Return : int ; +* +* Note : +************************************************************************/ +static int +httpmsg_compare( void *param1, + void *param2 ) +{ + assert( param1 != NULL ); + assert( param2 != NULL ); + + return ( ( http_header_t * ) param1 )->name_id == + ( ( http_header_t * ) param2 )->name_id; +} + +/************************************************************************ +* Function : httpheader_free +* +* Parameters : +* void *msg ; +* +* Description : Free memory allocated for the http header +* +* Return : void ; +* +* Note : +************************************************************************/ +static void +httpheader_free( void *msg ) +{ + http_header_t *hdr = ( http_header_t * ) msg; + + membuffer_destroy( &hdr->name_buf ); + membuffer_destroy( &hdr->value ); + free( hdr ); +} + +/************************************************************************ +* Function : httpmsg_init +* +* Parameters : +* INOUT http_message_t* msg ; HTTP Message Object +* +* Description : Initialize and allocate memory for http message +* +* Return : void ; +* +* Note : +************************************************************************/ +void +httpmsg_init( INOUT http_message_t * msg ) +{ + msg->initialized = 1; + msg->entity.buf = NULL; + msg->entity.length = 0; + ListInit( &msg->headers, httpmsg_compare, httpheader_free ); + membuffer_init( &msg->msg ); + membuffer_init( &msg->status_msg ); +} + +/************************************************************************ +* Function : httpmsg_destroy +* +* Parameters : +* INOUT http_message_t* msg ; HTTP Message Object +* +* Description : Free memory allocated for the http message +* +* Return : void ; +* +* Note : +************************************************************************/ +void +httpmsg_destroy( INOUT http_message_t * msg ) +{ + assert( msg != NULL ); + + if( msg->initialized == 1 ) { + ListDestroy( &msg->headers, 1 ); + membuffer_destroy( &msg->msg ); + membuffer_destroy( &msg->status_msg ); + free( msg->urlbuf ); + msg->initialized = 0; + } +} + +/************************************************************************ +* Function : httpmsg_find_hdr_str +* +* Parameters : +* IN http_message_t* msg ; HTTP Message Object +* IN const char* header_name ; Header name to be compared with +* +* Description : Compares the header name with the header names stored +* in the linked list of messages +* +* Return : http_header_t* - Pointer to a header on success; +* NULL on failure +* +* Note : +************************************************************************/ +http_header_t * +httpmsg_find_hdr_str( IN http_message_t * msg, + IN const char *header_name ) +{ + http_header_t *header; + + ListNode *node; + + node = ListHead( &msg->headers ); + while( node != NULL ) { + + header = ( http_header_t * ) node->item; + + if( memptr_cmp_nocase( &header->name, header_name ) == 0 ) { + return header; + } + + node = ListNext( &msg->headers, node ); + } + return NULL; +} + +/************************************************************************ +* Function : httpmsg_find_hdr +* +* Parameters : +* IN http_message_t* msg ; HTTP Message Object +* IN int header_name_id ; Header Name ID to be compared with +* OUT memptr* value ; Buffer to get the ouput to. +* +* Description : Finds header from a list, with the given 'name_id'. +* +* Return : http_header_t* - Pointer to a header on success; * +* NULL on failure +* +* Note : +************************************************************************/ +http_header_t * +httpmsg_find_hdr( IN http_message_t * msg, + IN int header_name_id, + OUT memptr * value ) +{ + http_header_t header; // temp header for searching + + ListNode *node; + + http_header_t *data; + + header.name_id = header_name_id; + + node = ListFind( &msg->headers, NULL, &header ); + + if( node == NULL ) { + return NULL; + } + + data = ( http_header_t * ) node->item; + + if( value != NULL ) { + value->buf = data->value.buf; + value->length = data->value.length; + } + + return data; +} + +/***********************************************************************/ + +/************* http_parser_t **************/ + +/***********************************************************************/ + +/************************************************************************ +* Function : skip_blank_lines +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* +* Description : skips blank lines at the start of a msg. +* +* Return : int ; +* +* Note : +************************************************************************/ +static XINLINE int +skip_blank_lines( INOUT scanner_t * scanner ) +{ + memptr token; + token_type_t tok_type; + parse_status_t status; + + // skip ws, crlf + do { + status = scanner_get_token( scanner, &token, &tok_type ); + } while( status == PARSE_OK && + ( tok_type == TT_WHITESPACE || tok_type == TT_CRLF ) ); + + if( status == PARSE_OK ) { + // pushback a non-whitespace token + scanner->cursor -= token.length; + //scanner_pushback( scanner, token.length ); + } + + return status; +} + +/************************************************************************ +* Function : skip_lws +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* +* Description : skip linear whitespace. +* +* Return : int ; +* PARSE_OK: (LWS)* removed from input +* PARSE_FAILURE: bad input +* PARSE_INCOMPLETE: incomplete input +* +* Note : +************************************************************************/ +static XINLINE int +skip_lws( INOUT scanner_t * scanner ) +{ + memptr token; + token_type_t tok_type; + parse_status_t status; + size_t save_pos; + xboolean matched; + + do { + save_pos = scanner->cursor; + matched = FALSE; + + // get CRLF or WS + status = scanner_get_token( scanner, &token, &tok_type ); + if( status == PARSE_OK ) { + if( tok_type == TT_CRLF ) { + // get WS + status = scanner_get_token( scanner, &token, &tok_type ); + } + + if( status == PARSE_OK && tok_type == TT_WHITESPACE ) { + matched = TRUE; + } else { + // did not match LWS; pushback token(s) + scanner->cursor = save_pos; + } + } + } while( matched ); + + // if entire msg is loaded, ignore an 'incomplete' warning + if( status == PARSE_INCOMPLETE && scanner->entire_msg_loaded ) { + status = PARSE_OK; + } + + return status; +} + +/************************************************************************ +* Function : match_non_ws_string +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* OUT memptr* str ; Buffer to get the scanner buffer contents. +* +* Description : Match a string without whitespace or CRLF (%S) +* +* Return : XINLINE parse_status_t ; +* PARSE_OK +* PARSE_NO_MATCH +* PARSE_FAILURE +* PARSE_INCOMPLETE +* +* Note : +************************************************************************/ +static XINLINE parse_status_t +match_non_ws_string( INOUT scanner_t * scanner, + OUT memptr * str ) +{ + memptr token; + token_type_t tok_type; + parse_status_t status; + xboolean done = FALSE; + size_t save_cursor; + + save_cursor = scanner->cursor; + + str->length = 0; + str->buf = scanner_get_str( scanner ); // point to next char in input + + while( !done ) { + status = scanner_get_token( scanner, &token, &tok_type ); + if( status == PARSE_OK && + tok_type != TT_WHITESPACE && tok_type != TT_CRLF ) { + // append non-ws token + str->length += token.length; + } else { + done = TRUE; + } + } + + if( status == PARSE_OK ) { + // last token was WS; push it back in + scanner->cursor -= token.length; + } + // tolerate 'incomplete' msg + if( status == PARSE_OK || + ( status == PARSE_INCOMPLETE && scanner->entire_msg_loaded ) + ) { + if( str->length == 0 ) { + // no strings found + return PARSE_NO_MATCH; + } else { + return PARSE_OK; + } + } else { + // error -- pushback tokens + scanner->cursor = save_cursor; + return status; + } +} + +/************************************************************************ +* Function : match_raw_value +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* OUT memptr* raw_value ; Buffer to get the scanner buffer +* contents +* +* Description : Matches a raw value in a the input; value's length +* can be 0 or more. Whitespace after value is trimmed. On success, +* scanner points the CRLF that ended the value +* +* Return : parse_status_t ; +* PARSE_OK +* PARSE_INCOMPLETE +* PARSE_FAILURE +* +* Note : +************************************************************************/ +static XINLINE parse_status_t +match_raw_value( INOUT scanner_t * scanner, + OUT memptr * raw_value ) +{ + memptr token; + token_type_t tok_type; + parse_status_t status; + xboolean done = FALSE; + xboolean saw_crlf = FALSE; + size_t pos_at_crlf = 0; + size_t save_pos; + char c; + + save_pos = scanner->cursor; + + // value points to start of input + raw_value->buf = scanner_get_str( scanner ); + raw_value->length = 0; + + while( !done ) { + status = scanner_get_token( scanner, &token, &tok_type ); + if( status == PARSE_OK ) { + if( !saw_crlf ) { + if( tok_type == TT_CRLF ) { + // CRLF could end value + saw_crlf = TRUE; + + // save input position at start of CRLF + pos_at_crlf = scanner->cursor - token.length; + } + // keep appending value + raw_value->length += token.length; + } else // already seen CRLF + { + if( tok_type == TT_WHITESPACE ) { + // start again; forget CRLF + saw_crlf = FALSE; + raw_value->length += token.length; + } else { + // non-ws means value ended just before CRLF + done = TRUE; + + // point to the crlf which ended the value + scanner->cursor = pos_at_crlf; + } + } + } else { + // some kind of error; restore scanner position + scanner->cursor = save_pos; + done = TRUE; + } + } + + if( status == PARSE_OK ) { + // trim whitespace on right side of value + while( raw_value->length > 0 ) { + // get last char + c = raw_value->buf[raw_value->length - 1]; + + if( c != ' ' && c != '\t' && + c != TOKCHAR_CR && c != TOKCHAR_LF ) { + // done; no more whitespace + break; + } + // remove whitespace + raw_value->length--; + } + } + + return status; +} + +/************************************************************************ +* Function: match_int +* +* Parameters: +* INOUT scanner_t* scanner ; Scanner Object +* IN int base : Base of number in the string; +* valid values: 10 or 16 +* OUT int* value ; Number stored here +* +* Description: Matches an unsigned integer value in the input. The +* integer is returned in 'value'. Except for PARSE_OK result, the +* scanner's cursor is moved back to its original position on error. +* +* Returns: +* PARSE_OK +* PARSE_NO_MATCH -- got different kind of token +* PARSE_FAILURE -- bad input +* PARSE_INCOMPLETE +************************************************************************/ +static XINLINE int +match_int( INOUT scanner_t * scanner, + IN int base, + OUT int *value ) +{ + memptr token; + token_type_t tok_type; + parse_status_t status; + int num; + char *end_ptr; + size_t save_pos; + + save_pos = scanner->cursor; + + status = scanner_get_token( scanner, &token, &tok_type ); + if( status == PARSE_OK ) { + if( tok_type == TT_IDENTIFIER ) { + errno = 0; + + num = strtol( token.buf, &end_ptr, base ); + if( ( num < 0 ) + // all and only those chars in token should be used for num + || ( end_ptr != token.buf + token.length ) + || ( ( num == LONG_MIN || num == LONG_MAX ) + && ( errno == ERANGE ) ) + ) { + status = PARSE_NO_MATCH; + } + + *value = num; // save result + } else { + status = PARSE_NO_MATCH; // token must be an identifier + } + } + + if( status != PARSE_OK ) { + // restore scanner position for bad values + scanner->cursor = save_pos; + } + + return status; +} + +/************************************************************************ +* Function: read_until_crlf +* +* Parameters: +* INOUT scanner_t* scanner ; Scanner Object +* OUT memptr* str ; Buffer to copy scanner buffer contents to +* +* Description: Reads data until end of line; the crlf at the end of +* line is not consumed. On error, scanner is not restored. On +* success, 'str' points to a string that runs until eol +* +* Returns: +* PARSE_OK +* PARSE_FAILURE +* PARSE_INCOMPLETE +************************************************************************/ +static XINLINE int +read_until_crlf( INOUT scanner_t * scanner, + OUT memptr * str ) +{ + memptr token; + token_type_t tok_type; + parse_status_t status; + size_t start_pos; + + start_pos = scanner->cursor; + str->buf = scanner_get_str( scanner ); + + // read until we hit a crlf + do { + status = scanner_get_token( scanner, &token, &tok_type ); + } while( status == PARSE_OK && tok_type != TT_CRLF ); + + if( status == PARSE_OK ) { + // pushback crlf in stream + scanner->cursor -= token.length; + + // str should include all strings except crlf at the end + str->length = scanner->cursor - start_pos; + } + + return status; +} + +/************************************************************************ +* Function: skip_to_end_of_header +* +* Parameters: +* INOUT scanner_t* scanner ; Scanner Object +* +* Description: Skip to end of header +* +* Returns: +* PARSE_OK +* PARSE_FAILURE +* PARSE_INCOMPLETE +************************************************************************/ +static XINLINE int +skip_to_end_of_header( INOUT scanner_t * scanner ) +{ + memptr dummy_raw_value; + parse_status_t status; + + status = match_raw_value( scanner, &dummy_raw_value ); + return status; +} + +/************************************************************************ +* Function: match_char +* +* Parameters: +* INOUT scanner_t* scanner ; Scanner Object +* IN char c ; Character to be compared with +* IN xboolean case_sensitive; Flag indicating whether comparison should +* be case sensitive +* +* Description: Compares a character to the next char in the scanner; +* on error, scanner chars are not restored +* +* Returns: +* PARSE_OK +* PARSE_NO_MATCH +* PARSE_INCOMPLETE +************************************************************************/ +static XINLINE parse_status_t +match_char( INOUT scanner_t * scanner, + IN char c, + IN xboolean case_sensitive ) +{ + char scan_char; + + if( scanner->cursor >= scanner->msg->length ) { + return PARSE_INCOMPLETE; + } + // read next char from scanner + scan_char = scanner->msg->buf[scanner->cursor++]; + + if( case_sensitive ) { + return c == scan_char ? PARSE_OK : PARSE_NO_MATCH; + } else { + return tolower( c ) == tolower( scan_char ) ? + PARSE_OK : PARSE_NO_MATCH; + } +} + +//////////////////////////////////////////////////////////////////////// +// args for ... +// %d, int * (31-bit positive integer) +// %x, int * (31-bit postive number encoded as hex) +// %s, memptr* (simple identifier) +// %q, memptr* (quoted string) +// %S, memptr* (non-whitespace string) +// %R, memptr* (raw value) +// %U, uri_type* (url) +// %L, memptr* (string until end of line) +// %P, int * (current index of the string being scanned) +// +// no args for +// ' ' LWS* +// \t whitespace +// "%%" matches '%' +// "% " matches ' ' +// %c matches CRLF +// %i ignore case in literal matching +// %n case-sensitive matching in literals +// %w optional whitespace; (similar to '\t', +// except whitespace is optional) +// %0 (zero) match null-terminator char '\0' +// (can only be used as last char in fmt) +// use only in matchstr(), not match() +// other chars match literally +// +// returns: +// PARSE_OK +// PARSE_INCOMPLETE +// PARSE_FAILURE -- bad input +// PARSE_NO_MATCH -- input does not match pattern + +/************************************************************************ +* Function : vfmatch +* +* Parameters : +* INOUT scanner_t* scanner ; Scanner Object +* IN const char* fmt ; Pattern Format +* va_list argp ; List of variable arguments +* +* Description : Extracts variable parameters depending on the passed +* in format parameter. Parses data also based on the passed in +* format parameter. +* +* Return : int ; +* PARSE_OK +* PARSE_INCOMPLETE +* PARSE_FAILURE - bad input +* PARSE_NO_MATCH - input does not match pattern +* +* Note : +************************************************************************/ +static int +vfmatch( INOUT scanner_t * scanner, + IN const char *fmt, + va_list argp ) +{ + char c; + const char *fmt_ptr = fmt; + parse_status_t status; + memptr *str_ptr; + memptr temp_str; + int *int_ptr; + uri_type *uri_ptr; + size_t save_pos; + int stat; + xboolean case_sensitive = TRUE; + memptr token; + token_type_t tok_type; + int base; + + assert( scanner != NULL ); + assert( fmt != NULL ); + + // save scanner pos; to aid error recovery + save_pos = scanner->cursor; + + status = PARSE_OK; + while( ( ( c = *fmt_ptr++ ) != 0 ) && ( status == PARSE_OK ) + ) { + if( c == '%' ) { + c = *fmt_ptr++; + + switch ( c ) { + + case 'R': // raw value + str_ptr = va_arg( argp, memptr * ); + assert( str_ptr != NULL ); + status = match_raw_value( scanner, str_ptr ); + break; + + case 's': // simple identifier + str_ptr = va_arg( argp, memptr * ); + assert( str_ptr != NULL ); + status = scanner_get_token( scanner, str_ptr, + &tok_type ); + if( status == PARSE_OK && tok_type != TT_IDENTIFIER ) { + // not an identifier + status = PARSE_NO_MATCH; + } + break; + + case 'c': // crlf + status = scanner_get_token( scanner, + &token, &tok_type ); + if( status == PARSE_OK && tok_type != TT_CRLF ) { + // not CRLF token + status = PARSE_NO_MATCH; + } + break; + + case 'd': // integer + case 'x': // hex number + int_ptr = va_arg( argp, int * ); + + assert( int_ptr != NULL ); + base = ( c == 'd' ? 10 : 16 ); + status = match_int( scanner, base, int_ptr ); + break; + + case 'S': // non-whitespace string + case 'U': // uri + if( c == 'S' ) { + str_ptr = va_arg( argp, memptr * ); + } else { + str_ptr = &temp_str; + } + assert( str_ptr != NULL ); + status = match_non_ws_string( scanner, str_ptr ); + if( c == 'U' && status == PARSE_OK ) { + uri_ptr = va_arg( argp, uri_type * ); + assert( uri_ptr != NULL ); + stat = parse_uri( str_ptr->buf, str_ptr->length, + uri_ptr ); + if( stat != HTTP_SUCCESS ) { + status = PARSE_NO_MATCH; + } + } + break; + + case 'L': // string till eol + str_ptr = va_arg( argp, memptr * ); + assert( str_ptr != NULL ); + status = read_until_crlf( scanner, str_ptr ); + break; + + case ' ': // match space + case '%': // match percentage symbol + status = match_char( scanner, c, case_sensitive ); + break; + + case 'n': // case-sensitive match + case_sensitive = TRUE; + break; + + case 'i': // ignore case + case_sensitive = FALSE; + break; + + case 'q': // quoted string + str_ptr = ( memptr * ) va_arg( argp, memptr * ); + status = + scanner_get_token( scanner, str_ptr, &tok_type ); + if( status == PARSE_OK && tok_type != TT_QUOTEDSTRING ) { + status = PARSE_NO_MATCH; // not a quoted string + } + break; + + case 'w': // optional whitespace + status = scanner_get_token( scanner, + &token, &tok_type ); + if( status == PARSE_OK && tok_type != TT_WHITESPACE ) { + // restore non-whitespace token + scanner->cursor -= token.length; + } + break; + + case 'P': // current pos of scanner + int_ptr = va_arg( argp, int * ); + + assert( int_ptr != NULL ); + *int_ptr = scanner->cursor; + break; + + // valid only in matchstr() + case '0': // end of msg? + // check that we are 1 beyond last char + if( scanner->cursor == scanner->msg->length && + scanner->msg->buf[scanner->cursor] == '\0' ) { + status = PARSE_OK; + } else { + status = PARSE_NO_MATCH; + } + break; + + default: + assert( 0 ); // unknown option + } + } else { + switch ( c ) { + case ' ': // LWS* + status = skip_lws( scanner ); + break; + + case '\t': // Whitespace + status = scanner_get_token( scanner, + &token, &tok_type ); + if( status == PARSE_OK && tok_type != TT_WHITESPACE ) { + // not whitespace token + status = PARSE_NO_MATCH; + } + break; + + default: // match characters + { + status = match_char( scanner, c, case_sensitive ); + } + } + } + } + + if( status != PARSE_OK ) { + // on error, restore original scanner pos + scanner->cursor = save_pos; + } + + return status; +} + +/************************************************************************ +* Function: match +* +* Parameters: +* INOUT scanner_t* scanner ; Scanner Object +* IN const char* fmt; Pattern format +* ... +* +* Description: matches a variable parameter list and takes necessary +* actions based on the data type specified. +* +* Returns: +* PARSE_OK +* PARSE_NO_MATCH +* PARSE_INCOMPLETE +************************************************************************/ +static int +match( INOUT scanner_t * scanner, + IN const char *fmt, + ... ) +{ + int ret_code; + va_list args; + + va_start( args, fmt ); + ret_code = vfmatch( scanner, fmt, args ); + va_end( args ); + + return ret_code; +} + +/************************************************************************ +* Function: matchstr +* +* Parameters: +* IN char *str ; String to be matched +* IN size_t slen ; Length of the string +* IN const char* fmt ; Pattern format +* ... +* +* Description: Matches a variable parameter list with a string +* and takes actions based on the data type specified. +* +* Returns: +* PARSE_OK +* PARSE_NO_MATCH -- failure to match pattern 'fmt' +* PARSE_FAILURE -- 'str' is bad input +************************************************************************/ +int +matchstr( IN char *str, + IN size_t slen, + IN const char *fmt, + ... ) +{ + int ret_code; + char save_char; + scanner_t scanner; + membuffer buf; + va_list arg_list; + + // null terminate str + save_char = str[slen]; + str[slen] = '\0'; + + membuffer_init( &buf ); + + // under no circumstances should this buffer be modifed because its memory + // might have not come from malloc() + membuffer_attach( &buf, str, slen ); + + scanner_init( &scanner, &buf ); + scanner.entire_msg_loaded = TRUE; + + va_start( arg_list, fmt ); + ret_code = vfmatch( &scanner, fmt, arg_list ); + va_end( arg_list ); + + // restore str + str[slen] = save_char; + + // don't destroy buf + + return ret_code; +} + +/************************************************************************ +* Function: parser_init +* +* Parameters: +* OUT http_parser_t* parser ; HTTP Parser object +* +* Description: Initializes the parser object. +* +* Returns: +* void +************************************************************************/ +static XINLINE void +parser_init( OUT http_parser_t * parser ) +{ + memset( parser, 0, sizeof( http_parser_t ) ); + + parser->http_error_code = HTTP_BAD_REQUEST; // err msg by default + parser->ent_position = ENTREAD_DETERMINE_READ_METHOD; + parser->valid_ssdp_notify_hack = FALSE; + + httpmsg_init( &parser->msg ); + scanner_init( &parser->scanner, &parser->msg.msg ); +} + +/************************************************************************ +* Function: parser_parse_requestline +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Get HTTP Method, URL location and version information. +* +* Returns: +* PARSE_OK +* PARSE_SUCCESS +* PARSE_FAILURE +************************************************************************/ +static parse_status_t +parser_parse_requestline( INOUT http_parser_t * parser ) +{ + parse_status_t status; + http_message_t *hmsg = &parser->msg; + memptr method_str; + memptr version_str; + int index; + char save_char; + int num_scanned; + memptr url_str; + + assert( parser->position == POS_REQUEST_LINE ); + + status = skip_blank_lines( &parser->scanner ); + if( status != PARSE_OK ) { + return status; + } + //simple get http 0.9 as described in http 1.0 spec + + status = + match( &parser->scanner, "%s\t%S%w%c", &method_str, &url_str ); + + if( status == PARSE_OK ) { + + index = + map_str_to_int( method_str.buf, method_str.length, + Http_Method_Table, NUM_HTTP_METHODS, TRUE ); + + if( index < 0 ) { + // error; method not found + parser->http_error_code = HTTP_NOT_IMPLEMENTED; + return PARSE_FAILURE; + } + + if( Http_Method_Table[index].id != HTTPMETHOD_GET ) { + parser->http_error_code = HTTP_BAD_REQUEST; + return PARSE_FAILURE; + } + + hmsg->method = HTTPMETHOD_SIMPLEGET; + + // store url + hmsg->urlbuf = str_alloc( url_str.buf, url_str.length ); + if( hmsg->urlbuf == NULL ) { + // out of mem + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + if( parse_uri( hmsg->urlbuf, url_str.length, &hmsg->uri ) != + HTTP_SUCCESS ) { + return PARSE_FAILURE; + } + + parser->position = POS_COMPLETE; // move to headers + + return PARSE_SUCCESS; + } + + status = match( &parser->scanner, + "%s\t%S\t%ihttp%w/%w%L%c", &method_str, &url_str, + &version_str ); + if( status != PARSE_OK ) { + return status; + } + // store url + hmsg->urlbuf = str_alloc( url_str.buf, url_str.length ); + if( hmsg->urlbuf == NULL ) { + // out of mem + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + if( parse_uri( hmsg->urlbuf, url_str.length, &hmsg->uri ) != + HTTP_SUCCESS ) { + return PARSE_FAILURE; + } + // scan version + save_char = version_str.buf[version_str.length]; + version_str.buf[version_str.length] = '\0'; // null-terminate + num_scanned = sscanf( version_str.buf, "%d . %d", + &hmsg->major_version, &hmsg->minor_version ); + version_str.buf[version_str.length] = save_char; // restore + if( num_scanned != 2 || + hmsg->major_version < 0 || hmsg->minor_version < 0 ) { + // error; bad http version + return PARSE_FAILURE; + } + + index = + map_str_to_int( method_str.buf, method_str.length, + Http_Method_Table, NUM_HTTP_METHODS, TRUE ); + if( index < 0 ) { + // error; method not found + parser->http_error_code = HTTP_NOT_IMPLEMENTED; + return PARSE_FAILURE; + } + + hmsg->method = Http_Method_Table[index].id; + parser->position = POS_HEADERS; // move to headers + + return PARSE_OK; +} + +/************************************************************************ +* Function: parser_parse_responseline +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Get HTTP Method, URL location and version information. +* +* Returns: +* PARSE_OK +* PARSE_SUCCESS +* PARSE_FAILURE +************************************************************************/ +parse_status_t +parser_parse_responseline( INOUT http_parser_t * parser ) +{ + parse_status_t status; + http_message_t *hmsg = &parser->msg; + memptr line; + char save_char; + int num_scanned; + int i; + char *p; + + assert( parser->position == POS_RESPONSE_LINE ); + + status = skip_blank_lines( &parser->scanner ); + if( status != PARSE_OK ) { + return status; + } + // response line + //status = match( &parser->scanner, "%ihttp%w/%w%d\t.\t%d\t%d\t%L%c", + // &hmsg->major_version, &hmsg->minor_version, + // &hmsg->status_code, &hmsg->status_msg ); + + status = match( &parser->scanner, "%ihttp%w/%w%L%c", &line ); + if( status != PARSE_OK ) { + return status; + } + + save_char = line.buf[line.length]; + line.buf[line.length] = '\0'; // null-terminate + + // scan http version and ret code + num_scanned = sscanf( line.buf, "%d . %d %d", + &hmsg->major_version, &hmsg->minor_version, + &hmsg->status_code ); + + line.buf[line.length] = save_char; // restore + + if( num_scanned != 3 || + hmsg->major_version < 0 || + hmsg->minor_version < 0 || hmsg->status_code < 0 ) { + // bad response line + return PARSE_FAILURE; + } + // + // point to status msg + // + + p = line.buf; + + // skip 3 ints + for( i = 0; i < 3; i++ ) { + // go to start of num + while( !isdigit( *p ) ) { + p++; + } + + // skip int + while( isdigit( *p ) ) { + p++; + } + } + + // whitespace must exist after status code + if( *p != ' ' && *p != '\t' ) { + return PARSE_FAILURE; + } + // skip whitespace + while( *p == ' ' || *p == '\t' ) { + p++; + } + + // now, p is at start of status msg + if( membuffer_assign( &hmsg->status_msg, p, + line.length - ( p - line.buf ) ) != 0 ) { + // out of mem + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + + parser->position = POS_HEADERS; // move to headers + + return PARSE_OK; +} + +/************************************************************************ +* Function: parser_parse_headers +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Get HTTP Method, URL location and version information. +* +* Returns: +* PARSE_OK +* PARSE_SUCCESS +* PARSE_FAILURE +************************************************************************/ +parse_status_t +parser_parse_headers( INOUT http_parser_t * parser ) +{ + parse_status_t status; + memptr token; + memptr hdr_value; + token_type_t tok_type; + scanner_t *scanner = &parser->scanner; + size_t save_pos; + http_header_t *header; + int header_id; + int ret = 0; + int index; + http_header_t *orig_header; + char save_char; + int ret2; + + assert( parser->position == POS_HEADERS || + parser->ent_position == ENTREAD_CHUNKY_HEADERS ); + + while( TRUE ) { + save_pos = scanner->cursor; + + // + // check end of headers + // + status = scanner_get_token( scanner, &token, &tok_type ); + if( status != PARSE_OK ) { + return status; + } + + if( tok_type == TT_CRLF ) { + + // end of headers + if( ( parser->msg.is_request ) + && ( parser->msg.method == HTTPMETHOD_POST ) ) { + parser->position = POS_COMPLETE; //post entity parsing + //is handled separately + return PARSE_SUCCESS; + } + + parser->position = POS_ENTITY; // read entity next + return PARSE_OK; + } + // + // not end; read header + // + if( tok_type != TT_IDENTIFIER ) { + return PARSE_FAILURE; // didn't see header name + } + + status = match( scanner, " : %R%c", &hdr_value ); + if( status != PARSE_OK ) { + // pushback tokens; useful only on INCOMPLETE error + scanner->cursor = save_pos; + return status; + } + // + // add header + // + + // find header + index = map_str_to_int( token.buf, token.length, Http_Header_Names, + NUM_HTTP_HEADER_NAMES, FALSE ); + if( index != -1 ) { + + //Check if it is a soap header + if( Http_Header_Names[index].id == HDR_SOAPACTION ) { + parser->msg.method = SOAPMETHOD_POST; + } + + header_id = Http_Header_Names[index].id; + orig_header = + httpmsg_find_hdr( &parser->msg, header_id, NULL ); + } else { + header_id = HDR_UNKNOWN; + + save_char = token.buf[token.length]; + token.buf[token.length] = '\0'; + + orig_header = httpmsg_find_hdr_str( &parser->msg, token.buf ); + + token.buf[token.length] = save_char; // restore + } + + if( orig_header == NULL ) { + // + // add new header + // + + header = ( http_header_t * ) malloc( sizeof( http_header_t ) ); + if( header == NULL ) { + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + membuffer_init( &header->name_buf ); + membuffer_init( &header->value ); + + // value can be 0 length + if( hdr_value.length == 0 ) { + hdr_value.buf = "\0"; + hdr_value.length = 1; + } + // save in header in buffers + if( membuffer_assign + ( &header->name_buf, token.buf, token.length ) != 0 + || membuffer_assign( &header->value, hdr_value.buf, + hdr_value.length ) != 0 ) { + // not enuf mem + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + + header->name.buf = header->name_buf.buf; + header->name.length = header->name_buf.length; + header->name_id = header_id; + + ListAddTail( &parser->msg.headers, header ); + + //NNS: ret = dlist_append( &parser->msg.headers, header ); + if( ret == UPNP_E_OUTOF_MEMORY ) { + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + } else if( hdr_value.length > 0 ) { + // + // append value to existing header + // + + // append space + ret = membuffer_append_str( &orig_header->value, ", " ); + + // append continuation of header value + ret2 = membuffer_append( &orig_header->value, + hdr_value.buf, hdr_value.length ); + + if( ret == UPNP_E_OUTOF_MEMORY || ret2 == UPNP_E_OUTOF_MEMORY ) { + // not enuf mem + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + } + } // end while + +} + +//////////////////////////////////////////////////////////////////////// +#ifdef HIGHLY_UNLIKELY +// ************** +static parse_status_t +parser_parse_headers_old( INOUT http_parser_t * parser ) +{ + parse_status_t status; + memptr token; + memptr hdr_value; + token_type_t tok_type; + scanner_t *scanner = &parser->scanner; + size_t save_pos; + http_header_t *header; + int header_id; + int ret = 0; + int index; + http_header_t *orig_header; + char save_char; + int ret2, + ret3; + + assert( parser->position == POS_HEADERS || + parser->ent_position == ENTREAD_CHUNKY_HEADERS ); + + while( TRUE ) { + save_pos = scanner->cursor; + + // + // check end of headers + // + status = scanner_get_token( scanner, &token, &tok_type ); + if( status != PARSE_OK ) { + return status; + } + + if( tok_type == TT_CRLF ) { + // end of headers + parser->position = POS_ENTITY; // read entity next + return PARSE_OK; + } + // + // not end; read header + // + if( tok_type != TT_IDENTIFIER ) { + return PARSE_FAILURE; // didn't see header name + } + + status = match( scanner, " : %R%c", &hdr_value ); + if( status != PARSE_OK ) { + // pushback tokens; useful only on INCOMPLETE error + scanner->cursor = save_pos; + return status; + } + + // + // add header + // + + // find header + index = map_str_to_int( token.buf, token.length, Http_Header_Names, + NUM_HTTP_HEADER_NAMES, FALSE ); + if( index != -1 ) { + header_id = Http_Header_Names[index].id; + + orig_header = + httpmsg_find_hdr( &parser->msg, header_id, NULL ); + } else { + header_id = HDR_UNKNOWN; + + save_char = token.buf[token.length]; + token.buf[token.length] = '\0'; + + orig_header = httpmsg_find_hdr_str( &parser->msg, token.buf ); + + token.buf[token.length] = save_char; // restore + } + + if( orig_header == NULL ) { + // + // add new header + // + + header = ( http_header_t * ) malloc( sizeof( http_header_t ) ); + if( header == NULL ) { + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + membuffer_init( &header->multi_hdr_buf ); + + header->name = token; + header->value = hdr_value; + header->name_id = header_id; + + ret = dlist_append( &parser->msg.headers, header ); + if( ret == UPNP_E_OUTOF_MEMORY ) { + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + } else if( hdr_value.length > 0 ) { + // + // append value to existing header + // + + if( orig_header->multi_hdr_buf.buf == NULL ) { + // store in buffer + ret = membuffer_append( &orig_header->multi_hdr_buf, + orig_header->value.buf, + orig_header->value.length ); + } + // append space + ret2 = + membuffer_append( &orig_header->multi_hdr_buf, ", ", 2 ); + + // append continuation of header value + ret3 = membuffer_append( &orig_header->multi_hdr_buf, + hdr_value.buf, hdr_value.length ); + + if( ret == UPNP_E_OUTOF_MEMORY || + ret2 == UPNP_E_OUTOF_MEMORY || + ret3 == UPNP_E_OUTOF_MEMORY ) { + // not enuf mem + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + // header value points to allocated buf + orig_header->value.buf = orig_header->multi_hdr_buf.buf; + orig_header->value.length = orig_header->multi_hdr_buf.length; + } + } // end while + +} +#endif +// ****************************** + +/************************************************************************ +* Function: parser_parse_entity_using_clen +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: reads entity using content-length +* +* Returns: +* PARSE_INCOMPLETE +* PARSE_FAILURE -- entity length > content-length value +* PARSE_SUCCESS +************************************************************************/ +static XINLINE parse_status_t +parser_parse_entity_using_clen( INOUT http_parser_t * parser ) +{ + //int entity_length; + + assert( parser->ent_position == ENTREAD_USING_CLEN ); + + // determine entity (i.e. body) length so far + //entity_length = parser->msg.msg.length - parser->entity_start_position; + parser->msg.entity.length = + parser->msg.msg.length - parser->entity_start_position; + + if( parser->msg.entity.length < parser->content_length ) { + // more data to be read + return PARSE_INCOMPLETE; + } else { + if( parser->msg.entity.length > parser->content_length ) { + // silently discard extra data + parser->msg.msg.buf[parser->entity_start_position + + parser->content_length] = '\0'; + } + // save entity length + parser->msg.entity.length = parser->content_length; + + // save entity start ptr; (the very last thing to do) + parser->msg.entity.buf = parser->msg.msg.buf + + parser->entity_start_position; + + // done reading entity + parser->position = POS_COMPLETE; + return PARSE_SUCCESS; + } +} + +/************************************************************************ +* Function: parser_parse_chunky_body +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Read data in the chunks +* +* Returns: +* PARSE_INCOMPLETE +* PARSE_FAILURE -- entity length > content-length value +* PARSE_SUCCESS +************************************************************************/ +static XINLINE parse_status_t +parser_parse_chunky_body( INOUT http_parser_t * parser ) +{ + parse_status_t status; + size_t save_pos; + + // if 'chunk_size' of bytes have been read; read next chunk + if( ( int )( parser->msg.msg.length - parser->scanner.cursor ) >= + parser->chunk_size ) { + // move to next chunk + parser->scanner.cursor += parser->chunk_size; + save_pos = parser->scanner.cursor; + + //discard CRLF + status = match( &parser->scanner, "%c" ); + if( status != PARSE_OK ) { + parser->scanner.cursor -= parser->chunk_size; //move back + //parser->scanner.cursor = save_pos; + return status; + } + + membuffer_delete( &parser->msg.msg, save_pos, + ( parser->scanner.cursor - save_pos ) ); + parser->scanner.cursor = save_pos; + parser->msg.entity.length += parser->chunk_size; //update temp + parser->ent_position = ENTREAD_USING_CHUNKED; + return PARSE_CONTINUE_1; + } else { + return PARSE_INCOMPLETE; // need more data for chunk + } +} + +/************************************************************************ +* Function: parser_parse_chunky_headers +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Read headers at the end of the chunked entity +* +* Returns: +* PARSE_INCOMPLETE +* PARSE_FAILURE -- entity length > content-length value +* PARSE_SUCCESS +************************************************************************/ +static XINLINE parse_status_t +parser_parse_chunky_headers( INOUT http_parser_t * parser ) +{ + parse_status_t status; + size_t save_pos; + + save_pos = parser->scanner.cursor; + status = parser_parse_headers( parser ); + if( status == PARSE_OK ) { + // finally, done with the whole msg + parser->position = POS_COMPLETE; + + // save entity start ptr as the very last thing to do + parser->msg.entity.buf = parser->msg.msg.buf + + parser->entity_start_position; + + membuffer_delete( &parser->msg.msg, save_pos, + ( parser->scanner.cursor - save_pos ) ); + parser->scanner.cursor = save_pos; + + return PARSE_SUCCESS; + } else { + return status; + } +} + +/************************************************************************ +* Function: parser_parse_chunky_entity +* +* Parameters: +* INOUT http_parser_t* parser - HTTP Parser Object +* +* Description: Read headers at the end of the chunked entity +* +* Returns: +* PARSE_INCOMPLETE +* PARSE_FAILURE -- entity length > content-length value +* PARSE_SUCCESS +* PARSE_CONTINUE_1 +************************************************************************/ +static XINLINE parse_status_t +parser_parse_chunky_entity( INOUT http_parser_t * parser ) +{ + scanner_t *scanner = &parser->scanner; + parse_status_t status; + size_t save_pos; + memptr dummy; + + assert( parser->ent_position == ENTREAD_USING_CHUNKED ); + + save_pos = scanner->cursor; + + // get size of chunk, discard extension, discard CRLF + status = match( scanner, "%x%L%c", &parser->chunk_size, &dummy ); + if( status != PARSE_OK ) { + scanner->cursor = save_pos; + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "CHUNK COULD NOT BE PARSED\n" ); ) + return status; + } + // remove chunk info just matched; just retain data + membuffer_delete( &parser->msg.msg, save_pos, + ( scanner->cursor - save_pos ) ); + scanner->cursor = save_pos; // adjust scanner too + + if( parser->chunk_size == 0 ) { + // done reading entity; determine length of entity + parser->msg.entity.length = parser->scanner.cursor - + parser->entity_start_position; + + // read entity headers + parser->ent_position = ENTREAD_CHUNKY_HEADERS; + } else { + // read chunk body + parser->ent_position = ENTREAD_CHUNKY_BODY; + } + + return PARSE_CONTINUE_1; // continue to reading body +} + +/************************************************************************ +* Function: parser_parse_entity_until_close +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Read headers at the end of the chunked entity +* +* Returns: +* PARSE_INCOMPLETE_ENTITY +************************************************************************/ +static XINLINE parse_status_t +parser_parse_entity_until_close( INOUT http_parser_t * parser ) +{ + size_t cursor; + + assert( parser->ent_position == ENTREAD_UNTIL_CLOSE ); + + // eat any and all data + cursor = parser->msg.msg.length; + + // update entity length + parser->msg.entity.length = cursor - parser->entity_start_position; + + // update pointer + parser->msg.entity.buf = + parser->msg.msg.buf + parser->entity_start_position; + + parser->scanner.cursor = cursor; + + return PARSE_INCOMPLETE_ENTITY; // add anything +} + +/************************************************************************ +* Function: parser_get_entity_read_method +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Determines method to read entity +* +* Returns: +* PARSE_OK +* PARSE_FAILURE +* PARSE_COMPLETE -- no more reading to do +************************************************************************/ +XINLINE parse_status_t +parser_get_entity_read_method( INOUT http_parser_t * parser ) +{ + http_message_t *hmsg = &parser->msg; + int response_code; + memptr hdr_value; + + assert( parser->ent_position == ENTREAD_DETERMINE_READ_METHOD ); + + // entity points to start of msg body + parser->msg.entity.buf = scanner_get_str( &parser->scanner ); + parser->msg.entity.length = 0; + + // remember start of body + parser->entity_start_position = parser->scanner.cursor; + + // std http rules for determining content length + + // * no body for 1xx, 204, 304 and HEAD, GET, + // SUBSCRIBE, UNSUBSCRIBE + if( hmsg->is_request ) { + switch ( hmsg->method ) { + case HTTPMETHOD_HEAD: + case HTTPMETHOD_GET: + //case HTTPMETHOD_POST: + case HTTPMETHOD_SUBSCRIBE: + case HTTPMETHOD_UNSUBSCRIBE: + case HTTPMETHOD_MSEARCH: + // no body; mark as done + parser->position = POS_COMPLETE; + return PARSE_SUCCESS; + break; + + default: + ; // do nothing + } + } else // response + { + response_code = hmsg->status_code; + + if( response_code == 204 || + response_code == 304 || + ( response_code >= 100 && response_code <= 199 ) || + hmsg->request_method == HTTPMETHOD_HEAD || + hmsg->request_method == HTTPMETHOD_MSEARCH || + hmsg->request_method == HTTPMETHOD_SUBSCRIBE || + hmsg->request_method == HTTPMETHOD_UNSUBSCRIBE || + hmsg->request_method == HTTPMETHOD_NOTIFY ) { + parser->position = POS_COMPLETE; + return PARSE_SUCCESS; + } + } + + // * transfer-encoding -- used to indicate chunked data + if( httpmsg_find_hdr( hmsg, HDR_TRANSFER_ENCODING, &hdr_value ) ) { + if( raw_find_str( &hdr_value, "chunked" ) >= 0 ) { + // read method to use chunked transfer encoding + parser->ent_position = ENTREAD_USING_CHUNKED; + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "Found Chunked Encoding ....\n" ); ) + + return PARSE_CONTINUE_1; + } + } + // * use content length + if( httpmsg_find_hdr( hmsg, HDR_CONTENT_LENGTH, &hdr_value ) ) { + parser->content_length = raw_to_int( &hdr_value, 10 ); + if( parser->content_length < 0 ) { + // bad content-length + return PARSE_FAILURE; + } + parser->ent_position = ENTREAD_USING_CLEN; + return PARSE_CONTINUE_1; + } + // * multi-part/byteranges not supported (yet) + + // * read until connection is closed + if( hmsg->is_request ) { + // set hack flag for NOTIFY methods; if set to true this is + // a valid SSDP notify msg + if( hmsg->method == HTTPMETHOD_NOTIFY ) { + parser->valid_ssdp_notify_hack = TRUE; + } + + parser->http_error_code = HTTP_LENGTH_REQUIRED; + return PARSE_FAILURE; + } + + parser->ent_position = ENTREAD_UNTIL_CLOSE; + return PARSE_CONTINUE_1; +} + +/************************************************************************ +* Function: parser_parse_entity +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Determines method to read entity +* +* Returns: +* PARSE_OK +* PARSE_FAILURE +* PARSE_COMPLETE -- no more reading to do +************************************************************************/ +XINLINE parse_status_t +parser_parse_entity( INOUT http_parser_t * parser ) +{ + parse_status_t status = PARSE_OK; + + assert( parser->position == POS_ENTITY ); + + do { + switch ( parser->ent_position ) { + case ENTREAD_USING_CLEN: + status = parser_parse_entity_using_clen( parser ); + break; + + case ENTREAD_USING_CHUNKED: + status = parser_parse_chunky_entity( parser ); + break; + + case ENTREAD_CHUNKY_BODY: + status = parser_parse_chunky_body( parser ); + break; + + case ENTREAD_CHUNKY_HEADERS: + status = parser_parse_chunky_headers( parser ); + break; + + case ENTREAD_UNTIL_CLOSE: + status = parser_parse_entity_until_close( parser ); + break; + + case ENTREAD_DETERMINE_READ_METHOD: + status = parser_get_entity_read_method( parser ); + break; + + default: + assert( 0 ); + } + + } while( status == PARSE_CONTINUE_1 ); + + return status; +} + +/************************************************************************ +* Function: parser_request_init +* +* Parameters: +* OUT http_parser_t* parser ; HTTP Parser object +* +* Description: Initializes parser object for a request +* +* Returns: +* void +************************************************************************/ +void +parser_request_init( OUT http_parser_t * parser ) +{ + parser_init( parser ); + parser->msg.is_request = TRUE; + parser->position = POS_REQUEST_LINE; +} + +/************************************************************************ +* Function: parser_response_init +* +* Parameters: +* OUT http_parser_t* parser ; HTTP Parser object +* IN http_method_t request_method ; Request method +* +* Description: Initializes parser object for a response +* +* Returns: +* void +************************************************************************/ +void +parser_response_init( OUT http_parser_t * parser, + IN http_method_t request_method ) +{ + parser_init( parser ); + parser->msg.is_request = FALSE; + parser->msg.request_method = request_method; + parser->position = POS_RESPONSE_LINE; +} + +/************************************************************************ +* Function: parser_parse +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: The parser function. Depending on the position of the +* parser object the actual parsing function is invoked +* +* Returns: +* void +************************************************************************/ +parse_status_t +parser_parse( INOUT http_parser_t * parser ) +{ + parse_status_t status; + + //takes an http_parser_t with memory already allocated + //in the message + assert( parser != NULL ); + + do { + switch ( parser->position ) { + case POS_ENTITY: + status = parser_parse_entity( parser ); + + break; + + case POS_HEADERS: + status = parser_parse_headers( parser ); + + break; + + case POS_REQUEST_LINE: + status = parser_parse_requestline( parser ); + + break; + + case POS_RESPONSE_LINE: + status = parser_parse_responseline( parser ); + + break; + + default: + { + status = PARSE_FAILURE; + assert( 0 ); + } + } + + } while( status == PARSE_OK ); + + return status; + +} + +/************************************************************************ +* Function: parser_append +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser Object +* IN const char* buf ; buffer to be appended to the parser +* buffer +* IN size_t buf_length ; Size of the buffer +* +* Description: The parser function. Depending on the position of the +* parser object the actual parsing function is invoked +* +* Returns: +* void +************************************************************************/ +parse_status_t +parser_append( INOUT http_parser_t * parser, + IN const char *buf, + IN size_t buf_length ) +{ + int ret_code; + + assert( parser != NULL ); + assert( buf != NULL ); + + // append data to buffer + ret_code = membuffer_append( &parser->msg.msg, buf, buf_length ); + if( ret_code != 0 ) { + // set failure status + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + + return parser_parse( parser ); +} + +/************************************************************************ +********** end of parser *********** +************************************************************************/ + +/************************************************************************ +* Function: raw_to_int +* +* Parameters: +* IN memptr* raw_value ; Buffer to be converted +* IN int base ; Base to use for conversion +* +* Description: Converts raw character data to long-integer value +* +* Returns: +* int +************************************************************************/ +int +raw_to_int( IN memptr * raw_value, + IN int base ) +{ + int num; + char *end_ptr; + + if( raw_value->length == 0 ) { + return -1; + } + + errno = 0; + num = strtol( raw_value->buf, &end_ptr, base ); + if( ( num < 0 ) + // all and only those chars in token should be used for num + || ( end_ptr != raw_value->buf + raw_value->length ) + || ( ( num == LONG_MIN || num == LONG_MAX ) + && ( errno == ERANGE ) ) + ) { + return -1; + } + return num; + +} + +/************************************************************************ +* Function: raw_find_str +* +* Parameters: +* IN memptr* raw_value ; Buffer containg the string +* IN const char* str ; Substring to be found +* +* Description: Find a substring from raw character string buffer +* +* Returns: +* int - index at which the substring is found. +************************************************************************/ +int +raw_find_str( IN memptr * raw_value, + IN const char *str ) +{ + char c; + char *ptr; + + c = raw_value->buf[raw_value->length]; // save + raw_value->buf[raw_value->length] = 0; // null-terminate + + ptr = strstr( raw_value->buf, str ); + + raw_value->buf[raw_value->length] = c; // restore + + if( ptr == 0 ) { + return -1; + } + + return ptr - raw_value->buf; // return index +} + +/************************************************************************ +* Function: method_to_str +* +* Parameters: +* IN http_method_t method ; HTTP method +* +* Description: A wrapper function that maps a method id to a method +* nameConverts a http_method id stored in the HTTP Method +* +* Returns: +* const char* ptr - Ptr to the HTTP Method * +************************************************************************/ +const char * +method_to_str( IN http_method_t method ) +{ + int index; + + index = map_int_to_str( method, Http_Method_Table, NUM_HTTP_METHODS ); + + assert( index != -1 ); + + return index == -1 ? NULL : Http_Method_Table[index].name; +} + +/************************************************************************ +* Function: print_http_headers +* +* Parameters: +* http_message_t* hmsg ; HTTP Message object +* +* Description: +* +* Returns: +* void +************************************************************************/ +void +print_http_headers( http_message_t * hmsg ) +{ + + ListNode *node; + + //NNS: dlist_node *node; + http_header_t *header; + + // print start line + if( hmsg->is_request ) { + //printf( "method = %d, version = %d.%d, url = %.*s\n", + // hmsg->method, hmsg->major_version, hmsg->minor_version, + // hmsg->uri.pathquery.size, hmsg->uri.pathquery.buff); + } else { + // printf( "resp status = %d, version = %d.%d, status msg = %.*s\n", + // hmsg->status_code, hmsg->major_version, hmsg->minor_version, + // (int)hmsg->status_msg.length, hmsg->status_msg.buf); + } + + // print headers + + node = ListHead( &hmsg->headers ); + //NNS: node = dlist_first_node( &hmsg->headers ); + while( node != NULL ) { + + header = ( http_header_t * ) node->item; + //NNS: header = (http_header_t *)node->data; + //printf( "hdr name: %.*s, value: %.*s\n", + // (int)header->name.length, header->name.buf, + // (int)header->value.length, header->value.buf ); + + node = ListNext( &hmsg->headers, node ); + + //NNS: node = dlist_next( &hmsg->headers, node ); + } +} diff --git a/libupnp/upnp/src/genlib/net/http/httpreadwrite.c b/libupnp/upnp/src/genlib/net/http/httpreadwrite.c new file mode 100644 index 0000000..74bb476 --- /dev/null +++ b/libupnp/upnp/src/genlib/net/http/httpreadwrite.c @@ -0,0 +1,2394 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file defines the functionality making use of the http +* It defines functions to receive messages, process messages, send +* messages +************************************************************************/ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unixutil.h" +#include "upnp.h" +#include "upnpapi.h" +#include "membuffer.h" +#include "uri.h" +#include "statcodes.h" +#include "httpreadwrite.h" +#include "sock.h" +#include "webserver.h" + +#define DOSOCKET_READ 1 +#define DOSOCKET_WRITE 0 + +/************************************************************************ +* Function: http_FixUrl +* +* Parameters: +* IN uri_type* url ; URL to be validated and fixed +* OUT uri_type* fixed_url ; URL after being fixed. +* +* Description: Validates URL +* +* Returns: +* UPNP_E_INVALID_URL +* UPNP_E_SUCCESS +************************************************************************/ +int +http_FixUrl( IN uri_type * url, + OUT uri_type * fixed_url ) +{ + char *temp_path = "/"; + + *fixed_url = *url; + + if( token_string_casecmp( &fixed_url->scheme, "http" ) != 0 ) { + return UPNP_E_INVALID_URL; + } + + if( fixed_url->hostport.text.size == 0 ) { + return UPNP_E_INVALID_URL; + } + // set pathquery to "/" if it is empty + if( fixed_url->pathquery.size == 0 ) { + fixed_url->pathquery.buff = temp_path; + fixed_url->pathquery.size = 1; + } + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function: http_FixStrUrl +* +* Parameters: +* IN char* urlstr ; Character string as a URL +* IN int urlstrlen ; Length of the character string +* OUT uri_type* fixed_url ; Fixed and corrected URL +* +* Description: Parses URL and then validates URL +* +* Returns: +* UPNP_E_INVALID_URL +* UPNP_E_SUCCESS +************************************************************************/ +int +http_FixStrUrl( IN char *urlstr, + IN int urlstrlen, + OUT uri_type * fixed_url ) +{ + uri_type url; + + if( parse_uri( urlstr, urlstrlen, &url ) != HTTP_SUCCESS ) { + return UPNP_E_INVALID_URL; + } + + return http_FixUrl( &url, fixed_url ); +} + +/************************************************************************ +* Function: http_Connect +* +* Parameters: +* IN uri_type* destination_url ; URL containing destination information +* OUT uri_type *url ; Fixed and corrected URL +* +* Description: Gets destination address from URL and then connects to the +* remote end +* +* Returns: +* socket descriptor on sucess +* UPNP_E_OUTOF_SOCKET +* UPNP_E_SOCKET_CONNECT on error +************************************************************************/ +int +http_Connect( IN uri_type * destination_url, + OUT uri_type * url ) +{ + int connfd; + + http_FixUrl( destination_url, url ); + + connfd = socket( AF_INET, SOCK_STREAM, 0 ); + if( connfd == -1 ) { + return UPNP_E_OUTOF_SOCKET; + } + + if( connect( connfd, ( struct sockaddr * )&url->hostport.IPv4address, + sizeof( struct sockaddr_in ) ) == -1 ) { + shutdown( connfd, SD_BOTH ); + UpnpCloseSocket( connfd ); + return UPNP_E_SOCKET_CONNECT; + } + + return connfd; +} + +/************************************************************************ +* Function: http_RecvMessage +* +* Parameters: +* IN SOCKINFO *info ; Socket information object +* OUT http_parser_t* parser, HTTP parser object +* IN http_method_t request_method ; HTTP request method +* IN OUT int* timeout_secs ; time out +* OUT int* http_error_code ; HTTP error code returned +* +* Description: Get the data on the socket and take actions based on the +* read data to modify the parser objects buffer. If an error is reported +* while parsing the data, the error code is passed in the http_errr_code +* parameter +* +* Returns: +* UPNP_E_BAD_HTTPMSG +* UPNP_E_SUCCESS +************************************************************************/ +int +http_RecvMessage( IN SOCKINFO * info, + OUT http_parser_t * parser, + IN http_method_t request_method, + IN OUT int *timeout_secs, + OUT int *http_error_code ) +{ + parse_status_t status; + int num_read; + xboolean ok_on_close = FALSE; + char buf[2 * 1024]; + + if( request_method == HTTPMETHOD_UNKNOWN ) { + parser_request_init( parser ); + } else { + parser_response_init( parser, request_method ); + } + + while( TRUE ) { + num_read = sock_read( info, buf, sizeof( buf ), timeout_secs ); + if( num_read > 0 ) { + // got data + status = parser_append( parser, buf, num_read ); + + if( status == PARSE_SUCCESS ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "<<< (RECVD) <<<\n%s\n-----------------\n", + parser->msg.msg.buf ); + //print_http_headers( &parser->msg ); + ) + + if( parser->content_length > + ( unsigned int )g_maxContentLength ) { + *http_error_code = HTTP_REQ_ENTITY_TOO_LARGE; + return UPNP_E_OUTOF_BOUNDS; + } + + return 0; + } else if( status == PARSE_FAILURE ) { + *http_error_code = parser->http_error_code; + return UPNP_E_BAD_HTTPMSG; + } else if( status == PARSE_INCOMPLETE_ENTITY ) { + // read until close + ok_on_close = TRUE; + } else if( status == PARSE_CONTINUE_1 ) //Web post request. murari + { + return PARSE_SUCCESS; + } + } else if( num_read == 0 ) { + if( ok_on_close ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "<<< (RECVD) <<<\n%s\n-----------------\n", + parser->msg.msg.buf ); + //print_http_headers( &parser->msg ); + ) + + return 0; + } else { + // partial msg + *http_error_code = HTTP_BAD_REQUEST; // or response + return UPNP_E_BAD_HTTPMSG; + } + } else { + *http_error_code = parser->http_error_code; + return num_read; + } + } +} + +/************************************************************************ +* Function: http_SendMessage +* +* Parameters: +* IN SOCKINFO *info ; Socket information object +* IN OUT int * TimeOut ; time out value +* IN const char* fmt, ... Pattern format to take actions upon +* +* Description: Sends a message to the destination based on the +* IN const char* fmt parameter +* fmt types: +* 'f': arg = const char * file name +* 'm': arg1 = const char * mem_buffer; arg2= size_t buf_length +* E.g.: +* char *buf = "POST /xyz.cgi http/1.1\r\n\r\n"; +* char *filename = "foo.dat"; +* int status = http_SendMessage( tcpsock, "mf", +* buf, strlen(buf), // args for memory buffer +* filename ); // arg for file +* +* Returns: +* UPNP_E_OUTOF_MEMORY +* UPNP_E_FILE_READ_ERROR +* UPNP_E_SUCCESS +************************************************************************/ +int +http_SendMessage( IN SOCKINFO * info, + IN OUT int *TimeOut, + IN const char *fmt, + ... ) +{ +#define CHUNK_HEADER_SIZE 10 +#define CHUNK_TAIL_SIZE 10 + + char c; + char *buf = NULL; + size_t buf_length; + char *filename = NULL; + FILE *Fp; + int num_read, + num_written, + amount_to_be_read = 0; + va_list argp; + char *file_buf = NULL, + *ChunkBuf = NULL; + struct SendInstruction *Instr = NULL; + char Chunk_Header[10]; + int RetVal = 0; + + // 10 byte allocated for chunk header. + int Data_Buf_Size = WEB_SERVER_BUF_SIZE; + + va_start( argp, fmt ); + + while( ( c = *fmt++ ) != 0 ) { + if( c == 'I' ) { + Instr = ( struct SendInstruction * ) + va_arg( argp, struct SendInstruction * ); + + assert( Instr ); + + if( Instr->ReadSendSize >= 0 ) + amount_to_be_read = Instr->ReadSendSize; + else + amount_to_be_read = Data_Buf_Size; + + if( amount_to_be_read < WEB_SERVER_BUF_SIZE ) + Data_Buf_Size = amount_to_be_read; + + ChunkBuf = ( char * )malloc( Data_Buf_Size + + CHUNK_HEADER_SIZE + + CHUNK_TAIL_SIZE ); + if( !ChunkBuf ) + return UPNP_E_OUTOF_MEMORY; + + file_buf = ChunkBuf + 10; + } + + if( c == 'f' ) { // file name + + filename = ( char * )va_arg( argp, char * ); + + if( Instr && Instr->IsVirtualFile ) + Fp = virtualDirCallback.open( filename, UPNP_READ ); + else + Fp = fopen( filename, "rb" ); + + if( Fp == NULL ) { + free( ChunkBuf ); + return UPNP_E_FILE_READ_ERROR; + } + + assert( Fp ); + + if( Instr && Instr->IsRangeActive && Instr->IsVirtualFile ) { + if( virtualDirCallback.seek( Fp, Instr->RangeOffset, + SEEK_CUR ) != 0 ) { + free( ChunkBuf ); + return UPNP_E_FILE_READ_ERROR; + } + } else if( Instr && Instr->IsRangeActive ) { + if( fseek( Fp, Instr->RangeOffset, SEEK_CUR ) != 0 ) { + free( ChunkBuf ); + return UPNP_E_FILE_READ_ERROR; + } + } + + while( amount_to_be_read ) { + if( Instr ) { + if( amount_to_be_read >= Data_Buf_Size ) { + if( Instr->IsVirtualFile ) + num_read = virtualDirCallback.read( Fp, + file_buf, + Data_Buf_Size ); + else + num_read = fread( file_buf, 1, Data_Buf_Size, + Fp ); + } else { + if( Instr->IsVirtualFile ) + num_read = virtualDirCallback.read( Fp, + file_buf, + amount_to_be_read ); + else + num_read = fread( file_buf, 1, + amount_to_be_read, Fp ); + } + + amount_to_be_read = amount_to_be_read - num_read; + + if( Instr->ReadSendSize < 0 ) { + //read until close + amount_to_be_read = Data_Buf_Size; + } + } else { + num_read = fread( file_buf, 1, Data_Buf_Size, Fp ); + } + + if( num_read == 0 ) // EOF so no more to send. + { + if( Instr && Instr->IsChunkActive ) { + num_written = sock_write( info, "0\r\n\r\n", + strlen( "0\r\n\r\n" ), + TimeOut ); + } else { + RetVal = UPNP_E_FILE_READ_ERROR; + } + goto Cleanup_File; + } + // Create chunk for the current buffer. + if( Instr && Instr->IsChunkActive ) { + //Copy CRLF at the end of the chunk + memcpy( file_buf + num_read, "\r\n", 2 ); + + //Hex length for the chunk size. + sprintf( Chunk_Header, "%x", num_read ); + + //itoa(num_read,Chunk_Header,16); + strcat( Chunk_Header, "\r\n" ); + + //Copy the chunk size header + memcpy( file_buf - strlen( Chunk_Header ), + Chunk_Header, strlen( Chunk_Header ) ); + + // on the top of the buffer. + //file_buf[num_read+strlen(Chunk_Header)] = NULL; + //printf("Sending %s\n",file_buf-strlen(Chunk_Header)); + num_written = sock_write( info, + file_buf - + strlen( Chunk_Header ), + num_read + + strlen( Chunk_Header ) + 2, + TimeOut ); + + if( num_written != + num_read + ( int )strlen( Chunk_Header ) + + 2 ) { + goto Cleanup_File; //Send error nothing we can do. + } + } else { + // write data + num_written = sock_write( info, file_buf, num_read, + TimeOut ); + + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + ">>> (SENT) >>>\n%.*s\n------------\n", + ( int )num_written, file_buf ); + ) + + //Send error nothing we can do + if( num_written != num_read ) { + goto Cleanup_File; + } + } + } //While + Cleanup_File: + va_end( argp ); + if( Instr && Instr->IsVirtualFile ) + virtualDirCallback.close( Fp ); + else + fclose( Fp ); + free( ChunkBuf ); + return RetVal; + + } else if( c == 'b' ) { // memory buffer + + buf = ( char * )va_arg( argp, char * ); + + buf_length = ( size_t ) va_arg( argp, size_t ); + if( buf_length > 0 ) { + num_written = sock_write( info, buf, buf_length, TimeOut ); + if( ( size_t ) num_written != buf_length ) + goto end; + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + ">>> (SENT) >>>\n%.*s\n------------\n", + ( int )buf_length, buf ); + ) + } + } + } + + end: + va_end( argp ); + free( ChunkBuf ); + return 0; +} + +/************************************************************************ +* Function: http_RequestAndResponse +* +* Parameters: +* IN uri_type* destination ; Destination URI object which contains +* remote IP address among other elements +* IN const char* request ; Request to be sent +* IN size_t request_length ; Length of the request +* IN http_method_t req_method ; HTTP Request method +* IN int timeout_secs ; time out value +* OUT http_parser_t* response ; Parser object to receive the repsonse +* +* Description: Initiates socket, connects to the destination, sends a +* request and waits for the response from the remote end +* +* Returns: +* UPNP_E_SOCKET_ERROR +* UPNP_E_SOCKET_CONNECT +* Error Codes returned by http_SendMessage +* Error Codes returned by http_RecvMessage +************************************************************************/ +int +http_RequestAndResponse( IN uri_type * destination, + IN const char *request, + IN size_t request_length, + IN http_method_t req_method, + IN int timeout_secs, + OUT http_parser_t * response ) +{ + int tcp_connection; + int ret_code; + int http_error_code; + SOCKINFO info; + + tcp_connection = socket( AF_INET, SOCK_STREAM, 0 ); + if( tcp_connection == -1 ) { + parser_response_init( response, req_method ); + return UPNP_E_SOCKET_ERROR; + } + if( sock_init( &info, tcp_connection ) != UPNP_E_SUCCESS ) + { + sock_destroy( &info, SD_BOTH ); + parser_response_init( response, req_method ); + return UPNP_E_SOCKET_ERROR; + } + // connect + ret_code = connect( info.socket, + ( struct sockaddr * )&destination->hostport. + IPv4address, sizeof( struct sockaddr_in ) ); + + if( ret_code == -1 ) { + sock_destroy( &info, SD_BOTH ); + parser_response_init( response, req_method ); + return UPNP_E_SOCKET_CONNECT; + } + // send request + ret_code = http_SendMessage( &info, &timeout_secs, "b", + request, request_length ); + if( ret_code != 0 ) { + sock_destroy( &info, SD_BOTH ); + parser_response_init( response, req_method ); + return ret_code; + } + // recv response + ret_code = http_RecvMessage( &info, response, req_method, + &timeout_secs, &http_error_code ); + + sock_destroy( &info, SD_BOTH ); //should shutdown completely + + return ret_code; +} + +/************************************************************************ +* Function : http_Download +* +* Parameters : +* IN const char* url_str : String as a URL +* IN int timeout_secs : time out value +* OUT char** document : buffer to store the document extracted +* from the donloaded message. +* OUT int* doc_length : length of the extracted document +* OUT char* content_type : Type of content +* +* Description : Download the document message and extract the document +* from the message. +* +* Return : int; +* UPNP_E_SUCCESS; +* UPNP_E_INVALID_URL; +* +* +* Note : +************************************************************************/ +int +http_Download( IN const char *url_str, + IN int timeout_secs, + OUT char **document, + OUT int *doc_length, + OUT char *content_type ) +{ + int ret_code; + uri_type url; + char *msg_start, + *entity_start, + *hoststr, + *temp; + http_parser_t response; + size_t msg_length, + hostlen; + memptr ctype; + size_t copy_len; + membuffer request; + char *urlPath = alloca( strlen( url_str ) + 1 ); + + //ret_code = parse_uri( (char*)url_str, strlen(url_str), &url ); + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, "DOWNLOAD URL : %s\n", + url_str ); + ) + ret_code = + http_FixStrUrl( ( char * )url_str, strlen( url_str ), &url ); + if( ret_code != UPNP_E_SUCCESS ) { + return ret_code; + } + // make msg + membuffer_init( &request ); + + strcpy( urlPath, url_str ); + hoststr = strstr( urlPath, "//" ); + if( hoststr == NULL ) { + return UPNP_E_INVALID_URL; + } + + hoststr += 2; + temp = strchr( hoststr, '/' ); + if( temp == NULL ) { + return UPNP_E_INVALID_URL; + } + + *temp = '\0'; + hostlen = strlen( hoststr ); + *temp = '/'; + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HOSTNAME : %s Length : %d\n", hoststr, hostlen ); + ) + + ret_code = http_MakeMessage( &request, 1, 1, "QsbcDCUc", + HTTPMETHOD_GET, url.pathquery.buff, + url.pathquery.size, "HOST: ", hoststr, + hostlen ); + if( ret_code != 0 ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Makemessage failed\n" ); + ) + membuffer_destroy( &request ); + return ret_code; + } + + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Buffer:\n %s\n----------END--------\n", request.buf ); + ) + // get doc msg + ret_code = + http_RequestAndResponse( &url, request.buf, request.length, + HTTPMETHOD_GET, timeout_secs, &response ); + + if( ret_code != 0 ) { + httpmsg_destroy( &response.msg ); + membuffer_destroy( &request ); + return ret_code; + } + + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n" ); + ) + DBGONLY( print_http_headers( &response.msg ); + ) + + // optional content-type + if( content_type ) { + if( httpmsg_find_hdr( &response.msg, HDR_CONTENT_TYPE, &ctype ) == + NULL ) { + *content_type = '\0'; // no content-type + } else { + // safety + copy_len = ctype.length < LINE_SIZE - 1 ? + ctype.length : LINE_SIZE - 1; + + memcpy( content_type, ctype.buf, copy_len ); + content_type[copy_len] = '\0'; + } + } + // + // extract doc from msg + // + + if( ( *doc_length = ( int )response.msg.entity.length ) == 0 ) { + // 0-length msg + *document = NULL; + } else if( response.msg.status_code == HTTP_OK ) //LEAK_FIX_MK + { + // copy entity + entity_start = response.msg.entity.buf; // what we want + msg_length = response.msg.msg.length; // save for posterity + msg_start = membuffer_detach( &response.msg.msg ); // whole msg + + // move entity to the start; copy null-terminator too + memmove( msg_start, entity_start, *doc_length + 1 ); + + // save mem for body only + *document = realloc( msg_start, *doc_length + 1 ); //LEAK_FIX_MK + //*document = Realloc( msg_start,msg_length, *doc_length + 1 );//LEAK_FIX_MK + + // shrink can't fail + assert( ( int )msg_length > *doc_length ); + assert( *document != NULL ); + } + + if( response.msg.status_code == HTTP_OK ) { + ret_code = 0; // success + } else { + // server sent error msg (not requested doc) + ret_code = response.msg.status_code; + } + + httpmsg_destroy( &response.msg ); + membuffer_destroy( &request ); + + return ret_code; +} + +typedef struct HTTPPOSTHANDLE { + SOCKINFO sock_info; + int contentLength; +} http_post_handle_t; + +/************************************************************************ +* Function: MakePostMessage +* +* Parameters: +* const char *url_str ; String as a URL +* membuffer *request ; Buffer containing the request +* uri_type *url ; URI object containing the scheme, path +* query token, etc. +* int contentLength ; length of content +* const char *contentType ; Type of content +* +* Description: Makes the message for the HTTP POST message +* +* Returns: +* UPNP_E_INVALID_URL +* UPNP_E_INVALID_PARAM +* UPNP_E_SUCCESS +************************************************************************/ +int +MakePostMessage( const char *url_str, + membuffer * request, + uri_type * url, + int contentLength, + const char *contentType ) +{ + int ret_code = 0; + char *urlPath = alloca( strlen( url_str ) + 1 ); + int hostlen = 0; + char *hoststr, + *temp; + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "DOWNLOAD URL : %s\n", url_str ); + ) + + ret_code = + http_FixStrUrl( ( char * )url_str, strlen( url_str ), url ); + + if( ret_code != UPNP_E_SUCCESS ) { + return ret_code; + } + // make msg + membuffer_init( request ); + + strcpy( urlPath, url_str ); + hoststr = strstr( urlPath, "//" ); + if( hoststr == NULL ) { + return UPNP_E_INVALID_URL; + } + + hoststr += 2; + temp = strchr( hoststr, '/' ); + if( temp == NULL ) { + return UPNP_E_INVALID_URL; + } + + *temp = '\0'; + hostlen = strlen( hoststr ); + *temp = '/'; + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HOSTNAME : %s Length : %d\n", hoststr, hostlen ); + ) + + if( contentLength >= 0 ) { + ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUTNc", + HTTPMETHOD_POST, url->pathquery.buff, + url->pathquery.size, "HOST: ", + hoststr, hostlen, contentType, + contentLength ); + } else if( contentLength == UPNP_USING_CHUNKED ) { + ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUTKc", + HTTPMETHOD_POST, url->pathquery.buff, + url->pathquery.size, "HOST: ", + hoststr, hostlen, contentType ); + } else if( contentLength == UPNP_UNTIL_CLOSE ) { + ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUTc", + HTTPMETHOD_POST, url->pathquery.buff, + url->pathquery.size, "HOST: ", + hoststr, hostlen, contentType ); + } else { + ret_code = UPNP_E_INVALID_PARAM; + } + + if( ret_code != 0 ) { + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Makemessage failed\n" ); + ) + membuffer_destroy( request ); + return ret_code; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Buffer:\n %s\n" "----------END--------\n", + request->buf ); + ) + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : http_WriteHttpPost +* +* Parameters : +* IN void *Handle : Handle to the http post object +* IN char *buf : Buffer to send to peer, if format used +* is not UPNP_USING_CHUNKED, +* IN unsigned int *size : Size of the data to be sent. +* IN int timeout : time out value +* +* Description : Formats data if format used is UPNP_USING_CHUNKED. +* Writes data on the socket connected to the peer. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_INVALID_PARAM - Invalid Parameter +* -1 - On Socket Error. +* +* Note : +************************************************************************/ +int +http_WriteHttpPost( IN void *Handle, + IN char *buf, + IN unsigned int *size, + IN int timeout ) +{ + http_post_handle_t *handle = ( http_post_handle_t * ) Handle; + char *tempbuf = NULL; + int tempbufSize = 0; + int freeTempbuf = 0; + int numWritten = 0; + + if( ( !handle ) || ( !size ) || ( ( ( *size ) > 0 ) && !buf ) + || ( ( *size ) < 0 ) ) { + ( *size ) = 0; + return UPNP_E_INVALID_PARAM; + } + if( handle->contentLength == UPNP_USING_CHUNKED ) { + if( ( *size ) ) { + int tempSize = 0; + + tempbuf = + ( char * )malloc( ( *size ) + CHUNK_HEADER_SIZE + + CHUNK_TAIL_SIZE ); + sprintf( tempbuf, "%x\r\n", ( *size ) ); //begin chunk + tempSize = strlen( tempbuf ); + memcpy( tempbuf + tempSize, buf, ( *size ) ); + memcpy( tempbuf + tempSize + ( *size ), "\r\n", 2 ); //end of chunk + tempbufSize = tempSize + ( *size ) + 2; + freeTempbuf = 1; + } + } else { + tempbuf = buf; + tempbufSize = ( *size ); + } + + numWritten = + sock_write( &handle->sock_info, tempbuf, tempbufSize, &timeout ); + //(*size) = sock_write(&handle->sock_info,tempbuf,tempbufSize,&timeout); + + if( freeTempbuf ) { + free( tempbuf ); + } + if( numWritten < 0 ) { + ( *size ) = 0; + return numWritten; + } else { + ( *size ) = numWritten; + return UPNP_E_SUCCESS; + } +} + +/************************************************************************ +* Function : http_CloseHttpPost +* +* Parameters : +* IN void *Handle : Handle to the http post object +* IN OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Sends remaining data if using UPNP_USING_CHUNKED +* format. Receives any more messages. Destroys socket and any socket +* associated memory. Frees handle associated with the HTTP POST msg. +* +* Return : int ; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Parameter; +* +* Note : +************************************************************************/ +int +http_CloseHttpPost( IN void *Handle, + IN OUT int *httpStatus, + IN int timeout ) +{ + int retc = 0; + http_parser_t response; + int http_error_code; + + http_post_handle_t *handle = Handle; + + if( ( !handle ) || ( !httpStatus ) ) { + return UPNP_E_INVALID_PARAM; + } + + if( handle->contentLength == UPNP_USING_CHUNKED ) { + retc = sock_write( &handle->sock_info, "0\r\n\r\n", strlen( "0\r\n\r\n" ), &timeout ); //send last chunk + } + //read response + parser_response_init( &response, HTTPMETHOD_POST ); + + retc = + http_RecvMessage( &handle->sock_info, &response, HTTPMETHOD_POST, + &timeout, &http_error_code ); + + ( *httpStatus ) = http_error_code; + + sock_destroy( &handle->sock_info, SD_BOTH ); //should shutdown completely + + httpmsg_destroy( &response.msg ); + free( handle ); + + return retc; +} + +/************************************************************************ +* Function : http_OpenHttpPost +* +* Parameters : +* IN const char *url_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN const char *contentType : Type of content +* IN int contentLength : length of content +* IN int timeout : time out value +* +* Description : Makes the HTTP POST message, connects to the peer, +* sends the HTTP POST request. Adds the post handle to buffer of +* such handles +* +* Return : int; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Paramter ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_SOCKET_CONNECT ; +* +* Note : +************************************************************************/ +int +http_OpenHttpPost( IN const char *url_str, + IN OUT void **Handle, + IN const char *contentType, + IN int contentLength, + IN int timeout ) +{ + int ret_code; + int tcp_connection; + membuffer request; + http_post_handle_t *handle = NULL; + uri_type url; + + if( ( !url_str ) || ( !Handle ) || ( !contentType ) ) { + return UPNP_E_INVALID_PARAM; + } + + ( *Handle ) = handle; + + if( ( ret_code = + MakePostMessage( url_str, &request, &url, contentLength, + contentType ) ) != UPNP_E_SUCCESS ) { + return ret_code; + } + + handle = + ( http_post_handle_t * ) malloc( sizeof( http_post_handle_t ) ); + + if( handle == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + handle->contentLength = contentLength; + + tcp_connection = socket( AF_INET, SOCK_STREAM, 0 ); + if( tcp_connection == -1 ) { + ret_code = UPNP_E_SOCKET_ERROR; + goto errorHandler; + } + + if( sock_init( &handle->sock_info, tcp_connection ) != UPNP_E_SUCCESS ) + { + sock_destroy( &handle->sock_info, SD_BOTH ); + ret_code = UPNP_E_SOCKET_ERROR; + goto errorHandler; + } + + ret_code = connect( handle->sock_info.socket, + ( struct sockaddr * )&url.hostport.IPv4address, + sizeof( struct sockaddr_in ) ); + + if( ret_code == -1 ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + ret_code = UPNP_E_SOCKET_CONNECT; + goto errorHandler; + } + // send request + ret_code = http_SendMessage( &handle->sock_info, &timeout, "b", + request.buf, request.length ); + if( ret_code != 0 ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + } + + errorHandler: + membuffer_destroy( &request ); + ( *Handle ) = handle; + return ret_code; +} + +typedef struct HTTPGETHANDLE { + http_parser_t response; + SOCKINFO sock_info; + int entity_offset; + int cancel; +} http_get_handle_t; + +/************************************************************************ +* Function: MakeGetMessage +* +* Parameters: +* const char *url_str ; String as a URL +* const char *proxy_str ; String as a URL of proxy to use +* membuffer *request ; Buffer containing the request +* uri_type *url ; URI object containing the scheme, path +* query token, etc. +* +* Description: Makes the message for the HTTP GET method +* +* Returns: +* UPNP_E_INVALID_URL +* Error Codes returned by http_MakeMessage +* UPNP_E_SUCCESS +************************************************************************/ +int +MakeGetMessage( const char *url_str, + const char *proxy_str, + membuffer * request, + uri_type * url ) +{ + int ret_code; + char *urlPath = alloca( strlen( url_str ) + 1 ); + int querylen = 0; + const char *querystr; + int hostlen = 0; + char *hoststr, + *temp; + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "DOWNLOAD URL : %s\n", url_str ); + ) + + ret_code = + http_FixStrUrl( ( char * )url_str, strlen( url_str ), url ); + + if( ret_code != UPNP_E_SUCCESS ) { + return ret_code; + } + // make msg + membuffer_init( request ); + + strcpy( urlPath, url_str ); + hoststr = strstr( urlPath, "//" ); + if( hoststr == NULL ) { + return UPNP_E_INVALID_URL; + } + + hoststr += 2; + temp = strchr( hoststr, '/' ); + if( temp == NULL ) { + return UPNP_E_INVALID_URL; + } + + *temp = '\0'; + hostlen = strlen( hoststr ); + *temp = '/'; + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HOSTNAME : %s Length : %d\n", hoststr, hostlen ); + ) + + if( proxy_str ) { + querystr = url_str; + querylen = strlen( querystr ); + } else { + querystr = url->pathquery.buff; + querylen = url->pathquery.size; + } + + ret_code = http_MakeMessage( request, 1, 1, "QsbcDCUc", + HTTPMETHOD_GET, querystr, querylen, + "HOST: ", hoststr, hostlen ); + + if( ret_code != 0 ) { + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Makemessage failed\n" ); + ) + membuffer_destroy( request ); + return ret_code; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Buffer:\n %s\n" "----------END--------\n", + request->buf ); + ) + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : ReadResponseLineAndHeaders +* +* Parameters : +* IN SOCKINFO *info ; Socket information object +* IN OUT http_parser_t *parser ; HTTP Parser object +* IN OUT int *timeout_secs ; time out value +* IN OUT int *http_error_code ; HTTP errror code returned +* +* Description : Parses already exiting data. If not complete reads more +* data on the connected socket. The read data is then parsed. The +* same methid is carried out for headers. +* +* Return : int ; +* PARSE_OK - On Success +* PARSE_FAILURE - Failure to parse data correctly +* UPNP_E_BAD_HTTPMSG - Socker read() returns an error +* +* Note : +************************************************************************/ +int +ReadResponseLineAndHeaders( IN SOCKINFO * info, + IN OUT http_parser_t * parser, + IN OUT int *timeout_secs, + IN OUT int *http_error_code ) +{ + parse_status_t status; + int num_read; + char buf[2 * 1024]; + int done = 0; + int ret_code = 0; + + //read response line + + status = parser_parse_responseline( parser ); + if( status == PARSE_OK ) { + done = 1; + } else if( status == PARSE_INCOMPLETE ) { + done = 0; + } else { + //error + return status; + } + + while( !done ) { + num_read = sock_read( info, buf, sizeof( buf ), timeout_secs ); + if( num_read > 0 ) { + // append data to buffer + ret_code = membuffer_append( &parser->msg.msg, buf, num_read ); + if( ret_code != 0 ) { + // set failure status + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + status = parser_parse_responseline( parser ); + if( status == PARSE_OK ) { + done = 1; + } else if( status == PARSE_INCOMPLETE ) { + done = 0; + } else { + //error + return status; + } + } else if( num_read == 0 ) { + + // partial msg + *http_error_code = HTTP_BAD_REQUEST; // or response + return UPNP_E_BAD_HTTPMSG; + + } else { + *http_error_code = parser->http_error_code; + return num_read; + } + } + + done = 0; + + status = parser_parse_headers( parser ); + if( ( status == PARSE_OK ) && ( parser->position == POS_ENTITY ) ) { + + done = 1; + } else if( status == PARSE_INCOMPLETE ) { + done = 0; + } else { + //error + return status; + } + + //read headers + while( !done ) { + num_read = sock_read( info, buf, sizeof( buf ), timeout_secs ); + if( num_read > 0 ) { + // append data to buffer + ret_code = membuffer_append( &parser->msg.msg, buf, num_read ); + if( ret_code != 0 ) { + // set failure status + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return PARSE_FAILURE; + } + status = parser_parse_headers( parser ); + if( ( status == PARSE_OK ) + && ( parser->position == POS_ENTITY ) ) { + + done = 1; + } else if( status == PARSE_INCOMPLETE ) { + done = 0; + } else { + //error + return status; + } + } else if( num_read == 0 ) { + + // partial msg + *http_error_code = HTTP_BAD_REQUEST; // or response + return UPNP_E_BAD_HTTPMSG; + + } else { + *http_error_code = parser->http_error_code; + return num_read; + } + } + + return PARSE_OK; +} + +/************************************************************************ +* Function : http_ReadHttpGet +* +* Parameters : +* IN void *Handle : Handle to the HTTP get object +* IN OUT char *buf : Buffer to get the read and parsed data +* IN OUT unsigned int *size : Size of the buffer passed +* IN int timeout : time out value +* +* Description : Parses already existing data, then gets new data. +* Parses and extracts information from the new data. +* +* Return : int ; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Parameter; +* UPNP_E_BAD_RESPONSE ; +* UPNP_E_BAD_HTTPMSG ; +* UPNP_E_CANCELED ; +* +* Note : +************************************************************************/ +int +http_ReadHttpGet( IN void *Handle, + IN OUT char *buf, + IN OUT unsigned int *size, + IN int timeout ) +{ + http_get_handle_t *handle = Handle; + + parse_status_t status; + int num_read; + xboolean ok_on_close = FALSE; + char tempbuf[2 * 1024]; + + int ret_code = 0; + + if( ( !handle ) || ( !size ) || ( ( ( *size ) > 0 ) && !buf ) + || ( ( *size ) < 0 ) ) { + ( *size ) = 0; + return UPNP_E_INVALID_PARAM; + } + //first parse what has already been gotten + if( handle->response.position != POS_COMPLETE ) { + status = parser_parse_entity( &handle->response ); + } else { + status = PARSE_SUCCESS; + } + + if( status == PARSE_INCOMPLETE_ENTITY ) { + // read until close + ok_on_close = TRUE; + } else if( ( status != PARSE_SUCCESS ) + && ( status != PARSE_CONTINUE_1 ) + && ( status != PARSE_INCOMPLETE ) ) { + //error + ( *size ) = 0; + return UPNP_E_BAD_RESPONSE; + } + //read more if necessary entity + while( ( ( handle->entity_offset + ( *size ) ) > + handle->response.msg.entity.length ) + && ( ! handle->cancel ) + && ( handle->response.position != POS_COMPLETE ) ) { + num_read = + sock_read( &handle->sock_info, tempbuf, sizeof( tempbuf ), + &timeout ); + if( num_read > 0 ) { + // append data to buffer + ret_code = membuffer_append( &handle->response.msg.msg, + tempbuf, num_read ); + if( ret_code != 0 ) { + // set failure status + handle->response.http_error_code = + HTTP_INTERNAL_SERVER_ERROR; + ( *size ) = 0; + return PARSE_FAILURE; + } + status = parser_parse_entity( &handle->response ); + if( status == PARSE_INCOMPLETE_ENTITY ) { + // read until close + ok_on_close = TRUE; + } else if( ( status != PARSE_SUCCESS ) + && ( status != PARSE_CONTINUE_1 ) + && ( status != PARSE_INCOMPLETE ) ) { + //error + ( *size ) = 0; + return UPNP_E_BAD_RESPONSE; + } + } else if( num_read == 0 ) { + if( ok_on_close ) { + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "<<< (RECVD) <<<\n%s\n-----------------\n", + handle->response.msg.msg.buf ); + //print_http_headers( &parser->msg ); + ) + handle->response.position = POS_COMPLETE; + } else { + // partial msg + ( *size ) = 0; + handle->response.http_error_code = HTTP_BAD_REQUEST; // or response + return UPNP_E_BAD_HTTPMSG; + } + } else { + ( *size ) = 0; + return num_read; + } + } + + if( ( handle->entity_offset + ( *size ) ) > + handle->response.msg.entity.length ) { + ( *size ) = + handle->response.msg.entity.length - handle->entity_offset; + } + + memcpy( buf, + &handle->response.msg.msg.buf[handle-> + response.entity_start_position + + handle->entity_offset], + ( *size ) ); + handle->entity_offset += ( *size ); + + if ( handle->cancel ) + return UPNP_E_CANCELED; + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : http_HttpGetProgress +* +* Parameters : +* IN void *Handle : Handle to the HTTP get object +* OUT unsigned int *length : Buffer to get the read and parsed data +* OUT unsigned int *total : Size of tge buffer passed +* +* Description : Extracts information from the Handle to the HTTP get +* object. +* +* Return : int ; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Parameter; +* +* Note : +************************************************************************/ +int http_HttpGetProgress( IN void *Handle, + OUT unsigned int *length, + OUT unsigned int *total ) +{ + http_get_handle_t *handle = Handle; + + if( ( !handle ) || ( !length ) || ( !total ) ) { + return UPNP_E_INVALID_PARAM; + } + *length = handle->response.msg.entity.length; + *total = handle->response.content_length; + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : http_CancelHttpGet +* +* Parameters : +* IN void *Handle ; Handle to HTTP get object +* +* Description : Set the cancel flag of the HttpGet handle +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_INVALID_PARAM - Invalid Parameter +* +* Note : +************************************************************************/ +int +http_CancelHttpGet( IN void *Handle ) +{ + http_get_handle_t *handle = Handle; + + if( !handle ) { + return UPNP_E_INVALID_PARAM; + } + + handle->cancel = 1; + + return UPNP_E_SUCCESS; +} + + +/************************************************************************ +* Function : http_CloseHttpGet +* +* Parameters : +* IN void *Handle ; Handle to HTTP get object +* +* Description : Clears the handle allocated for the HTTP GET operation +* Clears socket states and memory allocated for socket operations. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_INVALID_PARAM - Invalid Parameter +* +* Note : +************************************************************************/ +int +http_CloseHttpGet( IN void *Handle ) +{ + http_get_handle_t *handle = Handle; + + if( !handle ) { + return UPNP_E_INVALID_PARAM; + } + + sock_destroy( &handle->sock_info, SD_BOTH ); //should shutdown completely + httpmsg_destroy( &handle->response.msg ); + handle->entity_offset = 0; + free( handle ); + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : http_OpenHttpGet +* +* Parameters : +* IN const char *url_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN OUT char **contentType : Type of content +* OUT int *contentLength : length of content +* OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Makes the HTTP GET message, connects to the peer, +* sends the HTTP GET request, gets the response and parses the +* response. +* +* Return : int; +* UPNP_E_SUCCESS - On Success ; +* UPNP_E_INVALID_PARAM - Invalid Paramters ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_BAD_RESPONSE ; +* +* Note : +* +************************************************************************/ +int +http_OpenHttpGet( IN const char *url_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int timeout ) +{ + return http_OpenHttpGetProxy(url_str, NULL, Handle, contentType, contentLength, httpStatus, timeout); +} + +/************************************************************************ +* Function : http_OpenHttpGetProxy +* +* Parameters : +* IN const char *url_str : String as a URL +* IN const char *proxy_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN OUT char **contentType : Type of content +* OUT int *contentLength : length of content +* OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Makes the HTTP GET message, connects to the peer, +* sends the HTTP GET request, gets the response and parses the +* response. +* If a proxy URL is defined then the connection is made there. +* +* Return : int; +* UPNP_E_SUCCESS - On Success ; +* UPNP_E_INVALID_PARAM - Invalid Paramters ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_BAD_RESPONSE ; +* +* Note : +* +************************************************************************/ +int +http_OpenHttpGetProxy( IN const char *url_str, + IN const char *proxy_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int timeout ) +{ + int ret_code; + int http_error_code; + memptr ctype; + int tcp_connection; + membuffer request; + http_get_handle_t *handle = NULL; + uri_type url; + uri_type proxy; + uri_type *peer; + parse_status_t status; + + if( ( !url_str ) || ( !Handle ) || ( !contentType ) + || ( !httpStatus ) ) { + return UPNP_E_INVALID_PARAM; + } + + ( *httpStatus ) = 0; + ( *Handle ) = handle; + ( *contentType ) = NULL; + ( *contentLength ) = 0; + + if( ( ret_code = + MakeGetMessage( url_str, proxy_str, &request, &url ) ) != UPNP_E_SUCCESS ) { + return ret_code; + } + if( proxy_str ) { + ret_code = http_FixStrUrl( ( char * )proxy_str, strlen( proxy_str ), &proxy ); + peer = &proxy; + } else { + peer = &url; + } + + handle = ( http_get_handle_t * ) malloc( sizeof( http_get_handle_t ) ); + + if( handle == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + handle->entity_offset = 0; + handle->cancel = 0; + parser_response_init( &handle->response, HTTPMETHOD_GET ); + + tcp_connection = socket( AF_INET, SOCK_STREAM, 0 ); + if( tcp_connection == -1 ) { + ret_code = UPNP_E_SOCKET_ERROR; + goto errorHandler; + } + + if( sock_init( &handle->sock_info, tcp_connection ) != UPNP_E_SUCCESS ) + { + sock_destroy( &handle->sock_info, SD_BOTH ); + ret_code = UPNP_E_SOCKET_ERROR; + goto errorHandler; + } + + ret_code = connect( handle->sock_info.socket, + ( struct sockaddr * )&peer->hostport.IPv4address, + sizeof( struct sockaddr_in ) ); + + if( ret_code == -1 ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + ret_code = UPNP_E_SOCKET_CONNECT; + goto errorHandler; + } + // send request + ret_code = http_SendMessage( &handle->sock_info, &timeout, "b", + request.buf, request.length ); + if( ret_code != 0 ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + goto errorHandler; + } + + status = + ReadResponseLineAndHeaders( &handle->sock_info, &handle->response, + &timeout, &http_error_code ); + + if( status != PARSE_OK ) { + ret_code = UPNP_E_BAD_RESPONSE; + goto errorHandler; + } + + status = parser_get_entity_read_method( &handle->response ); + + if( ( status != PARSE_CONTINUE_1 ) && ( status != PARSE_SUCCESS ) ) { + ret_code = UPNP_E_BAD_RESPONSE; + goto errorHandler; + } + + ( *httpStatus ) = handle->response.msg.status_code; + ret_code = UPNP_E_SUCCESS; + + if( httpmsg_find_hdr( &handle->response.msg, HDR_CONTENT_TYPE, &ctype ) + == NULL ) { + *contentType = NULL; // no content-type + } else { + *contentType = ctype.buf; + } + + if( handle->response.position == POS_COMPLETE ) { + ( *contentLength ) = 0; + } else if( handle->response.ent_position == ENTREAD_USING_CHUNKED ) { + ( *contentLength ) = UPNP_USING_CHUNKED; + } else if( handle->response.ent_position == ENTREAD_USING_CLEN ) { + ( *contentLength ) = handle->response.content_length; + } else if( handle->response.ent_position == ENTREAD_UNTIL_CLOSE ) { + ( *contentLength ) = UPNP_UNTIL_CLOSE; + } + + errorHandler: + + ( *Handle ) = handle; + + membuffer_destroy( &request ); + + if( ret_code != UPNP_E_SUCCESS ) { + httpmsg_destroy( &handle->response.msg ); + } + return ret_code; +} + +/************************************************************************ +* Function : http_SendStatusResponse +* +* Parameters : +* IN SOCKINFO *info : Socket information object +* IN int http_status_code : error code returned while making +* or sending the response message +* IN int request_major_version : request major version +* IN int request_minor_version : request minor version +* +* Description : Generate a response message for the status query and +* send the status response. +* +* Return : int; +* 0 -- success +* UPNP_E_OUTOF_MEMORY +* UPNP_E_SOCKET_WRITE +* UPNP_E_TIMEDOUT +* +* Note : +************************************************************************/ +int +http_SendStatusResponse( IN SOCKINFO * info, + IN int http_status_code, + IN int request_major_version, + IN int request_minor_version ) +{ + int response_major, + response_minor; + membuffer membuf; + int ret; + int timeout; + + http_CalcResponseVersion( request_major_version, request_minor_version, + &response_major, &response_minor ); + + membuffer_init( &membuf ); + membuf.size_inc = 70; + + ret = http_MakeMessage( &membuf, response_major, response_minor, "RSCB", http_status_code, // response start line + http_status_code ); // body + if( ret == 0 ) { + timeout = HTTP_DEFAULT_TIMEOUT; + ret = http_SendMessage( info, &timeout, "b", + membuf.buf, membuf.length ); + } + + membuffer_destroy( &membuf ); + + return ret; +} + +/************************************************************************ +* Function : http_MakeMessage +* +* Parameters : +* INOUT membuffer* buf : buffer with the contents of the +* message +* IN int http_major_version : HTTP major version +* IN int http_minor_version : HTTP minor version +* IN const char* fmt : Pattern format +* ... : +* +* Description : Generate an HTTP message based on the format that is +* specified in the input parameters. +* +* fmt types: +* 's': arg = const char* C_string +* 'b': arg1 = const char* buf; arg2 = size_t buf_length +* memory ptr +* 'c': (no args) appends CRLF "\r\n" +* 'd': arg = int number // appends decimal number +* 't': arg = time_t * gmt_time // appends time in RFC 1123 fmt +* 'D': (no args) appends HTTP DATE: header +* 'S': (no args) appends HTTP SERVER: header +* 'U': (no args) appends HTTP USER-AGENT: header +* 'C': (no args) appends a HTTP CONNECTION: close header +* depending on major,minor version +* 'N': arg1 = int content_length // content-length header +* 'Q': arg1 = http_method_t; arg2 = char* url; +* arg3 = int url_length // start line of request +* 'R': arg = int status_code // adds a response start line +* 'B': arg = int status_code +* appends content-length, content-type and HTML body for given code +* 'T': arg = char * content_type; format e.g: "text/html"; +* content-type header +* --- PATCH START - Sergey 'Jin' Bostandzhyan +* 'X': arg = const char useragent; "redsonic" HTTP X-User-Agent: useragent +* --- PATCH END --- +* +* Return : int; +* 0 - On Success +* UPNP_E_OUTOF_MEMORY +* UPNP_E_INVALID_URL; +* +* Note : +************************************************************************/ +int +http_MakeMessage( INOUT membuffer * buf, + IN int http_major_version, + IN int http_minor_version, + IN const char *fmt, + ... ) +{ + char c; + char *s = NULL; + int num; + size_t length; + time_t *loc_time; + time_t curr_time; + struct tm *date; + char *start_str, + *end_str; + int status_code; + const char *status_msg; + http_method_t method; + const char *method_str; + const char *url_str; + const char *temp_str; + uri_type url; + uri_type *uri_ptr; + int error_code = UPNP_E_OUTOF_MEMORY; + + va_list argp; + char tempbuf[200]; + const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; + const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0" + "Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; + + va_start( argp, fmt ); + + while( ( c = *fmt++ ) != 0 ) { + + if( c == 's' ) // C string + { + s = ( char * )va_arg( argp, char * ); + + assert( s ); + + //DBGONLY(UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,"Adding a string : %s\n", s);) + if( membuffer_append( buf, s, strlen( s ) ) != 0 ) { + goto error_handler; + } + } else if( c == 'K' ) // Add Chunky header + { + if( membuffer_append + ( buf, "TRANSFER-ENCODING: chunked\r\n", + strlen( "Transfer-Encoding: chunked\r\n" ) ) != 0 ) { + goto error_handler; + } + } else if( c == 'G' ) // Add Range header + { + struct SendInstruction *RespInstr; + RespInstr = + ( struct SendInstruction * )va_arg( argp, + struct SendInstruction + * ); + assert( RespInstr ); + // connection header + if( membuffer_append + ( buf, RespInstr->RangeHeader, + strlen( RespInstr->RangeHeader ) ) != 0 ) { + goto error_handler; + } + + } else if( c == 'b' ) // mem buffer + { + s = ( char * )va_arg( argp, char * ); + + //DBGONLY(UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,"Adding a char Buffer starting with: %c\n", s[0]);) + assert( s ); + length = ( size_t ) va_arg( argp, size_t ); + if( membuffer_append( buf, s, length ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'c' ) // crlf + { + if( membuffer_append( buf, "\r\n", 2 ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'd' ) // integer + { + num = ( int )va_arg( argp, int ); + + sprintf( tempbuf, "%d", num ); + if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) { + goto error_handler; + } + } + + else if( c == 't' || c == 'D' ) // date + { + if( c == 'D' ) { + // header + start_str = "DATE: "; + end_str = "\r\n"; + curr_time = time( NULL ); + date = gmtime( &curr_time ); + } else { + // date value only + start_str = end_str = ""; + loc_time = ( time_t * ) va_arg( argp, time_t * ); + assert( loc_time ); + date = gmtime( loc_time ); + } + + sprintf( tempbuf, "%s%s, %02d %s %d %02d:%02d:%02d GMT%s", + start_str, + &weekday_str[date->tm_wday * 4], date->tm_mday, + &month_str[date->tm_mon * 4], date->tm_year + 1900, + date->tm_hour, date->tm_min, date->tm_sec, end_str ); + + if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'C' ) { + if( ( http_major_version > 1 ) || + ( http_major_version == 1 && http_minor_version == 1 ) + ) { + // connection header + if( membuffer_append_str( buf, "CONNECTION: close\r\n" ) != + 0 ) { + goto error_handler; + } + } + } + + else if( c == 'N' ) { + // content-length header + num = ( int )va_arg( argp, int ); + + assert( num >= 0 ); + if( http_MakeMessage + ( buf, http_major_version, http_minor_version, "sdc", + "CONTENT-LENGTH: ", num ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'S' || c == 'U' ) { + // SERVER or USER-AGENT header + + temp_str = ( c == 'S' ) ? "SERVER: " : "USER-AGENT: "; + get_sdk_info( tempbuf ); + if( http_MakeMessage + ( buf, http_major_version, http_minor_version, "ss", + temp_str, tempbuf ) != 0 ) { + goto error_handler; + } + } + +/* --- PATCH START - Sergey 'Jin' Bostandzhyan */ + if( c == 'X' ) // C string + { + s = ( char * )va_arg( argp, char * ); + + assert( s ); + + if( membuffer_append_str( buf, "X-User-Agent: ") != 0 ) { + goto error_handler; + } + if( membuffer_append( buf, s, strlen( s ) ) != 0 ) { + goto error_handler; + } + } + +/* --- PATCH END --- */ + + + else if( c == 'R' ) { + // response start line + // e.g.: 'HTTP/1.1 200 OK' + // + + // code + status_code = ( int )va_arg( argp, int ); + + assert( status_code > 0 ); + sprintf( tempbuf, "HTTP/%d.%d %d ", + http_major_version, http_minor_version, status_code ); + + // str + status_msg = http_get_code_text( status_code ); + if( http_MakeMessage + ( buf, http_major_version, http_minor_version, "ssc", + tempbuf, status_msg ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'B' ) { + // body of a simple reply + // + + status_code = ( int )va_arg( argp, int ); + + sprintf( tempbuf, "%s%d %s%s", + "

", + status_code, http_get_code_text( status_code ), + "

" ); + num = strlen( tempbuf ); + + if( http_MakeMessage( buf, http_major_version, http_minor_version, "NTcs", num, // content-length + "text/html", // content-type + tempbuf ) != 0 ) // body + { + goto error_handler; + } + } + + else if( c == 'Q' ) { + // request start line + // GET /foo/bar.html HTTP/1.1\r\n + // + + method = ( http_method_t ) va_arg( argp, http_method_t ); + method_str = method_to_str( method ); + url_str = ( const char * )va_arg( argp, const char * ); + num = ( int )va_arg( argp, int ); // length of url_str + + if( http_MakeMessage( buf, http_major_version, http_minor_version, "ssbsdsdc", method_str, // method + " ", url_str, num, // url + " HTTP/", http_major_version, ".", + http_minor_version ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'q' ) { + // request start line and HOST header + + method = ( http_method_t ) va_arg( argp, http_method_t ); + + uri_ptr = ( uri_type * ) va_arg( argp, uri_type * ); + assert( uri_ptr ); + if( http_FixUrl( uri_ptr, &url ) != 0 ) { + error_code = UPNP_E_INVALID_URL; + goto error_handler; + } + + if( http_MakeMessage + ( buf, http_major_version, http_minor_version, "Q" "sbc", + method, url.pathquery.buff, url.pathquery.size, "HOST: ", + url.hostport.text.buff, url.hostport.text.size ) != 0 ) { + goto error_handler; + } + } + + else if( c == 'T' ) { + // content type header + temp_str = ( const char * )va_arg( argp, const char * ); // type/subtype format + + if( http_MakeMessage + ( buf, http_major_version, http_minor_version, "ssc", + "CONTENT-TYPE: ", temp_str ) != 0 ) { + goto error_handler; + } + } + + else { + assert( 0 ); + } + } + + return 0; + + error_handler: + va_end( argp ); + membuffer_destroy( buf ); + return error_code; +} + +/************************************************************************ +* Function : http_CalcResponseVersion +* +* Parameters : +* IN int request_major_vers : Request major version +* IN int request_minor_vers : Request minor version +* OUT int* response_major_vers : Response mojor version +* OUT int* response_minor_vers : Response minor version +* +* Description : Calculate HTTP response versions based on the request +* versions. +* +* Return : void +* +* Note : +************************************************************************/ +void +http_CalcResponseVersion( IN int request_major_vers, + IN int request_minor_vers, + OUT int *response_major_vers, + OUT int *response_minor_vers ) +{ + if( ( request_major_vers > 1 ) || + ( request_major_vers == 1 && request_minor_vers >= 1 ) + ) { + *response_major_vers = 1; + *response_minor_vers = 1; + } else { + *response_major_vers = request_major_vers; + *response_minor_vers = request_minor_vers; + } +} + +/************************************************************************ +* Function: MakeGetMessageEx +* +* Parameters: +* const char *url_str ; String as a URL +* membuffer *request ; Buffer containing the request +* uri_type *url ; URI object containing the scheme, path +* query token, etc. +* +* Description: Makes the message for the HTTP GET method +* +* Returns: +* UPNP_E_INVALID_URL +* Error Codes returned by http_MakeMessage +* UPNP_E_SUCCESS +************************************************************************/ +int +MakeGetMessageEx( const char *url_str, + membuffer * request, + uri_type * url, + struct SendInstruction *pRangeSpecifier ) +{ + int errCode = UPNP_E_SUCCESS; + char *urlPath = NULL; + int hostlen = 0; + char *hoststr, + *temp; + + do { + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "DOWNLOAD URL : %s\n", url_str ); + ) + + if( ( errCode = http_FixStrUrl( ( char * )url_str, + strlen( url_str ), + url ) ) != UPNP_E_SUCCESS ) { + break; + } + // make msg + membuffer_init( request ); + + urlPath = alloca( strlen( url_str ) + 1 ); + if( !urlPath ) { + errCode = UPNP_E_OUTOF_MEMORY; + break; + } + + memset( urlPath, 0, strlen( url_str ) + 1 ); + + strcpy( urlPath, url_str ); + + hoststr = strstr( urlPath, "//" ); + if( hoststr == NULL ) { + errCode = UPNP_E_INVALID_URL; + break; + } + + hoststr += 2; + temp = strchr( hoststr, '/' ); + if( temp == NULL ) { + errCode = UPNP_E_INVALID_URL; + break; + } + + *temp = '\0'; + hostlen = strlen( hoststr ); + *temp = '/'; + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HOSTNAME : %s Length : %d\n", hoststr, + hostlen ); + ) + + errCode = http_MakeMessage( request, + 1, + 1, + "QsbcGDCUc", + HTTPMETHOD_GET, + url->pathquery.buff, + url->pathquery.size, + "HOST: ", + hoststr, + hostlen, pRangeSpecifier ); + + if( errCode != 0 ) { + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Makemessage failed\n" ); + ) + membuffer_destroy( request ); + return errCode; + } + } while( 0 ); + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "HTTP Buffer:\n %s\n" "----------END--------\n", + request->buf ); + ) + + return errCode; +} + +#define SIZE_RANGE_BUFFER 50 + +/************************************************************************ +* Function : http_OpenHttpGetEx +* +* Parameters : +* IN const char *url_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN OUT char **contentType : Type of content +* OUT int *contentLength : length of content +* OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Makes the HTTP GET message, connects to the peer, +* sends the HTTP GET request, gets the response and parses the +* response. +* +* Return : int; +* UPNP_E_SUCCESS - On Success ; +* UPNP_E_INVALID_PARAM - Invalid Paramters ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_BAD_RESPONSE ; +* +* Note : +* +************************************************************************/ +int +http_OpenHttpGetEx( IN const char *url_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int lowRange, + IN int highRange, + IN int timeout ) +{ + int http_error_code; + memptr ctype; + int tcp_connection; + membuffer request; + http_get_handle_t *handle = NULL; + uri_type url; + parse_status_t status; + int errCode = UPNP_E_SUCCESS; + + // char rangeBuf[SIZE_RANGE_BUFFER]; + struct SendInstruction rangeBuf; + + do { + // Checking Input parameters + if( ( !url_str ) || ( !Handle ) || + ( !contentType ) || ( !httpStatus ) ) { + errCode = UPNP_E_INVALID_PARAM; + break; + } + // Initialize output parameters + ( *httpStatus ) = 0; + ( *Handle ) = handle; + ( *contentType ) = NULL; + ( *contentLength ) = 0; + + if( lowRange > highRange ) { + errCode = UPNP_E_INTERNAL_ERROR; + break; + } + + memset( &rangeBuf, 0, sizeof( rangeBuf ) ); + sprintf( rangeBuf.RangeHeader, "Range: bytes=%d-%d\r\n", + lowRange, highRange ); + + membuffer_init( &request ); + + if( ( errCode = MakeGetMessageEx( url_str, + &request, &url, &rangeBuf ) ) + != UPNP_E_SUCCESS ) { + break; + } + + handle = + ( http_get_handle_t * ) malloc( sizeof( http_get_handle_t ) ); + if( handle == NULL ) { + errCode = UPNP_E_OUTOF_MEMORY; + break; + } + + memset( handle, 0, sizeof( *handle ) ); + + handle->entity_offset = 0; + parser_response_init( &handle->response, HTTPMETHOD_GET ); + + tcp_connection = socket( AF_INET, SOCK_STREAM, 0 ); + if( tcp_connection == -1 ) { + errCode = UPNP_E_SOCKET_ERROR; + free( handle ); + break; + } + + if( sock_init( &handle->sock_info, tcp_connection ) != + UPNP_E_SUCCESS ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + errCode = UPNP_E_SOCKET_ERROR; + free( handle ); + break; + } + + errCode = connect( handle->sock_info.socket, + ( struct sockaddr * )&url.hostport.IPv4address, + sizeof( struct sockaddr_in ) ); + if( errCode == -1 ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + errCode = UPNP_E_SOCKET_CONNECT; + free( handle ); + break; + } + // send request + errCode = http_SendMessage( &handle->sock_info, + &timeout, + "b", request.buf, request.length ); + + if( errCode != UPNP_E_SUCCESS ) { + sock_destroy( &handle->sock_info, SD_BOTH ); + free( handle ); + break; + } + + status = ReadResponseLineAndHeaders( &handle->sock_info, + &handle->response, + &timeout, &http_error_code ); + + if( status != PARSE_OK ) { + errCode = UPNP_E_BAD_RESPONSE; + free( handle ); + break; + } + + status = parser_get_entity_read_method( &handle->response ); + if( ( status != PARSE_CONTINUE_1 ) && ( status != PARSE_SUCCESS ) ) { + errCode = UPNP_E_BAD_RESPONSE; + free( handle ); + break; + } + + ( *httpStatus ) = handle->response.msg.status_code; + errCode = UPNP_E_SUCCESS; + + if( httpmsg_find_hdr( &handle->response.msg, + HDR_CONTENT_TYPE, &ctype ) == NULL ) { + *contentType = NULL; // no content-type + } else { + *contentType = ctype.buf; + } + + if( handle->response.position == POS_COMPLETE ) { + ( *contentLength ) = 0; + } else if( handle->response.ent_position == ENTREAD_USING_CHUNKED ) { + ( *contentLength ) = UPNP_USING_CHUNKED; + } else if( handle->response.ent_position == ENTREAD_USING_CLEN ) { + ( *contentLength ) = handle->response.content_length; + } else if( handle->response.ent_position == ENTREAD_UNTIL_CLOSE ) { + ( *contentLength ) = UPNP_UNTIL_CLOSE; + } + + ( *Handle ) = handle; + + } while( 0 ); + + membuffer_destroy( &request ); + + return errCode; +} + +/************************************************************************ +* Function : get_sdk_info +* +* Parameters : +* OUT char *info : buffer to store the operating system +* information +* +* Description : Returns the server information for the operating +* system +* +* Return : XINLINE void +* +* Note : +************************************************************************/ +// 'info' should have a size of at least 100 bytes +void +get_sdk_info( OUT char *info ) +{ + int ret_code; + struct utsname sys_info; + + ret_code = uname( &sys_info ); + if( ret_code == -1 ) { + *info = '\0'; + } + + sprintf( info, "%s/%s, UPnP/1.0, Intel SDK for UPnP devices/" + PACKAGE_VERSION "\r\n", + sys_info.sysname, sys_info.release ); +} diff --git a/libupnp/upnp/src/genlib/net/http/parsetools.c b/libupnp/upnp/src/genlib/net/http/parsetools.c new file mode 100644 index 0000000..b20d4e2 --- /dev/null +++ b/libupnp/upnp/src/genlib/net/http/parsetools.c @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file a function to extract the header information from * +* an http message and then matches the data with XML data. * +************************************************************************/ + +#include "config.h" +#include +#include "util.h" +#include "membuffer.h" +#include "httpparser.h" +#include "statcodes.h" +#include "parsetools.h" + +/************************************************************************ +* Function: has_xml_content_type +* +* Parameters: +* IN http_message_t* hmsg ; HTTP Message object +* +* Description: Find the header from the HTTP message and match the +* header for xml data. +* +* Returns: +* BOOLEAN +************************************************************************/ +xboolean +has_xml_content_type( IN http_message_t * hmsg ) +{ + memptr hdr_value; + + assert( hmsg ); + + // find 'content-type' header which must have text/xml + if( httpmsg_find_hdr( hmsg, HDR_CONTENT_TYPE, &hdr_value ) != NULL && + matchstr( hdr_value.buf, hdr_value.length, + "%itext%w/%wxml" ) == PARSE_OK ) { + return TRUE; + } + return FALSE; +} diff --git a/libupnp/upnp/src/genlib/net/http/statcodes.c b/libupnp/upnp/src/genlib/net/http/statcodes.c new file mode 100644 index 0000000..d5da8cb --- /dev/null +++ b/libupnp/upnp/src/genlib/net/http/statcodes.c @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file defines status codes, buffers to store the status * +* messages and functions to manipulate those buffers * +************************************************************************/ + +#include "config.h" +#include +#include +#include "util.h" +#include "statcodes.h" + +#define NUM_1XX_CODES 2 +static const char *Http1xxCodes[NUM_1XX_CODES]; +static const char *Http1xxStr = "Continue\0" "Switching Protocols\0"; + +#define NUM_2XX_CODES 7 +static const char *Http2xxCodes[NUM_2XX_CODES]; +static const char *Http2xxStr = + "OK\0" + "Created\0" + "Accepted\0" + "Non-Authoratative Information\0" + "No Content\0" "Reset Content\0" "Partial Content\0"; + +#define NUM_3XX_CODES 8 +static const char *Http3xxCodes[NUM_3XX_CODES]; +static const char *Http3xxStr = + "Multiple Choices\0" + "Moved Permanently\0" + "Found\0" + "See Other\0" + "Not Modified\0" "Use Proxy\0" "\0" "Temporary Redirect\0"; + +#define NUM_4XX_CODES 18 +static const char *Http4xxCodes[NUM_4XX_CODES]; +static const char *Http4xxStr = + "Bad Request\0" + "Unauthorized\0" + "Payment Required\0" + "Forbidden\0" + "Not Found\0" + "Method Not Allowed\0" + "Not Acceptable\0" + "Proxy Authentication Required\0" + "Request Timeout\0" + "Conflict\0" + "Gone\0" + "Length Required\0" + "Precondition Failed\0" + "Request Entity Too Large\0" + "Request-URI Too Long\0" + "Unsupported Media Type\0" + "Requested Range Not Satisfiable\0" "Expectation Failed\0"; + +#define NUM_5XX_CODES 6 +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"; + +static xboolean gInitialized = FALSE; + +/************************************************************************ +************************* Functions ************************************* +************************************************************************/ + +/************************************************************************ +* Function: init_table +* +* Parameters: +* IN const char* encoded_str ; status code encoded string +* OUT const char *table[] ; table to store the encoded status code +* strings +* IN int tbl_size ; size of the table +* +* Description: Initializing table representing an array of string +* pointers with the individual strings that are comprised in the +* input const char* encoded_str parameter. +* +* Returns: +* void +************************************************************************/ +static XINLINE void +init_table( IN const char *encoded_str, + OUT const char *table[], + IN int tbl_size ) +{ + int i; + const char *s = encoded_str; + + for( i = 0; i < tbl_size; i++ ) { + table[i] = s; + s += strlen( s ) + 1; // next entry + } +} + +/************************************************************************ +* Function: init_tables +* +* Parameters: +* none +* +* Description: Initializing tables with HTTP strings and different HTTP +* codes. +* +* Returns: +* void +************************************************************************/ +static XINLINE void +init_tables( void ) +{ + init_table( Http1xxStr, Http1xxCodes, NUM_1XX_CODES ); + init_table( Http2xxStr, Http2xxCodes, NUM_2XX_CODES ); + init_table( Http3xxStr, Http3xxCodes, NUM_3XX_CODES ); + init_table( Http4xxStr, Http4xxCodes, NUM_4XX_CODES ); + init_table( Http5xxStr, Http5xxCodes, NUM_5XX_CODES ); + + gInitialized = TRUE; // mark only after complete +} + +/************************************************************************ +* Function: http_get_code_text +* +* Parameters: +* int statusCode ; Status code based on which the status table and +* status message is returned +* +* Description: Return the right status message based on the passed in +* int statusCode input parameter +* +* Returns: +* const char* ptr - pointer to the status message string +************************************************************************/ +const char * +http_get_code_text( int statusCode ) +{ + int index; + int table_num; + + if( !gInitialized ) { + init_tables( ); + } + + if( statusCode < 100 && statusCode >= 600 ) { + return NULL; + } + + index = statusCode % 100; + table_num = statusCode / 100; + + if( table_num == 1 && index < NUM_1XX_CODES ) { + return Http1xxCodes[index]; + } + + if( table_num == 2 && index < NUM_2XX_CODES ) { + return Http2xxCodes[index]; + } + + if( table_num == 3 && index < NUM_3XX_CODES ) { + return Http3xxCodes[index]; + } + + if( table_num == 4 && index < NUM_4XX_CODES ) { + return Http4xxCodes[index]; + } + + if( table_num == 5 && index < NUM_5XX_CODES ) { + return Http5xxCodes[index]; + } + + return NULL; +} diff --git a/libupnp/upnp/src/genlib/net/http/webserver.c b/libupnp/upnp/src/genlib/net/http/webserver.c new file mode 100644 index 0000000..b3a22fe --- /dev/null +++ b/libupnp/upnp/src/genlib/net/http/webserver.c @@ -0,0 +1,1760 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file defines the Web Server and has functions to carry out +* operations of the Web Server. +************************************************************************/ + +#include "config.h" +#include +#include +#include "util.h" +#include "strintmap.h" +#include "membuffer.h" +#include "httpparser.h" +#include "httpreadwrite.h" +#include "statcodes.h" +#include "webserver.h" +#include "upnp.h" +#include "upnpapi.h" +#include "ssdplib.h" + +#include +#include +#include "ithread.h" +#include "unixutil.h" + +/* + Response Types + */ +enum resp_type { RESP_FILEDOC, RESP_XMLDOC, RESP_HEADERS, RESP_WEBDOC, + RESP_POST }; + +// mapping of file extension to content-type of document +struct document_type_t { + const char *file_ext; + const char *content_type; + const char *content_subtype; +}; + +struct xml_alias_t { + membuffer name; // name of DOC from root; e.g.: /foo/bar/mydesc.xml + membuffer doc; // the XML document contents + time_t last_modified; + int *ct; +}; + +static const char *gMediaTypes[] = { + NULL, // 0 + "audio", // 1 + "video", // 2 + "image", // 3 + "application", // 4 + "text" // 5 +}; + +/* + Defines + */ + +// index into 'gMediaTypes' +#define AUDIO_STR "\1" +#define VIDEO_STR "\2" +#define IMAGE_STR "\3" +#define APPLICATION_STR "\4" +#define TEXT_STR "\5" + +// int index +#define APPLICATION_INDEX 4 +#define TEXT_INDEX 5 + +// general +#define NUM_MEDIA_TYPES 69 +#define NUM_HTTP_HEADER_NAMES 33 + +// sorted by file extension; must have 'NUM_MEDIA_TYPES' extensions +static const char *gEncodedMediaTypes = + "aif\0" AUDIO_STR "aiff\0" + "aifc\0" AUDIO_STR "aiff\0" + "aiff\0" AUDIO_STR "aiff\0" + "asf\0" VIDEO_STR "x-ms-asf\0" + "asx\0" VIDEO_STR "x-ms-asf\0" + "au\0" AUDIO_STR "basic\0" + "avi\0" VIDEO_STR "msvideo\0" + "bmp\0" IMAGE_STR "bmp\0" + "dcr\0" APPLICATION_STR "x-director\0" + "dib\0" IMAGE_STR "bmp\0" + "dir\0" APPLICATION_STR "x-director\0" + "dxr\0" APPLICATION_STR "x-director\0" + "gif\0" IMAGE_STR "gif\0" + "hta\0" TEXT_STR "hta\0" + "htm\0" TEXT_STR "html\0" + "html\0" TEXT_STR "html\0" + "jar\0" APPLICATION_STR "java-archive\0" + "jfif\0" IMAGE_STR "pjpeg\0" + "jpe\0" IMAGE_STR "jpeg\0" + "jpeg\0" IMAGE_STR "jpeg\0" + "jpg\0" IMAGE_STR "jpeg\0" + "js\0" APPLICATION_STR "x-javascript\0" + "kar\0" AUDIO_STR "midi\0" + "m3u\0" AUDIO_STR "mpegurl\0" + "mid\0" AUDIO_STR "midi\0" + "midi\0" AUDIO_STR "midi\0" + "mov\0" VIDEO_STR "quicktime\0" + "mp2v\0" VIDEO_STR "x-mpeg2\0" + "mp3\0" AUDIO_STR "mpeg\0" + "mpe\0" VIDEO_STR "mpeg\0" + "mpeg\0" VIDEO_STR "mpeg\0" + "mpg\0" VIDEO_STR "mpeg\0" + "mpv\0" VIDEO_STR "mpeg\0" + "mpv2\0" VIDEO_STR "x-mpeg2\0" + "pdf\0" APPLICATION_STR "pdf\0" + "pjp\0" IMAGE_STR "jpeg\0" + "pjpeg\0" IMAGE_STR "jpeg\0" + "plg\0" TEXT_STR "html\0" + "pls\0" AUDIO_STR "scpls\0" + "png\0" IMAGE_STR "png\0" + "qt\0" VIDEO_STR "quicktime\0" + "ram\0" AUDIO_STR "x-pn-realaudio\0" + "rmi\0" AUDIO_STR "mid\0" + "rmm\0" AUDIO_STR "x-pn-realaudio\0" + "rtf\0" APPLICATION_STR "rtf\0" + "shtml\0" TEXT_STR "html\0" + "smf\0" AUDIO_STR "midi\0" + "snd\0" AUDIO_STR "basic\0" + "spl\0" APPLICATION_STR "futuresplash\0" + "ssm\0" APPLICATION_STR "streamingmedia\0" + "swf\0" APPLICATION_STR "x-shockwave-flash\0" + "tar\0" APPLICATION_STR "tar\0" + "tcl\0" APPLICATION_STR "x-tcl\0" + "text\0" TEXT_STR "plain\0" + "tif\0" IMAGE_STR "tiff\0" + "tiff\0" IMAGE_STR "tiff\0" + "txt\0" TEXT_STR "plain\0" + "ulw\0" AUDIO_STR "basic\0" + "wav\0" AUDIO_STR "wav\0" + "wax\0" AUDIO_STR "x-ms-wax\0" + "wm\0" VIDEO_STR "x-ms-wm\0" + "wma\0" AUDIO_STR "x-ms-wma\0" + "wmv\0" VIDEO_STR "x-ms-wmv\0" + "wvx\0" VIDEO_STR "x-ms-wvx\0" + "xbm\0" IMAGE_STR "x-xbitmap\0" + "xml\0" TEXT_STR "xml\0" + "xsl\0" TEXT_STR "xml\0" + "z\0" APPLICATION_STR "x-compress\0" + "zip\0" APPLICATION_STR "zip\0" "\0"; + // *** end *** + +/***********************************************************************/ +/* + module variables - Globals, static and externs + */ + +/***********************************************************************/ +static struct document_type_t gMediaTypeList[NUM_MEDIA_TYPES]; +membuffer gDocumentRootDir; // a local dir which serves as webserver root +static struct xml_alias_t gAliasDoc; // XML document +static ithread_mutex_t gWebMutex; +extern str_int_entry Http_Header_Names[NUM_HTTP_HEADER_NAMES]; + +/************************************************************************ +* Function: has_xml_content_type +* +* Parameters: +* none +* +* Description: decodes list and stores it in gMediaTypeList +* +* Returns: +* void +************************************************************************/ +static XINLINE void +media_list_init( void ) +{ + int i; + const char *s = gEncodedMediaTypes; + struct document_type_t *doc_type; + + for( i = 0; *s != '\0'; i++ ) { + doc_type = &gMediaTypeList[i]; + + doc_type->file_ext = s; + + s += strlen( s ) + 1; // point to type + doc_type->content_type = gMediaTypes[( int )*s]; // set cont-type + + s++; // point to subtype + doc_type->content_subtype = s; + + s += strlen( s ) + 1; // next entry + } + assert( i == NUM_MEDIA_TYPES ); +} + +/************************************************************************ +* Function: has_xml_content_type +* +* Parameters: +* IN const char* extension ; +* OUT const char** con_type, +* OUT const char** con_subtype +* +* Description: Based on the extension, returns the content type and +* content subtype +* +* Returns: +* 0 on success; +* -1 on error +************************************************************************/ +static XINLINE int +search_extension( IN const char *extension, + OUT const char **con_type, + OUT const char **con_subtype ) +{ + int top, + mid, + bot; + int cmp; + + top = 0; + bot = NUM_MEDIA_TYPES - 1; + + while( top <= bot ) { + mid = ( top + bot ) / 2; + cmp = strcasecmp( extension, gMediaTypeList[mid].file_ext ); + + if( cmp > 0 ) { + top = mid + 1; // look below mid + } else if( cmp < 0 ) { + bot = mid - 1; // look above mid + } else // cmp == 0 + { + *con_type = gMediaTypeList[mid].content_type; + *con_subtype = gMediaTypeList[mid].content_subtype; + return 0; + } + } + + return -1; +} + +/************************************************************************ +* Function: get_content_type +* +* Parameters: +* IN const char* filename, +* OUT DOMString* content_type +* +* Description: Based on the extension, clones an XML string based on +* type and content subtype. If content type and sub type are not +* found, unknown types are used +* +* Returns: +* 0 - On Sucess +* UPNP_E_OUTOF_MEMORY - on memory allocation failures +************************************************************************/ +XINLINE int +get_content_type( IN const char *filename, + OUT DOMString * content_type ) +{ + const char *extension; + const char *type, + *subtype; + xboolean ctype_found = FALSE; + char *temp = NULL; + int length = 0; + + ( *content_type ) = NULL; + + // get ext + extension = strrchr( filename, '.' ); + if( extension != NULL ) { + if( search_extension( extension + 1, &type, &subtype ) == 0 ) { + ctype_found = TRUE; + } + } + + if( !ctype_found ) { + // unknown content type + type = gMediaTypes[APPLICATION_INDEX]; + subtype = "octet-stream"; + } + + length = strlen( type ) + strlen( "/" ) + strlen( subtype ) + 1; + temp = ( char * )malloc( length ); + + if( !temp ) { + return UPNP_E_OUTOF_MEMORY; + } + + sprintf( temp, "%s/%s", type, subtype ); + ( *content_type ) = ixmlCloneDOMString( temp ); + + free( temp ); + + if( !content_type ) { + return UPNP_E_OUTOF_MEMORY; + } + + return 0; +} + +/************************************************************************ +* Function: glob_alias_init +* +* Parameters: +* none +* +* Description: Initialize the global XML document. Allocate buffers +* for the XML document +* +* Returns: +* void +************************************************************************/ +static XINLINE void +glob_alias_init( void ) +{ + struct xml_alias_t *alias = &gAliasDoc; + + membuffer_init( &alias->doc ); + membuffer_init( &alias->name ); + alias->ct = NULL; + alias->last_modified = 0; +} + +/************************************************************************ +* Function: is_valid_alias +* +* Parameters: +* IN const struct xml_alias_t* alias ; XML alias object +* +* Description: Check for the validity of the XML object buffer +* +* Returns: +* BOOLEAN +************************************************************************/ +static XINLINE xboolean +is_valid_alias( IN const struct xml_alias_t *alias ) +{ + return alias->doc.buf != NULL; +} + +/************************************************************************ +* Function: alias_grab +* +* Parameters: +* OUT struct xml_alias_t* alias ; XML alias object +* +* Description: Copy the contents of the global XML document into the +* local OUT parameter +* +* Returns: +* void +************************************************************************/ +static void +alias_grab( OUT struct xml_alias_t *alias ) +{ + ithread_mutex_lock( &gWebMutex ); + + assert( is_valid_alias( &gAliasDoc ) ); + + memcpy( alias, &gAliasDoc, sizeof( struct xml_alias_t ) ); + *alias->ct = *alias->ct + 1; + + ithread_mutex_unlock( &gWebMutex ); +} + +/************************************************************************ +* Function: alias_release +* +* Parameters: +* IN struct xml_alias_t* alias ; XML alias object +* +* Description: Release the XML document referred to by the IN parameter +* Free the allocated buffers associated with this object +* +* Returns: +* void +************************************************************************/ +static void +alias_release( IN struct xml_alias_t *alias ) +{ + ithread_mutex_lock( &gWebMutex ); + + // ignore invalid alias + if( !is_valid_alias( alias ) ) { + ithread_mutex_unlock( &gWebMutex ); + return; + } + + assert( alias->ct > 0 ); + + *alias->ct = *alias->ct - 1; + if( *alias->ct <= 0 ) { + membuffer_destroy( &alias->doc ); + membuffer_destroy( &alias->name ); + free( alias->ct ); + } + ithread_mutex_unlock( &gWebMutex ); +} + +/************************************************************************ +* Function: web_server_set_alias +* +* Parameters: +* alias_name: webserver name of alias; created by caller and freed by +* caller (doesn't even have to be malloc()d .) +* alias_content: the xml doc; this is allocated by the caller; and +* freed by the web server +* alias_content_length: length of alias body in bytes +* last_modified: time when the contents of alias were last +* changed (local time) +* +* Description: Replaces current alias with the given alias. To remove +* the current alias, set alias_name to NULL. +* +* Returns: +* 0 - OK +* UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here +************************************************************************/ +int +web_server_set_alias( IN const char *alias_name, + IN const char *alias_content, + IN size_t alias_content_length, + IN time_t last_modified ) +{ + int ret_code; + struct xml_alias_t alias; + + alias_release( &gAliasDoc ); + + if( alias_name == NULL ) { + // don't serve aliased doc anymore + return 0; + } + + assert( alias_content != NULL ); + + membuffer_init( &alias.doc ); + membuffer_init( &alias.name ); + alias.ct = NULL; + + do { + // insert leading /, if missing + if( *alias_name != '/' ) { + if( membuffer_assign_str( &alias.name, "/" ) != 0 ) { + break; // error; out of mem + } + } + + ret_code = membuffer_append_str( &alias.name, alias_name ); + if( ret_code != 0 ) { + break; // error + } + + if( ( alias.ct = ( int * )malloc( sizeof( int ) ) ) == NULL ) { + break; // error + } + *alias.ct = 1; + membuffer_attach( &alias.doc, ( char * )alias_content, + alias_content_length ); + + alias.last_modified = last_modified; + + // save in module var + ithread_mutex_lock( &gWebMutex ); + gAliasDoc = alias; + ithread_mutex_unlock( &gWebMutex ); + + return 0; + } while( FALSE ); + + // error handler + + // free temp alias + membuffer_destroy( &alias.name ); + membuffer_destroy( &alias.doc ); + free( alias.ct ); + return UPNP_E_OUTOF_MEMORY; +} + +/************************************************************************ +* Function: web_server_init +* +* Parameters: +* none +* +* Description: Initilialize the different documents. Initialize the +* memory for root directory for web server. Call to initialize global +* XML document. Sets bWebServerState to WEB_SERVER_ENABLED +* +* Returns: +* 0 - OK +* UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here +************************************************************************/ +int +web_server_init( void ) +{ + int ret_code; + + if( bWebServerState == WEB_SERVER_DISABLED ) { + media_list_init( ); // decode media list + membuffer_init( &gDocumentRootDir ); + glob_alias_init( ); + + pVirtualDirList = NULL; + + ret_code = ithread_mutex_init( &gWebMutex, NULL ); + if( ret_code == -1 ) { + return UPNP_E_OUTOF_MEMORY; + } + bWebServerState = WEB_SERVER_ENABLED; + } + + return 0; +} + +/************************************************************************ +* Function: web_server_destroy +* +* Parameters: +* none +* +* Description: Release memory allocated for the global web server root +* directory and the global XML document +* Resets the flag bWebServerState to WEB_SERVER_DISABLED +* +* Returns: +* void +************************************************************************/ +void +web_server_destroy( void ) +{ + int ret; + + if( bWebServerState == WEB_SERVER_ENABLED ) { + membuffer_destroy( &gDocumentRootDir ); + alias_release( &gAliasDoc ); + + ithread_mutex_lock( &gWebMutex ); + memset( &gAliasDoc, 0, sizeof( struct xml_alias_t ) ); + ithread_mutex_unlock( &gWebMutex ); + + ret = ithread_mutex_destroy( &gWebMutex ); + assert( ret == 0 ); + bWebServerState = WEB_SERVER_DISABLED; + } +} + +/************************************************************************ +* Function: get_file_info +* +* Parameters: +* IN const char* filename ; Filename having the description document +* OUT struct File_Info * info ; File information object having file +* attributes such as filelength, when was +* the file last modified, whether a file +* or a directory and whether the file or +* directory is readable. +* +* Description: Release memory allocated for the global web server root +* directory and the global XML document +* Resets the flag bWebServerState to WEB_SERVER_DISABLED +* +* Returns: +* int +************************************************************************/ +static int +get_file_info( IN const char *filename, + OUT struct File_Info *info ) +{ + int code; + struct stat s; + FILE *fp; + int rc = 0; + + info->content_type = NULL; + + code = stat( filename, &s ); + if( code == -1 ) { + return -1; + } + + if( S_ISDIR( s.st_mode ) ) { + info->is_directory = TRUE; + } else if( S_ISREG( s.st_mode ) ) { + info->is_directory = FALSE; + } else { + return -1; + } + + // check readable + fp = fopen( filename, "r" ); + info->is_readable = ( fp != NULL ); + if( fp ) { + fclose( fp ); + } + + info->file_length = s.st_size; + info->last_modified = s.st_mtime; + + rc = get_content_type( filename, &info->content_type ); + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "file info: %s, length: %d, last_mod=%s readable=%d\n", + filename, info->file_length, + asctime( gmtime( &info->last_modified ) ), + info->is_readable ); ) + + return rc; +} + +/************************************************************************ +* Function: web_server_set_root_dir +* +* Parameters: +* IN const char* root_dir ; String having the root directory for the +* document +* +* Description: Assign the path specfied by the IN const char* root_dir +* parameter to the global Document root directory. Also check for +* path names ending in '/' +* +* Returns: +* int +************************************************************************/ +int +web_server_set_root_dir( IN const char *root_dir ) +{ + int index; + int ret; + + ret = membuffer_assign_str( &gDocumentRootDir, root_dir ); + if( ret != 0 ) { + return ret; + } + // remove trailing '/', if any + if( gDocumentRootDir.length > 0 ) { + index = gDocumentRootDir.length - 1; // last char + if( gDocumentRootDir.buf[index] == '/' ) { + membuffer_delete( &gDocumentRootDir, index, 1 ); + } + } + + return 0; +} + +/************************************************************************ +* Function: get_alias +* +* Parameters: +* IN const char* request_file ; request file passed in to be compared with +* OUT struct xml_alias_t* alias ; xml alias object which has a file name +* stored +* OUT struct File_Info * info ; File information object which will be +* filled up if the file comparison +* succeeds +* +* Description: Compare the files names between the one on the XML alias +* the one passed in as the input parameter. If equal extract file +* information +* +* Returns: +* TRUE - On Success +* FALSE if request is not an alias +************************************************************************/ +static XINLINE xboolean +get_alias( IN const char *request_file, + OUT struct xml_alias_t *alias, + OUT struct File_Info *info ) +{ + int cmp; + + cmp = strcmp( alias->name.buf, request_file ); + if( cmp == 0 ) { + // fill up info + info->file_length = alias->doc.length; + info->is_readable = TRUE; + info->is_directory = FALSE; + info->last_modified = alias->last_modified; + } + + return cmp == 0; +} + +/************************************************************************ +* Function: isFileInVirtualDir +* +* Parameters: +* IN char *filePath ; directory path to be tested for virtual directory +* +* Description: Compares filePath with paths from the list of virtual +* directory lists +* +* Returns: +* BOOLEAN +************************************************************************/ +int +isFileInVirtualDir( IN char *filePath ) +{ + virtualDirList *pCurVirtualDir; + int webDirLen; + + pCurVirtualDir = pVirtualDirList; + while( pCurVirtualDir != NULL ) { + webDirLen = strlen( pCurVirtualDir->dirName ); + if( pCurVirtualDir->dirName[webDirLen - 1] == '/' ) { + if( strncmp( pCurVirtualDir->dirName, filePath, webDirLen ) == + 0 ) + return TRUE; + } else { + if( ( strncmp( pCurVirtualDir->dirName, filePath, webDirLen ) + == 0 ) && ( filePath[webDirLen] == '/' ) ) + return TRUE; + } + + pCurVirtualDir = pCurVirtualDir->next; + } + + return FALSE; +} + +/************************************************************************ +* Function: ToUpperCase +* +* Parameters: +* INOUT char * Str ; Input string to be converted +* +* Description: Converts input string to upper case +* +* Returns: +* int +************************************************************************/ +int +ToUpperCase( char *Str ) +{ + int i; + + for( i = 0; i < ( int )strlen( Str ); i++ ) + Str[i] = toupper( Str[i] ); + return 1; + +} + +/************************************************************************ +* Function: StrStr +* +* Parameters: +* IN char * S1 ; Input string +* IN char * S2 ; Input sub-string +* +* Description: Finds a substring from a string +* +* Returns: +* char * ptr - pointer to the first occurence of S2 in S1 +************************************************************************/ +char * +StrStr( char *S1, + char *S2 ) +{ + char *Str1, + *Str2; + char *Ptr, + *Ret; + int Pos; + + Str1 = ( char * )malloc( strlen( S1 ) + 2 ); + if(!Str1) return NULL; + Str2 = ( char * )malloc( strlen( S2 ) + 2 ); + if (!Str2) + { + free(Str1); + return NULL; + } + + strcpy( Str1, S1 ); + strcpy( Str2, S2 ); + + ToUpperCase( Str1 ); + ToUpperCase( Str2 ); + Ptr = strstr( Str1, Str2 ); + if( Ptr == NULL ) + return NULL; + + Pos = Ptr - Str1; + + Ret = S1 + Pos; + + free( Str1 ); + free( Str2 ); + return Ret; + +} + +/************************************************************************ +* Function: StrTok +* +* Parameters: +* IN char ** Src ; String containing the token +* IN char * del ; Set of delimiter characters +* +* Description: Finds next token in a string +* +* Returns: +* char * ptr - pointer to the first occurence of S2 in S1 +************************************************************************/ +char * +StrTok( char **Src, + char *Del ) +{ + char *TmpPtr, + *RetPtr; + + if( *Src != NULL ) { + RetPtr = *Src; + TmpPtr = strstr( *Src, Del ); + if( TmpPtr != NULL ) { + *TmpPtr = '\0'; + *Src = TmpPtr + strlen( Del ); + } else + *Src = NULL; + + return RetPtr; + } + + return NULL; +} + +/************************************************************************ +* Function: GetNextRange +* +* Parameters: +* IN char ** SrcRangeStr ; string containing the token / range +* OUT int * FirstByte ; gets the first byte of the token +* OUT int * LastByte ; gets the last byte of the token +* +* Description: Returns a range of integers from a sring +* +* Returns: int ; +* always returns 1; +************************************************************************/ +int +GetNextRange( char **SrcRangeStr, + int *FirstByte, + int *LastByte ) +{ + char *Ptr, + *Tok; + int i, + F = -1, + L = -1; + int Is_Suffix_byte_Range = 1; + + if( *SrcRangeStr == NULL ) + return -1; + + Tok = StrTok( SrcRangeStr, "," ); + + if( ( Ptr = strstr( Tok, "-" ) ) == NULL ) + return -1; + *Ptr = ' '; + sscanf( Tok, "%d%d", &F, &L ); + + if( F == -1 || L == -1 ) { + *Ptr = '-'; + for( i = 0; i < ( int )strlen( Tok ); i++ ) { + if( Tok[i] == '-' ) { + break; + } else if( isdigit( Tok[i] ) ) { + Is_Suffix_byte_Range = 0; + break; + } + + } + + if( Is_Suffix_byte_Range ) { + *FirstByte = L; + *LastByte = F; + return 1; + } + } + + *FirstByte = F; + *LastByte = L; + return 1; + +} + +/************************************************************************ +* Function: CreateHTTPRangeResponseHeader +* +* Parameters: +* char * ByteRangeSpecifier ; String containing the range +* long FileLength ; Length of the file +* OUT struct SendInstruction * Instr ; SendInstruction object where the +* range operations will be stored +* +* Description: Fills in the Offset, read size and contents to send out +* as an HTTP Range Response +* +* Returns: +* HTTP_BAD_REQUEST +* UPNP_E_OUTOF_MEMORY +* HTTP_REQUEST_RANGE_NOT_SATISFIABLE +* HTTP_OK +************************************************************************/ +int +CreateHTTPRangeResponseHeader( char *ByteRangeSpecifier, + long FileLength, + OUT struct SendInstruction *Instr ) +{ + + int FirstByte, + LastByte; + char *RangeInput, + *Ptr; + + Instr->IsRangeActive = 1; + Instr->ReadSendSize = FileLength; + + if( !ByteRangeSpecifier ) + return HTTP_BAD_REQUEST; + + RangeInput = malloc( strlen( ByteRangeSpecifier ) + 1 ); + if( !RangeInput ) + return UPNP_E_OUTOF_MEMORY; + strcpy( RangeInput, ByteRangeSpecifier ); + + //CONTENT-RANGE: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT + if( StrStr( RangeInput, "bytes" ) == NULL || + ( Ptr = StrStr( RangeInput, "=" ) ) == NULL ) { + free( RangeInput ); + Instr->IsRangeActive = 0; + return HTTP_BAD_REQUEST; + } + //Jump = + Ptr = Ptr + 1; + + if( FileLength < 0 ) { + free( RangeInput ); + return HTTP_REQUEST_RANGE_NOT_SATISFIABLE; + } + + if( GetNextRange( &Ptr, &FirstByte, &LastByte ) != -1 ) { + + if( FileLength < FirstByte ) { + free( RangeInput ); + return HTTP_REQUEST_RANGE_NOT_SATISFIABLE; + } + + if( FirstByte >= 0 && LastByte >= 0 && LastByte >= FirstByte ) { + if( LastByte >= FileLength ) + LastByte = FileLength - 1; + + Instr->RangeOffset = FirstByte; + Instr->ReadSendSize = LastByte - FirstByte + 1; + sprintf( Instr->RangeHeader, "CONTENT-RANGE: bytes %d-%d/%ld\r\n", FirstByte, LastByte, FileLength ); //Data between two range. + } else if( FirstByte >= 0 && LastByte == -1 + && FirstByte < FileLength ) { + Instr->RangeOffset = FirstByte; + Instr->ReadSendSize = FileLength - FirstByte; + sprintf( Instr->RangeHeader, + "CONTENT-RANGE: bytes %d-%ld/%ld\r\n", FirstByte, + FileLength - 1, FileLength ); + } else if( FirstByte == -1 && LastByte > 0 ) { + if( LastByte >= FileLength ) { + Instr->RangeOffset = 0; + Instr->ReadSendSize = FileLength; + sprintf( Instr->RangeHeader, + "CONTENT-RANGE: bytes 0-%ld/%ld\r\n", + FileLength - 1, FileLength ); + } else { + Instr->RangeOffset = FileLength - LastByte; + Instr->ReadSendSize = LastByte; + sprintf( Instr->RangeHeader, + "CONTENT-RANGE: bytes %ld-%ld/%ld\r\n", + FileLength - LastByte + 1, FileLength, + FileLength ); + } + } else { + free( RangeInput ); + return HTTP_REQUEST_RANGE_NOT_SATISFIABLE; + } + } else { + free( RangeInput ); + return HTTP_REQUEST_RANGE_NOT_SATISFIABLE; + } + + free( RangeInput ); + return HTTP_OK; +} + +/************************************************************************ +* Function: CheckOtherHTTPHeaders +* +* Parameters: +* IN http_message_t * Req ; HTTP Request message +* OUT struct SendInstruction * RespInstr ; Send Instruction object to +* data for the response +* int FileSize ; Size of the file containing the request document +* +* Description: Get header id from the request parameter and take +* appropriate action based on the ids. +* as an HTTP Range Response +* +* Returns: +* HTTP_BAD_REQUEST +* UPNP_E_OUTOF_MEMORY +* HTTP_REQUEST_RANGE_NOT_SATISFIABLE +* HTTP_OK +************************************************************************/ +int +CheckOtherHTTPHeaders( IN http_message_t * Req, + OUT struct SendInstruction *RespInstr, + int FileSize ) +{ + http_header_t *header; + ListNode *node; + + //NNS: dlist_node* node; + int index, + RetCode = HTTP_OK; + char *TmpBuf; + + TmpBuf = ( char * )malloc( LINE_SIZE ); + if( !TmpBuf ) + return UPNP_E_OUTOF_MEMORY; + + node = ListHead( &Req->headers ); + + while( node != NULL ) { + header = ( http_header_t * ) node->item; + + // find header type. + index = map_str_to_int( ( const char * )header->name.buf, + header->name.length, Http_Header_Names, + NUM_HTTP_HEADER_NAMES, FALSE ); + + if( header->value.length >= LINE_SIZE ) { + free( TmpBuf ); + TmpBuf = ( char * )malloc( header->value.length + 1 ); + if( !TmpBuf ) + return UPNP_E_OUTOF_MEMORY; + } + + memcpy( TmpBuf, header->value.buf, header->value.length ); + TmpBuf[header->value.length] = '\0'; + if( index >= 0 ) + switch ( Http_Header_Names[index].id ) { + case HDR_TE: //Request + { + RespInstr->IsChunkActive = 1; + + if( strlen( TmpBuf ) > strlen( "gzip" ) ) { + if( StrStr( TmpBuf, "trailers" ) != NULL ) { //means client will accept trailer + RespInstr->IsTrailers = 1; + } + } + } + break; + + case HDR_CONTENT_LENGTH: + { + RespInstr->RecvWriteSize = atoi( TmpBuf ); + break; + } + + case HDR_RANGE: + if( ( RetCode = CreateHTTPRangeResponseHeader( TmpBuf, + FileSize, + RespInstr ) ) + != HTTP_OK ) { + free( TmpBuf ); + return RetCode; + } + break; + default: + /* + TODO + */ + /* + header.value is the value. + */ + /* + case HDR_CONTENT_TYPE: //return 1; + case HDR_CONTENT_LANGUAGE://return 1; + case HDR_LOCATION: //return 1; + case HDR_CONTENT_LOCATION://return 1; + case HDR_ACCEPT: //return 1; + case HDR_ACCEPT_CHARSET://return 1; + case HDR_ACCEPT_LANGUAGE://return 1; + case HDR_USER_AGENT: break;//return 1; + */ + + //Header check for encoding + /* + case HDR_ACCEPT_RANGE: //Server capability. + case HDR_CONTENT_RANGE://Response. + case HDR_IF_RANGE: + */ + + //Header check for encoding + /* + case HDR_ACCEPT_ENCODING: + if(StrStr(TmpBuf, "identity")) + { + break; + } + else return -1; + case HDR_CONTENT_ENCODING: + case HDR_TRANSFER_ENCODING: //Response + */ + break; + } + + node = ListNext( &Req->headers, node ); + + } + + free( TmpBuf ); + return RetCode; +} + +/************************************************************************ +* Function: process_request +* +* Parameters: +* IN http_message_t *req ; HTTP Request message +* OUT enum resp_type *rtype ; Tpye of response +* OUT membuffer *headers ; +* OUT membuffer *filename ; Get filename from request document +* OUT struct xml_alias_t *alias ; Xml alias document from the +* request document, +* OUT struct SendInstruction * RespInstr ; Send Instruction object +* where the response is set up. +* +* Description: Processes the request and returns the result in the OUT +* parameters +* +* Returns: +* HTTP_BAD_REQUEST +* UPNP_E_OUTOF_MEMORY +* HTTP_REQUEST_RANGE_NOT_SATISFIABLE +* HTTP_OK +************************************************************************/ +static int +process_request( IN http_message_t * req, + OUT enum resp_type *rtype, + OUT membuffer * headers, + OUT membuffer * filename, + OUT struct xml_alias_t *alias, + OUT struct SendInstruction *RespInstr ) +{ + int code; + int err_code; + + //membuffer content_type; + char *request_doc; + struct File_Info finfo; + xboolean using_alias; + xboolean using_virtual_dir; + uri_type *url; + char *temp_str; + int resp_major, + resp_minor; + xboolean alias_grabbed; + int dummy; + struct UpnpVirtualDirCallbacks *pVirtualDirCallback; + + print_http_headers( req ); + + url = &req->uri; + assert( req->method == HTTPMETHOD_GET || + req->method == HTTPMETHOD_HEAD + || req->method == HTTPMETHOD_POST + || req->method == HTTPMETHOD_SIMPLEGET ); + + // init + request_doc = NULL; + finfo.content_type = NULL; + //membuffer_init( &content_type ); + alias_grabbed = FALSE; + err_code = HTTP_INTERNAL_SERVER_ERROR; // default error + using_virtual_dir = FALSE; + using_alias = FALSE; + + http_CalcResponseVersion( req->major_version, req->minor_version, + &resp_major, &resp_minor ); + + // + // remove dots + // + request_doc = malloc( url->pathquery.size + 1 ); + if( request_doc == NULL ) { + goto error_handler; // out of mem + } + memcpy( request_doc, url->pathquery.buff, url->pathquery.size ); + request_doc[url->pathquery.size] = '\0'; + dummy = url->pathquery.size; + remove_escaped_chars( request_doc, &dummy ); + code = remove_dots( request_doc, url->pathquery.size ); + if( code != 0 ) { + err_code = HTTP_FORBIDDEN; + goto error_handler; + } + + if( *request_doc != '/' ) { + // no slash + err_code = HTTP_BAD_REQUEST; + goto error_handler; + } + + if( isFileInVirtualDir( request_doc ) ) { + using_virtual_dir = TRUE; + RespInstr->IsVirtualFile = 1; + if( membuffer_assign_str( filename, request_doc ) != 0 ) { + goto error_handler; + } + + } else { + // + // try using alias + // + if( is_valid_alias( &gAliasDoc ) ) { + alias_grab( alias ); + alias_grabbed = TRUE; + + using_alias = get_alias( request_doc, alias, &finfo ); + if( using_alias == TRUE ) { + finfo.content_type = ixmlCloneDOMString( "text/xml" ); + + if( finfo.content_type == NULL ) { + goto error_handler; + } + } + } + } + + if( using_virtual_dir ) { + if( req->method != HTTPMETHOD_POST ) { + // get file info + pVirtualDirCallback = &virtualDirCallback; + if( pVirtualDirCallback->get_info( filename->buf, &finfo ) != + 0 ) { + err_code = HTTP_NOT_FOUND; + goto error_handler; + } + // try index.html if req is a dir + if( finfo.is_directory ) { + if( filename->buf[filename->length - 1] == '/' ) { + temp_str = "index.html"; + } else { + temp_str = "/index.html"; + } + if( membuffer_append_str( filename, temp_str ) != 0 ) { + goto error_handler; + } + // get info + if( ( pVirtualDirCallback-> + get_info( filename->buf, &finfo ) != UPNP_E_SUCCESS ) + || finfo.is_directory ) { + err_code = HTTP_NOT_FOUND; + goto error_handler; + } + } + // not readable + if( !finfo.is_readable ) { + err_code = HTTP_FORBIDDEN; + goto error_handler; + } + // finally, get content type + // if ( get_content_type(filename->buf, &content_type) != 0 ) + //{ + // goto error_handler; + // } + } + } else if( !using_alias ) { + if( gDocumentRootDir.length == 0 ) { + goto error_handler; + } + // + // get file name + // + + // filename str + if( membuffer_assign_str( filename, gDocumentRootDir.buf ) != 0 || + membuffer_append_str( filename, request_doc ) != 0 ) { + goto error_handler; // out of mem + } + // remove trailing slashes + while( filename->length > 0 && + filename->buf[filename->length - 1] == '/' ) { + membuffer_delete( filename, filename->length - 1, 1 ); + } + + if( req->method != HTTPMETHOD_POST ) { + // get info on file + if( get_file_info( filename->buf, &finfo ) != 0 ) { + err_code = HTTP_NOT_FOUND; + goto error_handler; + } + // try index.html if req is a dir + if( finfo.is_directory ) { + if( filename->buf[filename->length - 1] == '/' ) { + temp_str = "index.html"; + } else { + temp_str = "/index.html"; + } + if( membuffer_append_str( filename, temp_str ) != 0 ) { + goto error_handler; + } + // get info + if( get_file_info( filename->buf, &finfo ) != 0 || + finfo.is_directory ) { + err_code = HTTP_NOT_FOUND; + goto error_handler; + } + } + // not readable + if( !finfo.is_readable ) { + err_code = HTTP_FORBIDDEN; + goto error_handler; + } + + } + // finally, get content type + // if ( get_content_type(filename->buf, &content_type) != 0 ) + // { + // goto error_handler; + // } + } + + RespInstr->ReadSendSize = finfo.file_length; + + //Check other header field. + if( ( err_code = + CheckOtherHTTPHeaders( req, RespInstr, + finfo.file_length ) ) != HTTP_OK ) { + goto error_handler; + } + + if( req->method == HTTPMETHOD_POST ) { + *rtype = RESP_POST; + err_code = UPNP_E_SUCCESS; + goto error_handler; + } + + if( RespInstr->IsRangeActive && RespInstr->IsChunkActive ) { + +/* - PATCH START - Sergey 'Jin' Bostandzhyan + * added X-User-Agent header + */ + + //Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT + //Transfer-Encoding: chunked + // K means add chunky header ang G means range header. + if( http_MakeMessage( headers, resp_major, resp_minor, "RTGKDstcSXcCc", HTTP_PARTIAL_CONTENT, // status code + // RespInstr->ReadSendSize,// content length + finfo.content_type, + // content_type.buf, // content type + RespInstr, // Range + "LAST-MODIFIED: ", + &finfo.last_modified, + X_USER_AGENT) != 0 ) { + goto error_handler; + } + } else if( RespInstr->IsRangeActive && !RespInstr->IsChunkActive ) { + + //Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT + //Transfer-Encoding: chunked + // K means add chunky header ang G means range header. + if( http_MakeMessage( headers, resp_major, resp_minor, "RNTGDstcSXcCc", HTTP_PARTIAL_CONTENT, // status code + RespInstr->ReadSendSize, // content length + finfo.content_type, + //content_type.buf, // content type + RespInstr, //Range Info + "LAST-MODIFIED: ", + &finfo.last_modified, + X_USER_AGENT) != 0 ) { + goto error_handler; + } + + } else if( !RespInstr->IsRangeActive && RespInstr->IsChunkActive ) { + + //Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT + //Transfer-Encoding: chunked + // K means add chunky header ang G means range header. + if( http_MakeMessage( headers, resp_major, resp_minor, "RKTDstcSXcCc", HTTP_OK, // status code + //RespInstr->ReadSendSize,// content length + finfo.content_type, + // content_type.buf, // content type + "LAST-MODIFIED: ", + &finfo.last_modified, + X_USER_AGENT) != 0 ) { + goto error_handler; + } + + } else { + if( RespInstr->ReadSendSize >= 0 ) { + //Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT + //Transfer-Encoding: chunked + // K means add chunky header ang G means range header. + if( http_MakeMessage( headers, resp_major, resp_minor, "RNTDstcSXcCc", HTTP_OK, // status code + RespInstr->ReadSendSize, // content length + finfo.content_type, + //content_type.buf, // content type + "LAST-MODIFIED: ", + &finfo.last_modified, + X_USER_AGENT) != 0 ) { + goto error_handler; + } + } else { + //Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT + //Transfer-Encoding: chunked + // K means add chunky header ang G means range header. + if( http_MakeMessage( headers, resp_major, resp_minor, "RTDstcSXcCc", HTTP_OK, // status code + //RespInstr->ReadSendSize,// content length + finfo.content_type, + //content_type.buf, // content type + "LAST-MODIFIED: ", + &finfo.last_modified, + X_USER_AGENT) != 0 ) { + goto error_handler; + } + } + } +/* -- PATCH END -- */ + + if( req->method == HTTPMETHOD_HEAD ) { + *rtype = RESP_HEADERS; + } else if( using_alias ) { + // GET xml + *rtype = RESP_XMLDOC; + } else if( using_virtual_dir ) { + *rtype = RESP_WEBDOC; + } else { + // GET filename + *rtype = RESP_FILEDOC; + } + + //simple get http 0.9 as specified in http 1.0 + //don't send headers + if( req->method == HTTPMETHOD_SIMPLEGET ) { + membuffer_destroy( headers ); + } + + err_code = UPNP_E_SUCCESS; + + error_handler: + free( request_doc ); + ixmlFreeDOMString( finfo.content_type ); + // membuffer_destroy( &content_type ); + if( err_code != UPNP_E_SUCCESS && alias_grabbed ) { + alias_release( alias ); + } + + return err_code; +} + +/************************************************************************ +* Function: http_RecvPostMessage +* +* Parameters: +* http_parser_t* parser ; HTTP Parser object +* IN SOCKINFO *info ; Socket Information object +* char * filename ; File where received data is copied to +* struct SendInstruction * Instr ; Send Instruction object which gives +* information whether the file is a virtual file or not. +* +* Description: Receives the HTTP post message +* +* Returns: +* HTTP_INTERNAL_SERVER_ERROR +* HTTP_UNAUTHORIZED +* HTTP_REQUEST_RANGE_NOT_SATISFIABLE +* HTTP_OK +************************************************************************/ +int +http_RecvPostMessage( http_parser_t * parser, + IN SOCKINFO * info, + char *filename, + struct SendInstruction *Instr ) +{ + + unsigned int Data_Buf_Size = 1024; + char Buf[1024]; + int Timeout = 0; + long Num_Write = 0; + FILE *Fp; + parse_status_t status = PARSE_OK; + xboolean ok_on_close = FALSE; + unsigned int entity_offset = 0; + int num_read = 0; + int ret_code = 0; + + if( Instr && Instr->IsVirtualFile ) { + + Fp = virtualDirCallback.open( filename, UPNP_WRITE ); + if( Fp == NULL ) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + } else { + Fp = fopen( filename, "wb" ); + if( Fp == NULL ) { + return HTTP_UNAUTHORIZED; + } + } + + parser->position = POS_ENTITY; + + do { + //first parse what has already been gotten + if( parser->position != POS_COMPLETE ) { + status = parser_parse_entity( parser ); + } + + if( status == PARSE_INCOMPLETE_ENTITY ) { + // read until close + ok_on_close = TRUE; + } else if( ( status != PARSE_SUCCESS ) + && ( status != PARSE_CONTINUE_1 ) + && ( status != PARSE_INCOMPLETE ) ) { + //error + return HTTP_BAD_REQUEST; + } + //read more if necessary entity + while( ( ( entity_offset + Data_Buf_Size ) > + parser->msg.entity.length ) + && ( parser->position != POS_COMPLETE ) ) { + num_read = sock_read( info, Buf, sizeof( Buf ), &Timeout ); + if( num_read > 0 ) { + // append data to buffer + ret_code = membuffer_append( &parser->msg.msg, + Buf, num_read ); + if( ret_code != 0 ) { + // set failure status + parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR; + return HTTP_INTERNAL_SERVER_ERROR; + } + status = parser_parse_entity( parser ); + if( status == PARSE_INCOMPLETE_ENTITY ) { + // read until close + ok_on_close = TRUE; + } else if( ( status != PARSE_SUCCESS ) + && ( status != PARSE_CONTINUE_1 ) + && ( status != PARSE_INCOMPLETE ) ) { + return HTTP_BAD_REQUEST; + } + } else if( num_read == 0 ) { + if( ok_on_close ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, HTTP, __FILE__, __LINE__, + "<<< (RECVD) <<<\n%s\n-----------------\n", + parser->msg.msg.buf ); + //print_http_headers( &parser->msg ); + ) + + parser->position = POS_COMPLETE; + } else { + // partial msg + parser->http_error_code = HTTP_BAD_REQUEST; // or response + return HTTP_BAD_REQUEST; + } + } else { + return num_read; + } + } + + if( ( entity_offset + Data_Buf_Size ) > parser->msg.entity.length ) { + Data_Buf_Size = parser->msg.entity.length - entity_offset; + } + + memcpy( Buf, &parser->msg.msg.buf[parser->entity_start_position + + entity_offset], + Data_Buf_Size ); + entity_offset += Data_Buf_Size; + + if( Instr->IsVirtualFile ) { + Num_Write = virtualDirCallback.write( Fp, Buf, Data_Buf_Size ); + if( Num_Write < 0 ) { + virtualDirCallback.close( Fp ); + return HTTP_INTERNAL_SERVER_ERROR; + } + } else { + Num_Write = fwrite( Buf, 1, Data_Buf_Size, Fp ); + if( Num_Write < 0 ) { + fclose( Fp ); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + } while( ( parser->position != POS_COMPLETE ) + || ( entity_offset != parser->msg.entity.length ) ); + + if( Instr->IsVirtualFile ) { + virtualDirCallback.close( Fp ); + } else { + fclose( Fp ); + } + + /* + while(TotalByteReceived < Instr->RecvWriteSize && + (NumReceived = sock_read(info,Buf, Data_Buf_Size,&Timeout) ) > 0 ) + { + TotalByteReceived = TotalByteReceived + NumReceived; + Num_Write = virtualDirCallback.write(Fp, Buf, NumReceived); + if (ferror(Fp)) + { + virtualDirCallback.close(Fp); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + if(TotalByteReceived < Instr->RecvWriteSize) + { + return HTTP_INTERNAL_SERVER_ERROR; + } + + virtualDirCallback.close(Fp); + } + */ + return HTTP_OK; +} + +/************************************************************************ +* Function: web_server_callback +* +* Parameters: +* IN http_parser_t *parser ; HTTP Parser Object +* INOUT http_message_t* req ; HTTP Message request +* IN SOCKINFO *info ; Socket information object +* +* Description: main entry point into web server; +* handles HTTP GET and HEAD requests +* +* Returns: +* void +************************************************************************/ +void +web_server_callback( IN http_parser_t * parser, + INOUT http_message_t * req, + IN SOCKINFO * info ) +{ + int ret; + int timeout = 0; + enum resp_type rtype = 0; + membuffer headers; + membuffer filename; + struct xml_alias_t xmldoc; + struct SendInstruction RespInstr; + + //Initialize instruction header. + RespInstr.IsVirtualFile = 0; + RespInstr.IsChunkActive = 0; + RespInstr.IsRangeActive = 0; + RespInstr.IsTrailers = 0; + // init + membuffer_init( &headers ); + membuffer_init( &filename ); + + //Process request should create the different kind of header depending on the + //the type of request. + ret = + process_request( req, &rtype, &headers, &filename, &xmldoc, + &RespInstr ); + if( ret != UPNP_E_SUCCESS ) { + // send error code + http_SendStatusResponse( info, ret, req->major_version, + req->minor_version ); + } else { + // + // send response + switch ( rtype ) { + case RESP_FILEDOC: // send file, I = further instruction to send data. + http_SendMessage( info, &timeout, "Ibf", &RespInstr, + headers.buf, headers.length, + filename.buf ); + break; + + case RESP_XMLDOC: // send xmldoc , I = further instruction to send data. + http_SendMessage( info, &timeout, "Ibb", &RespInstr, + headers.buf, headers.length, + xmldoc.doc.buf, xmldoc.doc.length ); + alias_release( &xmldoc ); + break; + + case RESP_WEBDOC: //, I = further instruction to send data. + /* + http_SendVirtualDirDoc( info, &timeout, "Ibf",&RespInstr, + headers.buf, headers.length, + filename.buf ); + */ + http_SendMessage( info, &timeout, "Ibf", &RespInstr, + headers.buf, headers.length, + filename.buf ); + break; + + case RESP_HEADERS: // headers only + http_SendMessage( info, &timeout, "b", + headers.buf, headers.length ); + + break; + case RESP_POST: // headers only + ret = + http_RecvPostMessage( parser, info, filename.buf, + &RespInstr ); + //Send response. + +/* - PATCH START - Sergey 'Jin' Bostandzhyan + * added X-User-Agent header + */ + http_MakeMessage( &headers, 1, 1, "RTDSXcCc", ret, + "text/html", X_USER_AGENT ); +/* - PATCH END --- */ + + http_SendMessage( info, &timeout, "b", headers.buf, + headers.length ); + break; + + default: + assert( 0 ); + } + } + + DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, + "webserver: request processed...\n" ); + ) + + membuffer_destroy( &headers ); + membuffer_destroy( &filename ); +} diff --git a/libupnp/upnp/src/genlib/net/sock.c b/libupnp/upnp/src/genlib/net/sock.c new file mode 100644 index 0000000..cddfa57 --- /dev/null +++ b/libupnp/upnp/src/genlib/net/sock.c @@ -0,0 +1,309 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file implements the sockets functionality +************************************************************************/ + +#include "config.h" +#include +#include +#include +#include + +#include "sock.h" +#include "upnp.h" +#include +#include +#include +#include +#include +#include +#include "unixutil.h" + +/************************************************************************ +* Function : sock_init +* +* Parameters : +* OUT SOCKINFO* info ; Socket Information Object +* IN int sockfd ; Socket Descriptor +* +* Description : Assign the passed in socket descriptor to socket +* descriptor in the SOCKINFO structure. +* +* Return : int; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* UPNP_E_SOCKET_ERROR +* +* Note : +************************************************************************/ +int +sock_init( OUT SOCKINFO * info, + IN int sockfd ) +{ + assert( info ); + + memset( info, 0, sizeof( SOCKINFO ) ); + + info->socket = sockfd; + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : sock_init_with_ip +* +* Parameters : +* OUT SOCKINFO* info ; Socket Information Object +* IN int sockfd ; Socket Descriptor +* IN struct in_addr foreign_ip_addr ; Remote IP Address +* IN unsigned short foreign_ip_port ; Remote Port number +* +* Description : Calls the sock_init function and assigns the passed in +* IP address and port to the IP address and port in the SOCKINFO +* structure. +* +* Return : int; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* UPNP_E_SOCKET_ERROR +* +* Note : +************************************************************************/ +int +sock_init_with_ip( OUT SOCKINFO * info, + IN int sockfd, + IN struct in_addr foreign_ip_addr, + IN unsigned short foreign_ip_port ) +{ + int ret; + + ret = sock_init( info, sockfd ); + if( ret != UPNP_E_SUCCESS ) { + return ret; + } + + info->foreign_ip_addr = foreign_ip_addr; + info->foreign_ip_port = foreign_ip_port; + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : sock_destroy +* +* Parameters : +* INOUT SOCKINFO* info ; Socket Information Object +* int ShutdownMethod ; How to shutdown the socket. Used by +* sockets's shutdown() +* +* Description : Shutsdown the socket using the ShutdownMethod to +* indicate whether sends and receives on the socket will be +* dis-allowed. After shutting down the socket, closesocket is called +* to release system resources used by the socket calls. +* +* Return : int; +* UPNP_E_SOCKET_ERROR on failure +* UPNP_E_SUCCESS on success +* +* Note : +************************************************************************/ +int +sock_destroy( INOUT SOCKINFO * info, + int ShutdownMethod ) +{ + shutdown( info->socket, ShutdownMethod ); + if( UpnpCloseSocket( info->socket ) == -1 ) { + return UPNP_E_SOCKET_ERROR; + } + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : sock_read_write +* +* Parameters : +* IN SOCKINFO *info ; Socket Information Object +* OUT char* buffer ; Buffer to get data to or send data from +* IN size_t bufsize ; Size of the buffer +* IN int *timeoutSecs ; timeout value +* IN xboolean bRead ; Boolean value specifying read or write option +* +* Description : Receives or sends data. Also returns the time taken +* to receive or send data. +* +* Return :int ; +* numBytes - On Success, no of bytes received or sent +* UPNP_E_TIMEDOUT - Timeout +* UPNP_E_SOCKET_ERROR - Error on socket calls +* +* Note : +************************************************************************/ +static int +sock_read_write( IN SOCKINFO * info, + OUT char *buffer, + IN size_t bufsize, + IN int *timeoutSecs, + IN xboolean bRead ) +{ + int retCode; + fd_set readSet; + fd_set writeSet; + struct timeval timeout; + int numBytes; + time_t start_time = time( NULL ); + int sockfd = info->socket; + long bytes_sent = 0, + byte_left = 0, + num_written; + + if( *timeoutSecs < 0 ) { + return UPNP_E_TIMEDOUT; + } + + FD_ZERO( &readSet ); + FD_ZERO( &writeSet ); + if( bRead ) { + FD_SET( ( unsigned )sockfd, &readSet ); + } else { + FD_SET( ( unsigned )sockfd, &writeSet ); + } + + timeout.tv_sec = *timeoutSecs; + timeout.tv_usec = 0; + + while( TRUE ) { + if( *timeoutSecs == 0 ) { + retCode = + select( sockfd + 1, &readSet, &writeSet, NULL, NULL ); + } else { + retCode = + select( sockfd + 1, &readSet, &writeSet, NULL, &timeout ); + } + + if( retCode == 0 ) { + return UPNP_E_TIMEDOUT; + } + if( retCode == -1 ) { + if( errno == EINTR ) + continue; + return UPNP_E_SOCKET_ERROR; // error + } else { + break; // read or write + } + } + + if( bRead ) { + // read data + numBytes = recv( sockfd, buffer, bufsize,0); // MSG_NOSIGNAL is not a good idea in portable code, here SIGPIPE/SIGEPIPE has to be used instead + } else { + byte_left = bufsize; + bytes_sent = 0; + while( byte_left > 0 ) { + // write data + num_written = + send( sockfd, buffer + bytes_sent, byte_left, + MSG_DONTROUTE); // | MSG_NOSIGNAL is not a good idea in portable code, here SIGPIPE/SIGEPIPE has to be used instead + if( num_written == -1 ) { + return num_written; + } + + byte_left = byte_left - num_written; + bytes_sent += num_written; + } + + numBytes = bytes_sent; + } + + if( numBytes < 0 ) { + return UPNP_E_SOCKET_ERROR; + } + // subtract time used for reading/writing + if( *timeoutSecs != 0 ) { + *timeoutSecs -= time( NULL ) - start_time; + } + + return numBytes; +} + +/************************************************************************ +* Function : sock_read +* +* Parameters : +* IN SOCKINFO *info ; Socket Information Object +* OUT char* buffer ; Buffer to get data to +* IN size_t bufsize ; Size of the buffer +* IN int *timeoutSecs ; timeout value +* +* Description : Calls sock_read_write() for reading data on the +* socket +* +* Return : int; +* Values returned by sock_read_write() +* +* Note : +************************************************************************/ +int +sock_read( IN SOCKINFO * info, + OUT char *buffer, + IN size_t bufsize, + INOUT int *timeoutSecs ) +{ + return sock_read_write( info, buffer, bufsize, timeoutSecs, TRUE ); +} + +/************************************************************************ +* Function : sock_write +* +* Parameters : +* IN SOCKINFO *info ; Socket Information Object +* IN char* buffer ; Buffer to send data from +* IN size_t bufsize ; Size of the buffer +* IN int *timeoutSecs ; timeout value +* +* Description : Calls sock_read_write() for writing data on the +* socket +* +* Return : int; +* sock_read_write() +* +* Note : +************************************************************************/ +int +sock_write( IN SOCKINFO * info, + IN char *buffer, + IN size_t bufsize, + INOUT int *timeoutSecs ) +{ + return sock_read_write( info, buffer, bufsize, timeoutSecs, FALSE ); +} diff --git a/libupnp/upnp/src/genlib/net/uri/uri.c b/libupnp/upnp/src/genlib/net/uri/uri.c new file mode 100644 index 0000000..dfd3d09 --- /dev/null +++ b/libupnp/upnp/src/genlib/net/uri/uri.c @@ -0,0 +1,1061 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file contains functions for uri, url parsing utility. +************************************************************************/ + +#include "config.h" +#include "uri.h" + +/************************************************************************ +* Function : is_reserved +* +* Parameters : +* char in ; char to be matched for RESERVED characters +* +* Description : Returns a 1 if a char is a RESERVED char as defined in +* http://www.ietf.org/rfc/rfc2396.txt RFC explaining URIs) +* +* Return : int ; +* +* Note : +************************************************************************/ +int +is_reserved( char in ) +{ + if( strchr( RESERVED, in ) ) + return 1; + else + return 0; +} + +/************************************************************************ +* Function : is_mark +* +* Parameters : +* char in ; character to be matched for MARKED characters +* +* Description : Returns a 1 if a char is a MARK char as defined in +* http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) +* +* Return : int ; +* +* Note : +************************************************************************/ +int +is_mark( char in ) +{ + if( strchr( MARK, in ) ) + return 1; + else + return 0; +} + +/************************************************************************ +* Function : is_unreserved +* +* Parameters : +* char in ; character to be matched for UNRESERVED characters +* +* Description : Returns a 1 if a char is an unreserved char as defined in +* http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) +* +* Return : int ; +* +* Note : +************************************************************************/ +int +is_unreserved( char in ) +{ + if( isalnum( in ) || ( is_mark( in ) ) ) + return 1; + else + return 0; +} + +/************************************************************************ +* Function : is_escaped +* +* Parameters : +* char * in ; character to be matched for ESCAPED characters +* +* Description : Returns a 1 if a char[3] sequence is escaped as defined +* in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) +* size of array is NOT checked (MUST be checked by caller) +* +* Return : int ; +* +* Note : +************************************************************************/ +int +is_escaped( char *in ) +{ + + if( ( in[0] == '%' ) && ( isxdigit( in[1] ) ) && isxdigit( in[2] ) ) { + + return 1; + } else + return 0; +} + +/************************************************************************ +* Function : replace_escaped +* +* Parameters : +* char * in ; string of characters +* int index ; index at which to start checking the characters +* int *max ; +* +* Description : Replaces an escaped sequence with its unescaped version +* as in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) +* Size of array is NOT checked (MUST be checked by caller) +* +* Return : int ; +* +* Note : This function modifies the string. If the sequence is an +* escaped sequence it is replaced, the other characters in the +* string are shifted over, and NULL characters are placed at the +* end of the string. +************************************************************************/ +int +replace_escaped( char *in, + int index, + int *max ) +{ + int tempInt = 0; + char tempChar = 0; + int i = 0; + int j = 0; + + if( ( in[index] == '%' ) && ( isxdigit( in[index + 1] ) ) + && isxdigit( in[index + 2] ) ) { + //Note the "%2x", makes sure that we convert a maximum of two + //characters. + if( sscanf( &in[index + 1], "%2x", &tempInt ) != 1 ) + return 0; + + tempChar = ( char )tempInt; + + for( i = index + 3, j = index; j < ( *max ); i++, j++ ) { + in[j] = tempChar; + if( i < ( *max ) ) + tempChar = in[i]; + else + tempChar = 0; + } + ( *max ) -= 2; + return 1; + } else + return 0; +} + +/************************************************************************ +* Function : parse_uric +* +* Parameters : +* char *in ; string of characters +* int max ; maximum limit +* token *out ; token object where the string of characters is +* copied +* +* Description : Parses a string of uric characters starting at in[0] +* as defined in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining +* URIs) +* +* Return : int ; +* +* Note : +************************************************************************/ +int +parse_uric( char *in, + int max, + token * out ) +{ + int i = 0; + + while( ( i < max ) + && ( ( is_unreserved( in[i] ) ) || ( is_reserved( in[i] ) ) + || ( ( i + 2 < max ) && ( is_escaped( &in[i] ) ) ) ) ) { + i++; + } + + out->size = i; + out->buff = in; + return i; +} + +/************************************************************************ +* Function : copy_sockaddr_in +* +* Parameters : +* const struct sockaddr_in *in ; Source socket address object +* struct sockaddr_in *out ; Destination socket address object +* +* Description : Copies one socket address into another +* +* Return : void ; +* +* Note : +************************************************************************/ +void +copy_sockaddr_in( const struct sockaddr_in *in, + struct sockaddr_in *out ) +{ + memset( out->sin_zero, 0, 8 ); + out->sin_family = in->sin_family; + out->sin_port = in->sin_port; + out->sin_addr.s_addr = in->sin_addr.s_addr; +} + +/************************************************************************ +* Function : copy_token +* +* Parameters : +* const token *in ; source token +* const char * in_base ; +* token * out ; destination token +* char * out_base ; +* +* Description : Tokens are generally pointers into other strings +* this copies the offset and size from a token (in) relative to +* one string (in_base) into a token (out) relative to another +* string (out_base) +* +* Return : void ; +* +* Note : +************************************************************************/ +static void +copy_token( const token * in, + const char *in_base, + token * out, + char *out_base ) +{ + out->size = in->size; + out->buff = out_base + ( in->buff - in_base ); +} + +/************************************************************************ +* Function : copy_URL_list +* +* Parameters : +* URL_list *in ; Source URL list +* URL_list *out ; Destination URL list +* +* Description : Copies one URL_list into another. This includes +* dynamically allocating the out->URLs field (the full string), +* and the structures used to hold the parsedURLs. This memory MUST +* be freed by the caller through: free_URL_list(&out) +* +* Return : int ; +* HTTP_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - On Failure to allocate memory +* +* Note : +************************************************************************/ +int +copy_URL_list( URL_list * in, + URL_list * out ) +{ + int len = strlen( in->URLs ) + 1; + int i = 0; + + out->URLs = NULL; + out->parsedURLs = NULL; + out->size = 0; + + out->URLs = ( char * )malloc( len ); + out->parsedURLs = + ( uri_type * ) malloc( sizeof( uri_type ) * in->size ); + + if( ( out->URLs == NULL ) || ( out->parsedURLs == NULL ) ) + return UPNP_E_OUTOF_MEMORY; + + memcpy( out->URLs, in->URLs, len ); + + for( i = 0; i < in->size; i++ ) { + //copy the parsed uri + out->parsedURLs[i].type = in->parsedURLs[i].type; + copy_token( &in->parsedURLs[i].scheme, in->URLs, + &out->parsedURLs[i].scheme, out->URLs ); + + out->parsedURLs[i].path_type = in->parsedURLs[i].path_type; + copy_token( &in->parsedURLs[i].pathquery, in->URLs, + &out->parsedURLs[i].pathquery, out->URLs ); + copy_token( &in->parsedURLs[i].fragment, in->URLs, + &out->parsedURLs[i].fragment, out->URLs ); + copy_token( &in->parsedURLs[i].hostport.text, + in->URLs, &out->parsedURLs[i].hostport.text, + out->URLs ); + + copy_sockaddr_in( &in->parsedURLs[i].hostport.IPv4address, + &out->parsedURLs[i].hostport.IPv4address ); + } + out->size = in->size; + return HTTP_SUCCESS; + +} + +/************************************************************************ +* Function : free_URL_list +* +* Parameters : +* URL_list * list ; URL List object +* +* Description : Frees the memory associated with a URL_list. Frees the +* dynamically allocated members of of list. Does NOT free the +* pointer to the list itself ( i.e. does NOT free(list)) +* +* Return : void ; +* +* Note : +************************************************************************/ +void +free_URL_list( URL_list * list ) +{ + if( list->URLs ) + free( list->URLs ); + if( list->parsedURLs ) + free( list->parsedURLs ); + list->size = 0; +} + +/************************************************************************ +* Function : print_uri +* +* Parameters : +* uri_type *in ; URI object +* +* Description : Function useful in debugging for printing a parsed uri. +* Compiled out with DBGONLY macro. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY( void print_uri( uri_type * in ) { + print_token( &in->scheme ); + print_token( &in->hostport.text ); + print_token( &in->pathquery ); print_token( &in->fragment );} ) + +/************************************************************************ +* Function : print_token +* +* Parameters : +* token * in ; token +* +* Description : Function useful in debugging for printing a token. +* Compiled out with DBGONLY macro. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY( void print_token( token * in ) { + int i = 0; + printf( "Token Size : %d\n\'", in->size ); + for( i = 0; i < in->size; i++ ) { + putchar( in->buff[i] );} + putchar( '\'' ); putchar( '\n' );} + + ) + +/************************************************************************ +* Function : token_string_casecmp +* +* Parameters : +* token * in1 ; Token object whose buffer is to be compared +* char * in2 ; string of characters to compare with +* +* Description : Compares buffer in the token object with the buffer +* in in2 +* +* Return : int ; +* < 0 string1 less than string2 +* 0 string1 identical to string2 +* > 0 string1 greater than string2 +* +* Note : +************************************************************************/ + int token_string_casecmp( token * in1, + char *in2 ) { + int in2_length = strlen( in2 ); + + if( in1->size != in2_length ) + return 1; + else + return strncasecmp( in1->buff, in2, in1->size ); +} + +/************************************************************************ +* Function : token_string_cmp +* +* Parameters : +* token * in1 ; Token object whose buffer is to be compared +* char * in2 ; string of characters to compare with +* +* Description : Compares a null terminated string to a token (exact) +* +* Return : int ; +* < 0 string1 less than string2 +* 0 string1 identical to string2 +* > 0 string1 greater than string2 +* +* Note : +************************************************************************/ +int +token_string_cmp( token * in1, + char *in2 ) +{ + int in2_length = strlen( in2 ); + + if( in1->size != in2_length ) + return 1; + else + return strncmp( in1->buff, in2, in1->size ); +} + +/************************************************************************ +* Function : token_cmp +* +* Parameters : +* token *in1 ; First token object whose buffer is to be compared +* token *in2 ; Second token object used for the comparison +* +* Description : Compares two tokens +* +* Return : int ; +* < 0 string1 less than string2 +* 0 string1 identical to string2 +* > 0 string1 greater than string2 +* +* Note : +************************************************************************/ +int +token_cmp( token * in1, + token * in2 ) +{ + if( in1->size != in2->size ) + return 1; + else + return memcmp( in1->buff, in2->buff, in1->size ); +} + +/************************************************************************ +* Function : parse_port +* +* Parameters : +* int max ; sets a maximum limit +* char * port ; port to be parsed. +* unsigned short * out ; out parameter where the port is parsed +* and converted into network format +* +* Description : parses a port (i.e. '4000') and converts it into a +* network ordered unsigned short int. +* +* Return : int ; +* +* Note : +************************************************************************/ +int +parse_port( int max, + char *port, + unsigned short *out ) +{ + + char *finger = port; + char *max_ptr = finger + max; + unsigned short temp = 0; + + while( ( finger < max_ptr ) && ( isdigit( *finger ) ) ) { + temp = temp * 10; + temp += ( *finger ) - '0'; + finger++; + } + + *out = htons( temp ); + return finger - port; +} + +/************************************************************************ +* Function : parse_hostport +* +* Parameters : +* char *in ; string of characters representing host and port +* int max ; sets a maximum limit +* hostport_type *out ; out parameter where the host and port +* are represented as an internet address +* +* Description : Parses a string representing a host and port +* (e.g. "127.127.0.1:80" or "localhost") and fills out a +* hostport_type struct with internet address and a token +* representing the full host and port. uses gethostbyname. +* +* Return : int ; +* +* Note : +************************************************************************/ +int +parse_hostport( char *in, + int max, + hostport_type * out ) +{ +#define BUFFER_SIZE 8192 + + int i = 0; + int begin_port; + int hostport_size = 0; + int host_size = 0; + struct hostent h_buf; + char temp_hostbyname_buff[BUFFER_SIZE]; + struct hostent *h = NULL; + int errcode = 0; + char *temp_host_name = NULL; + int last_dot = -1; + + out->text.size = 0; + out->text.buff = NULL; + + out->IPv4address.sin_port = htons( 80 ); //default port is 80 + memset( &out->IPv4address.sin_zero, 0, 8 ); + + while( ( i < max ) && ( in[i] != ':' ) && ( in[i] != '/' ) + && ( ( isalnum( in[i] ) ) || ( in[i] == '.' ) + || ( in[i] == '-' ) ) ) { + i++; + if( in[i] == '.' ) { + last_dot = i; + } + } + + host_size = i; + + if( ( i < max ) && ( in[i] == ':' ) ) { + begin_port = i + 1; + //convert port + if( !( hostport_size = parse_port( max - begin_port, + &in[begin_port], + &out->IPv4address.sin_port ) ) ) + { + return UPNP_E_INVALID_URL; + } + hostport_size += begin_port; + } else + hostport_size = host_size; + + //convert to temporary null terminated string + temp_host_name = ( char * )malloc( host_size + 1 ); + + if( temp_host_name == NULL ) + return UPNP_E_OUTOF_MEMORY; + + memcpy( temp_host_name, in, host_size ); + temp_host_name[host_size] = '\0'; + + //check to see if host name is an ipv4 address + if( ( last_dot != -1 ) && ( last_dot + 1 < host_size ) + && ( isdigit( temp_host_name[last_dot + 1] ) ) ) { + //must be ipv4 address + + errcode = inet_pton( AF_INET, + temp_host_name, &out->IPv4address.sin_addr ); + if( errcode == 1 ) { + out->IPv4address.sin_family = AF_INET; + } else { + out->IPv4address.sin_addr.s_addr = 0; + out->IPv4address.sin_family = AF_INET; + free( temp_host_name ); + temp_host_name = NULL; + return UPNP_E_INVALID_URL; + } + } else { + int errCode = 0; + + //call gethostbyname_r (reentrant form of gethostbyname) + #ifndef SPARC_SOLARIS + errCode = gethostbyname_r( temp_host_name, + &h_buf, + temp_hostbyname_buff, + BUFFER_SIZE, &h, &errcode ); + #else + errCode = gethostbyname_r( temp_host_name, + &h, + temp_hostbyname_buff, + BUFFER_SIZE, &errcode ); + #endif + + if( errCode == 0 ) { + if( h ) { + if( ( h->h_addrtype == AF_INET ) && ( h->h_length == 4 ) ) { + out->IPv4address.sin_addr = + ( *( struct in_addr * )h->h_addr ); + out->IPv4address.sin_family = AF_INET; + + } + } + } else { + out->IPv4address.sin_addr.s_addr = 0; + out->IPv4address.sin_family = AF_INET; + free( temp_host_name ); + temp_host_name = NULL; + return UPNP_E_INVALID_URL; + } + } + + if( temp_host_name ) { + free( temp_host_name ); + temp_host_name = NULL; + } + + out->text.size = hostport_size; + out->text.buff = in; + return hostport_size; + +} + +/************************************************************************ +* Function : parse_scheme +* +* Parameters : +* char * in ; string of characters representing a scheme +* int max ; maximum number of characters +* token * out ; output parameter whose buffer is filled in with +* the scheme +* +* Description : parses a uri scheme starting at in[0] as defined in +* http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) +* (e.g. "http:" -> scheme= "http"). +* Note, string MUST include ':' within the max charcters +* +* Return : int ; +* +* Note : +************************************************************************/ +int +parse_scheme( char *in, + int max, + token * out ) +{ + int i = 0; + + out->size = 0; + out->buff = NULL; + + if( ( max == 0 ) || ( !isalpha( in[0] ) ) ) + return FALSE; + + i++; + while( ( i < max ) && ( in[i] != ':' ) ) { + + if( !( isalnum( in[i] ) || ( in[i] == '+' ) || ( in[i] == '-' ) + || ( in[i] == '.' ) ) ) + return FALSE; + + i++; + } + if( i < max ) { + out->size = i; + out->buff = &in[0]; + return i; + } + + return FALSE; + +} + +/************************************************************************ +* Function : remove_escaped_chars +* +* Parameters : +* INOUT char *in ; string of characters to be modified +* INOUT int *size ; size limit for the number of characters +* +* Description : removes http escaped characters such as: "%20" and +* replaces them with their character representation. i.e. +* "hello%20foo" -> "hello foo". The input IS MODIFIED in place. +* (shortened). Extra characters are replaced with NULL. +* +* Return : int ; +* UPNP_E_SUCCESS +* +* Note : +************************************************************************/ +int +remove_escaped_chars( INOUT char *in, + INOUT int *size ) +{ + int i = 0; + + for( i = 0; i < *size; i++ ) { + replace_escaped( in, i, size ); + } + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : remove_dots +* +* Parameters : +* char *in ; string of characters from which "dots" have to be +* removed +* int size ; size limit for the number of characters +* +* Description : Removes ".", and ".." from a path. If a ".." can not +* be resolved (i.e. the .. would go past the root of the path) an +* error is returned. The input IS modified in place.) +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - On failure to allocate memory +* UPNP_E_INVALID_URL - Failure to resolve URL +* +* Note : +* Examples +* char path[30]="/../hello"; +* remove_dots(path, strlen(path)) -> UPNP_E_INVALID_URL +* char path[30]="/./hello"; +* remove_dots(path, strlen(path)) -> UPNP_E_SUCCESS, +* in = "/hello" +* char path[30]="/./hello/foo/../goodbye" -> +* UPNP_E_SUCCESS, in = "/hello/goodbye" + +************************************************************************/ +int +remove_dots( char *in, + int size ) +{ + char *copyTo = in; + char *copyFrom = in; + char *max = in + size; + char **Segments = NULL; + int lastSegment = -1; + + Segments = malloc( sizeof( char * ) * size ); + + if( Segments == NULL ) + return UPNP_E_OUTOF_MEMORY; + + Segments[0] = NULL; + DBGONLY( 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; + } + } + + if( ( *copyFrom ) == '/' ) { + + lastSegment++; + Segments[lastSegment] = copyTo + 1; + } + ( *copyTo ) = ( *copyFrom ); + copyTo++; + copyFrom++; + } + if( copyFrom < max ) { + while( copyFrom < max ) { + ( *copyTo ) = ( *copyFrom ); + copyTo++; + copyFrom++; + } + } + ( *copyTo ) = 0; + free( Segments ); + DBGONLY( UpnpPrintf + ( UPNP_ALL, API, __FILE__, __LINE__, + "REMOVE_DOTS: after: %s\n", in ) ); + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : resolve_rel_url +* +* Parameters : +* char * base_url ; Base URL +* char * rel_url ; Relative URL +* +* Description : resolves a relative url with a base url returning a NEW +* (dynamically allocated with malloc) full url. If the base_url is +* NULL, then a copy of the rel_url is passed back if the rel_url +* is absolute then a copy of the rel_url is passed back if neither +* the base nor the rel_url are Absolute then NULL is returned. +* otherwise it tries and resolves the relative url with the base +* as described in: http://www.ietf.org/rfc/rfc2396.txt (RFCs +* explaining URIs) +* : resolution of '..' is NOT implemented, but '.' is resolved +* +* Return : char * ; +* +* Note : +************************************************************************/ +char * +resolve_rel_url( char *base_url, + char *rel_url ) +{ + uri_type base; + uri_type rel; + char temp_path = '/'; + + int i = 0; + char *finger = NULL; + + char *last_slash = NULL; + + char *out = NULL; + char *out_finger = NULL; + + if( base_url && rel_url ) { + out = + ( char * )malloc( strlen( base_url ) + strlen( rel_url ) + 2 ); + out_finger = out; + } else { + if( rel_url ) + return strdup( rel_url ); + else + return NULL; + } + + if( out == NULL ) { + return NULL; + } + + if( ( parse_uri( rel_url, strlen( rel_url ), &rel ) ) == HTTP_SUCCESS ) { + + if( rel.type == ABSOLUTE ) { + + strcpy( out, rel_url ); + } else { + + if( ( parse_uri( base_url, strlen( base_url ), &base ) == + HTTP_SUCCESS ) + && ( base.type == ABSOLUTE ) ) { + + if( strlen( rel_url ) == 0 ) { + strcpy( out, base_url ); + } else { + memcpy( out, base.scheme.buff, base.scheme.size ); + out_finger += base.scheme.size; + ( *out_finger ) = ':'; + out_finger++; + + if( rel.hostport.text.size > 0 ) { + sprintf( out_finger, "%s", rel_url ); + } else { + if( base.hostport.text.size > 0 ) { + memcpy( out_finger, "//", 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 == ABS_PATH ) { + strcpy( out_finger, rel_url ); + + } else { + + if( base.pathquery.size == 0 ) { + base.pathquery.size = 1; + base.pathquery.buff = &temp_path; + } + + finger = out_finger; + last_slash = finger; + i = 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++; + + } + i = 0; + strcpy( last_slash, rel_url ); + if( remove_dots( out_finger, + strlen( out_finger ) ) != + UPNP_E_SUCCESS ) { + free( out ); + //free(rel_url); + return NULL; + } + } + + } + } + } else { + free( out ); + //free(rel_url); + return NULL; + } + } + } else { + free( out ); + //free(rel_url); + return NULL; + } + + //free(rel_url); + return out; +} + +/************************************************************************ +* Function : parse_uri +* +* Parameters : +* char * in ; character string containing uri information to be +* parsed +* int max ; maximum limit on the number of characters +* uri_type * out ; out parameter which will have the parsed uri +* information +* +* Description : parses a uri as defined in http://www.ietf.org/rfc/ +* rfc2396.txt (RFC explaining URIs) +* Handles absolute, relative, and opaque uris. Parses into the +* following pieces: scheme, hostport, pathquery, fragment (path and +* query are treated as one token) +* Caller should check for the pieces they require. +* +* Return : int ; +* +* Note : +************************************************************************/ +int +parse_uri( char *in, + int max, + uri_type * out ) +{ + int begin_path = 0; + int begin_hostport = 0; + int begin_fragment = 0; + + if( ( begin_hostport = parse_scheme( in, max, &out->scheme ) ) ) { + out->type = ABSOLUTE; + out->path_type = OPAQUE_PART; + begin_hostport++; + } else { + out->type = RELATIVE; + out->path_type = REL_PATH; + } + + if( ( ( begin_hostport + 1 ) < max ) && ( in[begin_hostport] == '/' ) + && ( in[begin_hostport + 1] == '/' ) ) { + begin_hostport += 2; + + if( ( begin_path = parse_hostport( &in[begin_hostport], + max - begin_hostport, + &out->hostport ) ) >= 0 ) { + begin_path += begin_hostport; + } else + return begin_path; + + } else { + out->hostport.IPv4address.sin_port = 0; + out->hostport.IPv4address.sin_addr.s_addr = 0; + out->hostport.text.size = 0; + out->hostport.text.buff = 0; + begin_path = begin_hostport; + } + + begin_fragment = + parse_uric( &in[begin_path], max - begin_path, + &out->pathquery ) + begin_path; + + if( ( out->pathquery.size ) && ( out->pathquery.buff[0] == '/' ) ) { + out->path_type = ABS_PATH; + } + + if( ( begin_fragment < max ) && ( in[begin_fragment] == '#' ) ) { + begin_fragment++; + parse_uric( &in[begin_fragment], max - begin_fragment, + &out->fragment ); + } else { + out->fragment.buff = NULL; + out->fragment.size = 0; + } + return HTTP_SUCCESS; +} + +/************************************************************************ +* Function : parse_uri_and_unescape +* +* Parameters : +* char * in ; +* int max ; +* uri_type * out ; +* +* Description : Same as parse_uri, except that all strings are +* unescaped (%XX replaced by chars) +* +* Return : int ; +* +* Note: This modifies 'pathquery' and 'fragment' parts of the input +************************************************************************/ +int +parse_uri_and_unescape( char *in, + int max, + uri_type * out ) +{ + int ret; + + if( ( ret = parse_uri( in, max, out ) ) != HTTP_SUCCESS ) + return ret; + if( out->pathquery.size > 0 ) + remove_escaped_chars( out->pathquery.buff, &out->pathquery.size ); + if( out->fragment.size > 0 ) + remove_escaped_chars( out->fragment.buff, &out->fragment.size ); + return HTTP_SUCCESS; +} diff --git a/libupnp/upnp/src/genlib/service_table/service_table.c b/libupnp/upnp/src/genlib/service_table/service_table.c new file mode 100644 index 0000000..a635cfb --- /dev/null +++ b/libupnp/upnp/src/genlib/service_table/service_table.c @@ -0,0 +1,1140 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file defines the functions for services. It defines +* functions for adding and removing services to and from the service table, +* adding and accessing subscription and other attributes pertaining to the +* service +************************************************************************/ + +#include "config.h" +#include "service_table.h" + +#ifdef INCLUDE_DEVICE_APIS + +/************************************************************************ +* Function : copy_subscription +* +* Parameters : +* subscription *in ; Source subscription +* subscription *out ; Destination subscription +* +* Description : Makes a copy of the subscription +* +* Return : int ; +* HTTP_SUCCESS - On Sucess +* +* Note : +************************************************************************/ +int +copy_subscription( subscription * in, + subscription * out ) +{ + int return_code = HTTP_SUCCESS; + + memcpy( out->sid, in->sid, SID_SIZE ); + out->sid[SID_SIZE] = 0; + out->eventKey = in->eventKey; + out->ToSendEventKey = in->ToSendEventKey; + out->expireTime = in->expireTime; + out->active = in->active; + if( ( return_code = + copy_URL_list( &in->DeliveryURLs, &out->DeliveryURLs ) ) + != HTTP_SUCCESS ) + return return_code; + out->next = NULL; + return HTTP_SUCCESS; +} + +/************************************************************************ +* Function : RemoveSubscriptionSID +* +* Parameters : +* Upnp_SID sid ; subscription ID +* service_info * service ; service object providing the list of +* subscriptions +* +* Description : Remove the subscription represented by the +* const Upnp_SID sid parameter from the service table and update +* the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void +RemoveSubscriptionSID( Upnp_SID sid, + service_info * service ) +{ + subscription *finger = service->subscriptionList; + subscription *previous = NULL; + + while( finger ) { + if( !( strcmp( sid, finger->sid ) ) ) { + if( previous ) + previous->next = finger->next; + else + service->subscriptionList = finger->next; + finger->next = NULL; + freeSubscriptionList( finger ); + finger = NULL; + service->TotalSubscriptions--; + } else { + previous = finger; + finger = finger->next; + } + } + +} + +/************************************************************************ +* Function : GetSubscriptionSID +* +* Parameters : +* Upnp_SID sid ; subscription ID +* service_info * service ; service object providing the list of +* subscriptions +* +* Description : Return the subscription from the service table +* that matches const Upnp_SID sid value. +* +* Return : subscription * - Pointer to the matching subscription +* node; +* +* Note : +************************************************************************/ +subscription * +GetSubscriptionSID( Upnp_SID sid, + service_info * service ) +{ + subscription *next = service->subscriptionList; + subscription *previous = NULL; + subscription *found = NULL; + + time_t current_time; + + while( ( next ) && ( found == NULL ) ) { + if( !strcmp( next->sid, sid ) ) + found = next; + else { + previous = next; + next = next->next; + } + } + if( found ) { + //get the current_time + time( ¤t_time ); + if( ( found->expireTime != 0 ) + && ( found->expireTime < current_time ) ) { + if( previous ) + previous->next = found->next; + else + service->subscriptionList = found->next; + found->next = NULL; + freeSubscriptionList( found ); + found = NULL; + service->TotalSubscriptions--; + } + } + return found; + +} + +/************************************************************************ +* Function : GetNextSubscription +* +* Parameters : +* service_info * service ; service object providing the list of +* subscriptions +* subscription *current ; current subscription object +* +* Description : Get current and valid subscription from the service +* table. +* +* Return : subscription * - Pointer to the next subscription node; +* +* Note : +************************************************************************/ +subscription * +GetNextSubscription( service_info * service, + subscription * current ) +{ + time_t current_time; + subscription *next = NULL; + subscription *previous = NULL; + int notDone = 1; + + //get the current_time + time( ¤t_time ); + while( ( notDone ) && ( current ) ) { + previous = current; + current = current->next; + + if( current == NULL ) { + notDone = 0; + next = current; + } else + if( ( current->expireTime != 0 ) + && ( current->expireTime < current_time ) ) { + previous->next = current->next; + current->next = NULL; + freeSubscriptionList( current ); + current = previous; + service->TotalSubscriptions--; + } else if( current->active ) { + notDone = 0; + next = current; + } + } + return next; +} + +/************************************************************************ +* Function : GetFirstSubscription +* +* Parameters : +* service_info *service ; service object providing the list of +* subscriptions +* +* Description : Gets pointer to the first subscription node in the +* service table. +* +* Return : subscription * - pointer to the first subscription node ; +* +* Note : +************************************************************************/ +subscription * +GetFirstSubscription( service_info * service ) +{ + subscription temp; + subscription *next = NULL; + + temp.next = service->subscriptionList; + next = GetNextSubscription( service, &temp ); + service->subscriptionList = temp.next; + // service->subscriptionList=next; + return next; +} + +/************************************************************************ +* Function : freeSubscription +* +* Parameters : +* subscription * sub ; subscription to be freed +* +* Description : Free's the memory allocated for storing the URL of +* the subscription. +* +* Return : void ; +* +* Note : +************************************************************************/ +void +freeSubscription( subscription * sub ) +{ + if( sub ) { + free_URL_list( &sub->DeliveryURLs ); + } +} + +/************************************************************************ +* Function : freeSubscriptionList +* +* Parameters : +* subscription * head ; head of the subscription list +* +* Description : Free's memory allocated for all the subscriptions +* in the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void +freeSubscriptionList( subscription * head ) +{ + subscription *next = NULL; + + while( head ) { + next = head->next; + freeSubscription( head ); + free( head ); + head = next; + } +} + +/************************************************************************ +* Function : FindServiceId +* +* Parameters : +* service_table *table ; service table +* const char * serviceId ;string representing the service id +* to be found among those in the table +* const char * UDN ; string representing the UDN +* to be found among those in the table +* +* Description : Traverses through the service table and returns a +* pointer to the service node that matches a known service id +* and a known UDN +* +* Return : service_info * - pointer to the matching service_info node; +* +* Note : +************************************************************************/ +service_info * +FindServiceId( service_table * table, + const char *serviceId, + const char *UDN ) +{ + service_info *finger = NULL; + + if( table ) { + finger = table->serviceList; + while( finger ) { + if( ( !strcmp( serviceId, finger->serviceId ) ) && + ( !strcmp( UDN, finger->UDN ) ) ) { + return finger; + } + finger = finger->next; + } + } + + return NULL; +} + +/************************************************************************ +* Function : FindServiceEventURLPath +* +* Parameters : +* service_table *table ; service table +* char * eventURLPath ; event URL path used to find a service +* from the table +* +* Description : Traverses the service table and finds the node whose +* event URL Path matches a know value +* +* Return : service_info * - pointer to the service list node from the +* service table whose event URL matches a known event URL; +* +* Note : +************************************************************************/ +service_info * +FindServiceEventURLPath( service_table * table, + char *eventURLPath ) +{ + service_info *finger = NULL; + uri_type parsed_url; + uri_type parsed_url_in; + + if( ( table ) + && + ( parse_uri + ( eventURLPath, strlen( eventURLPath ), &parsed_url_in ) ) ) { + + finger = table->serviceList; + while( finger ) { + if( finger->eventURL ) + if( ( parse_uri + ( finger->eventURL, strlen( finger->eventURL ), + &parsed_url ) ) ) { + + if( !token_cmp + ( &parsed_url.pathquery, + &parsed_url_in.pathquery ) ) + return finger; + + } + finger = finger->next; + } + } + + return NULL; +} + +/************************************************************************ +* Function : FindServiceControlURLPath +* +* Parameters : +* service_table * table ; service table +* char * controlURLPath ; control URL path used to find a service +* from the table +* +* Description : Traverses the service table and finds the node whose +* control URL Path matches a know value +* +* Return : service_info * - pointer to the service list node from the +* service table whose control URL Path matches a known value; +* +* Note : +************************************************************************/ +service_info * +FindServiceControlURLPath( service_table * table, + char *controlURLPath ) +{ + service_info *finger = NULL; + uri_type parsed_url; + uri_type parsed_url_in; + + if( ( table ) + && + ( parse_uri + ( controlURLPath, strlen( controlURLPath ), + &parsed_url_in ) ) ) { + finger = table->serviceList; + while( finger ) { + if( finger->controlURL ) + if( ( parse_uri + ( finger->controlURL, strlen( finger->controlURL ), + &parsed_url ) ) ) { + if( !token_cmp + ( &parsed_url.pathquery, + &parsed_url_in.pathquery ) ) + return finger; + } + finger = finger->next; + } + } + + return NULL; + +} + +/************************************************************************ +* Function : printService +* +* Parameters : +* service_info *service ;Service whose information is to be printed +* Dbg_Level level ; Debug level specified to the print function +* Dbg_Module module ; Debug module specified to the print function +* +* Description : For debugging purposes prints information from the +* service passed into the function. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY( void printService( service_info * service, Dbg_Level level, + Dbg_Module module ) { + if( service ) { + if( service->serviceType ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "serviceType: %s\n", service->serviceType ); + if( service->serviceId ) + UpnpPrintf( level, module, __FILE__, __LINE__, "serviceId: %s\n", + service->serviceId ); if( service->SCPDURL ) + UpnpPrintf( level, module, __FILE__, __LINE__, "SCPDURL: %s\n", + service->SCPDURL ); if( service->controlURL ) + UpnpPrintf( level, module, __FILE__, __LINE__, "controlURL: %s\n", + service->controlURL ); if( service->eventURL ) + UpnpPrintf( level, module, __FILE__, __LINE__, "eventURL: %s\n", + service->eventURL ); if( service->UDN ) + UpnpPrintf( level, module, __FILE__, __LINE__, "UDN: %s\n\n", + service->UDN ); if( service->active ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "Service is active\n" ); + else + UpnpPrintf( level, module, __FILE__, __LINE__, + "Service is inactive\n" );} + } + + ) + +/************************************************************************ +* Function : printServiceList +* +* Parameters : +* service_info *service ; Service whose information is to be printed +* Dbg_Level level ; Debug level specified to the print function +* Dbg_Module module ; Debug module specified to the print function +* +* Description : For debugging purposes prints information of each +* service from the service table passed into the function. +* +* Return : void ; +* +* Note : +************************************************************************/ + DBGONLY( void printServiceList( service_info * service, + Dbg_Level level, + Dbg_Module module ) { + while( service ) { + if( service->serviceType ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "serviceType: %s\n", service->serviceType ); + if( service->serviceId ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "serviceId: %s\n", service->serviceId ); + if( service->SCPDURL ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "SCPDURL: %s\n", service->SCPDURL ); + if( service->controlURL ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "controlURL: %s\n", service->controlURL ); + if( service->eventURL ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "eventURL: %s\n", service->eventURL ); + if( service->UDN ) + UpnpPrintf( level, module, __FILE__, __LINE__, "UDN: %s\n\n", + service->UDN ); if( service->active ) + UpnpPrintf( level, module, __FILE__, __LINE__, + "Service is active\n" ); + else + UpnpPrintf( level, module, __FILE__, __LINE__, + "Service is inactive\n" ); + service = service->next;} + } + ) + +/************************************************************************ +* Function : printServiceTable +* +* Parameters : +* service_table * table ; Service table to be printed +* Dbg_Level level ; Debug level specified to the print function +* Dbg_Module module ; Debug module specified to the print function +* +* Description : For debugging purposes prints the URL base of the table +* and information of each service from the service table passed into +* the function. +* +* Return : void ; +* +* Note : +************************************************************************/ + DBGONLY( void printServiceTable( service_table * table, + Dbg_Level level, + Dbg_Module module ) { + UpnpPrintf( level, module, __FILE__, __LINE__, + "URL_BASE: %s\n", table->URLBase ); + UpnpPrintf( level, module, __FILE__, __LINE__, + "Services: \n" ); + printServiceList( table->serviceList, level, module );} + ) + +/************************************************************************ +* Function : freeService +* +* Parameters : +* service_info *in ; service information that is to be freed +* +* Description : Free's memory allocated for the various components +* of the service entry in the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ + void freeService( service_info * in ) +{ + if( in ) { + if( in->serviceType ) + ixmlFreeDOMString( in->serviceType ); + + if( in->serviceId ) + ixmlFreeDOMString( in->serviceId ); + + if( in->SCPDURL ) + free( in->SCPDURL ); + + if( in->controlURL ) + free( in->controlURL ); + + if( in->eventURL ) + free( in->eventURL ); + + if( in->UDN ) + ixmlFreeDOMString( in->UDN ); + + if( in->subscriptionList ) + freeSubscriptionList( in->subscriptionList ); + + in->TotalSubscriptions = 0; + free( in ); + } +} + +/************************************************************************ +* Function : freeServiceList +* +* Parameters : +* service_info * head ; Head of the service list to be freed +* +* Description : Free's memory allocated for the various components +* of each service entry in the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void +freeServiceList( service_info * head ) +{ + service_info *next = NULL; + + while( head ) { + if( head->serviceType ) + ixmlFreeDOMString( head->serviceType ); + if( head->serviceId ) + ixmlFreeDOMString( head->serviceId ); + if( head->SCPDURL ) + free( head->SCPDURL ); + if( head->controlURL ) + free( head->controlURL ); + if( head->eventURL ) + free( head->eventURL ); + if( head->UDN ) + ixmlFreeDOMString( head->UDN ); + if( head->subscriptionList ) + freeSubscriptionList( head->subscriptionList ); + + head->TotalSubscriptions = 0; + next = head->next; + free( head ); + head = next; + } +} + +/************************************************************************ +* Function : freeServiceTable +* +* Parameters : +* service_table * table ; Service table whose memory needs to be +* freed +* +* Description : Free's dynamic memory in table. +* (does not free table, only memory within the structure) +* +* Return : void ; +* +* Note : +************************************************************************/ +void +freeServiceTable( service_table * table ) +{ + ixmlFreeDOMString( table->URLBase ); + freeServiceList( table->serviceList ); + table->serviceList = NULL; + table->endServiceList = NULL; +} + +/************************************************************************ +* Function : getElementValue +* +* Parameters : +* IXML_Node *node ; Input node which provides the list of child +* nodes +* +* Description : Returns the clone of the element value +* +* Return : DOMString ; +* +* Note : value must be freed with DOMString_free +************************************************************************/ +DOMString +getElementValue( IXML_Node * node ) +{ + IXML_Node *child = ( IXML_Node * ) ixmlNode_getFirstChild( node ); + DOMString temp = NULL; + + if( ( child != 0 ) && ( ixmlNode_getNodeType( child ) == eTEXT_NODE ) ) { + temp = ixmlNode_getNodeValue( child ); + return ixmlCloneDOMString( temp ); + } else { + return NULL; + } +} + +/************************************************************************ +* Function : getSubElement +* +* Parameters : +* const char *element_name ; sub element name to be searched for +* IXML_Node *node ; Input node which provides the list of child +* nodes +* IXML_Node **out ; Ouput node to which the matched child node is +* returned. +* +* Description : Traverses through a list of XML nodes to find the +* node with the known element name. +* +* Return : int ; +* 1 - On Success +* 0 - On Failure +* +* Note : +************************************************************************/ +int +getSubElement( const char *element_name, + IXML_Node * node, + IXML_Node ** out ) +{ + + const DOMString NodeName = NULL; + int found = 0; + + IXML_Node *child = ( IXML_Node * ) ixmlNode_getFirstChild( node ); + + ( *out ) = NULL; + + while( ( child != NULL ) && ( !found ) ) { + + switch ( ixmlNode_getNodeType( child ) ) { + case eELEMENT_NODE: + + NodeName = ixmlNode_getNodeName( child ); + if( !strcmp( NodeName, element_name ) ) { + ( *out ) = child; + found = 1; + return found; + } + break; + + default: + break; + } + + child = ( IXML_Node * ) ixmlNode_getNextSibling( child ); + } + + return found; +} + +/************************************************************************ +* Function : getServiceList +* +* Parameters : +* IXML_Node *node ; XML node information +* service_info **end ; service added is returned to the output +* parameter +* char * URLBase ; provides Base URL to resolve relative URL +* +* Description : Returns pointer to service info after getting the +* sub-elements of the service info. +* +* Return : service_info * - pointer to the service info node ; +* +* Note : +************************************************************************/ +service_info * +getServiceList( IXML_Node * node, + service_info ** end, + char *URLBase ) +{ + IXML_Node *serviceList = NULL; + IXML_Node *current_service = NULL; + IXML_Node *UDN = NULL; + + IXML_Node *serviceType = NULL; + IXML_Node *serviceId = NULL; + IXML_Node *SCPDURL = NULL; + IXML_Node *controlURL = NULL; + IXML_Node *eventURL = NULL; + DOMString tempDOMString = NULL; + service_info *head = NULL; + service_info *current = NULL; + service_info *previous = NULL; + IXML_NodeList *serviceNodeList = NULL; + int NumOfServices = 0; + int i = 0; + int fail = 0; + + if( getSubElement( "UDN", node, &UDN ) && + getSubElement( "serviceList", node, &serviceList ) ) { + + serviceNodeList = ixmlElement_getElementsByTagName( ( IXML_Element + * ) + serviceList, + "service" ); + + if( serviceNodeList != NULL ) { + NumOfServices = ixmlNodeList_length( serviceNodeList ); + for( i = 0; i < NumOfServices; i++ ) { + current_service = ixmlNodeList_item( serviceNodeList, i ); + fail = 0; + + if( current ) { + current->next = + ( service_info * ) + malloc( sizeof( service_info ) ); + + previous = current; + current = current->next; + } else { + head = + ( service_info * ) + malloc( sizeof( service_info ) ); + current = head; + } + + if( !current ) { + freeServiceList( head ); + return NULL; + } + + current->next = NULL; + current->controlURL = NULL; + current->eventURL = NULL; + current->serviceType = NULL; + current->serviceId = NULL; + current->SCPDURL = NULL; + current->active = 1; + current->subscriptionList = NULL; + current->TotalSubscriptions = 0; + + if( !( current->UDN = getElementValue( UDN ) ) ) + fail = 1; + + if( ( !getSubElement( "serviceType", current_service, + &serviceType ) ) || + ( !( current->serviceType = + getElementValue( serviceType ) ) ) ) + fail = 1; + + if( ( !getSubElement( "serviceId", current_service, + &serviceId ) ) || + ( ! + ( current->serviceId = + getElementValue( serviceId ) ) ) ) + fail = 1; + + if( ( ! + ( getSubElement + ( "SCPDURL", current_service, &SCPDURL ) ) ) + || ( !( tempDOMString = getElementValue( SCPDURL ) ) ) + || + ( ! + ( current->SCPDURL = + resolve_rel_url( URLBase, tempDOMString ) ) ) ) + fail = 1; + + ixmlFreeDOMString( tempDOMString ); + tempDOMString = NULL; + + if( ( ! + ( getSubElement + ( "controlURL", current_service, &controlURL ) ) ) + || + ( !( tempDOMString = getElementValue( controlURL ) ) ) + || + ( ! + ( current->controlURL = + resolve_rel_url( URLBase, tempDOMString ) ) ) ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, GENA, __FILE__, __LINE__, + "BAD OR MISSING CONTROL URL" ) ); + DBGONLY( UpnpPrintf + ( UPNP_INFO, GENA, __FILE__, __LINE__, + "CONTROL URL SET TO NULL IN SERVICE INFO" ) ); + current->controlURL = NULL; + fail = 0; + } + + ixmlFreeDOMString( tempDOMString ); + tempDOMString = NULL; + + if( ( ! + ( getSubElement + ( "eventSubURL", current_service, &eventURL ) ) ) + || ( !( tempDOMString = getElementValue( eventURL ) ) ) + || + ( ! + ( current->eventURL = + resolve_rel_url( URLBase, tempDOMString ) ) ) ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, GENA, __FILE__, __LINE__, + "BAD OR MISSING EVENT URL" ) ); + DBGONLY( UpnpPrintf + ( UPNP_INFO, GENA, __FILE__, __LINE__, + "EVENT URL SET TO NULL IN SERVICE INFO" ) ); + current->eventURL = NULL; + fail = 0; + } + + ixmlFreeDOMString( tempDOMString ); + tempDOMString = NULL; + + if( fail ) { + freeServiceList( current ); + + if( previous ) + previous->next = NULL; + else + head = NULL; + + current = previous; + } + + } + + ixmlNodeList_free( serviceNodeList ); + } + + ( *end ) = current; + + return head; + } else + return NULL; + +} + +/************************************************************************ +* Function : getAllServiceList +* +* Parameters : +* IXML_Node *node ; XML node information +* char * URLBase ; provides Base URL to resolve relative URL +* service_info **out_end ; service added is returned to the output +* parameter +* +* Description : Returns pointer to service info after getting the +* sub-elements of the service info. +* +* Return : service_info * ; +* +* Note : +************************************************************************/ +service_info * +getAllServiceList( IXML_Node * node, + char *URLBase, + service_info ** out_end ) +{ + service_info *head = NULL; + service_info *end = NULL; + service_info *next_end = NULL; + IXML_NodeList *deviceList = NULL; + IXML_Node *currentDevice = NULL; + + int NumOfDevices = 0; + int i = 0; + + ( *out_end ) = NULL; + + deviceList = + ixmlElement_getElementsByTagName( ( IXML_Element * ) node, + "device" ); + if( deviceList != NULL ) { + NumOfDevices = ixmlNodeList_length( deviceList ); + for( i = 0; i < NumOfDevices; i++ ) { + currentDevice = ixmlNodeList_item( deviceList, i ); + if( head ) { + end->next = + getServiceList( currentDevice, &next_end, URLBase ); + end = next_end; + } else + head = getServiceList( currentDevice, &end, URLBase ); + + } + + ixmlNodeList_free( deviceList ); + } + + ( *out_end ) = end; + return head; +} + +/************************************************************************ +* Function : removeServiceTable +* +* Parameters : +* IXML_Node *node ; XML node information +* service_table *in ; service table from which services will be +* removed +* +* Description : This function assumes that services for a particular +* root device are placed linearly in the service table, and in the +* order in which they are found in the description document +* all services for this root device are removed from the list +* +* Return : int ; +* +* Note : +************************************************************************/ +int +removeServiceTable( IXML_Node * node, + service_table * in ) +{ + IXML_Node *root = NULL; + IXML_Node *currentUDN = NULL; + DOMString UDN = NULL; + IXML_NodeList *deviceList = NULL; + IXML_Node *currentDevice = NULL; + service_info *current_service = NULL; + service_info *start_search = NULL; + service_info *prev_service = NULL; + int NumOfDevices = 0; + int i = 0; + + if( getSubElement( "root", node, &root ) ) { + current_service = in->serviceList; + start_search = in->serviceList; + deviceList = + ixmlElement_getElementsByTagName( ( IXML_Element * ) root, + "device" ); + if( deviceList != NULL ) { + NumOfDevices = ixmlNodeList_length( deviceList ); + for( i = 0; i < NumOfDevices; i++ ) { + currentDevice = ixmlNodeList_item( deviceList, i ); + if( ( start_search ) + && ( ( getSubElement( "UDN", node, ¤tUDN ) ) + && ( UDN = getElementValue( currentUDN ) ) ) ) { + current_service = start_search; + //Services are put in the service table in the order in which they appear in the + //description document, therefore we go through the list only once to remove a particular + //root device + while( ( current_service ) + && ( strcmp( current_service->UDN, UDN ) ) ) { + current_service = current_service->next; + prev_service = current_service->next; + } + while( ( current_service ) + && ( !strcmp( current_service->UDN, UDN ) ) ) { + if( prev_service ) { + prev_service->next = current_service->next; + } else { + in->serviceList = current_service->next; + } + if( current_service == in->endServiceList ) + in->endServiceList = prev_service; + start_search = current_service->next; + freeService( current_service ); + current_service = start_search; + } + } + } + + ixmlNodeList_free( deviceList ); + } + } + return 1; +} + +/************************************************************************ +* Function : addServiceTable +* +* Parameters : +* IXML_Node *node ; XML node information +* service_table *in ; service table that will be initialized with +* services +* const char *DefaultURLBase ; Default base URL on which the URL +* will be returned to the service list. +* +* Description : Add Service to the table. +* +* Return : int ; +* +* Note : +************************************************************************/ +int +addServiceTable( IXML_Node * node, + service_table * in, + const char *DefaultURLBase ) +{ + IXML_Node *root = NULL; + IXML_Node *URLBase = NULL; + + service_info *tempEnd = NULL; + + if( in->URLBase ) { + free( in->URLBase ); + in->URLBase = NULL; + } + + if( getSubElement( "root", node, &root ) ) { + if( getSubElement( "URLBase", root, &URLBase ) ) { + in->URLBase = getElementValue( URLBase ); + } else { + if( DefaultURLBase ) { + in->URLBase = ixmlCloneDOMString( DefaultURLBase ); + } else { + in->URLBase = ixmlCloneDOMString( "" ); + } + } + + if( ( in->endServiceList->next = + getAllServiceList( root, in->URLBase, &tempEnd ) ) ) { + in->endServiceList = tempEnd; + return 1; + } + + } + + return 0; +} + +/************************************************************************ +* Function : getServiceTable +* +* Parameters : +* IXML_Node *node ; XML node information +* service_table *out ; output parameter which will contain the +* service list and URL +* const char *DefaultURLBase ; Default base URL on which the URL +* will be returned. +* +* Description : Retrieve service from the table +* +* Return : int ; +* +* Note : +************************************************************************/ +int +getServiceTable( IXML_Node * node, + service_table * out, + const char *DefaultURLBase ) +{ + IXML_Node *root = NULL; + IXML_Node *URLBase = NULL; + + if( getSubElement( "root", node, &root ) ) { + if( getSubElement( "URLBase", root, &URLBase ) ) { + out->URLBase = getElementValue( URLBase ); + } else { + if( DefaultURLBase ) { + out->URLBase = ixmlCloneDOMString( DefaultURLBase ); + } else { + out->URLBase = ixmlCloneDOMString( "" ); + } + } + + if( ( out->serviceList = getAllServiceList( root, out->URLBase, + &out-> + endServiceList ) ) ) { + return 1; + } + + } + + return 0; +} + +#endif // INCLUDE_DEVICE_APIS diff --git a/libupnp/upnp/src/genlib/util/membuffer.c b/libupnp/upnp/src/genlib/util/membuffer.c new file mode 100644 index 0000000..399e1f0 --- /dev/null +++ b/libupnp/upnp/src/genlib/util/membuffer.c @@ -0,0 +1,576 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file contains functions that operate on memory and +* buffers, allocation, re-allocation, and modification of the memory +************************************************************************/ + +#include "config.h" +#include +#include +#include +#include +#include "upnp.h" + +/************************************************************************ +* string * +************************************************************************/ + +/************************************************************************ +* Function : str_alloc +* +* Parameters : +* IN const char* str ; input string object +* IN size_t str_len ; input string length +* +* Description : Allocate memory and copy information from the input +* string to the newly allocated memory. +* +* Return : char* ; +* Pointer to the newly allocated memory. +* NULL if memory cannot be allocated. +* +* Note : +************************************************************************/ +char * +str_alloc( IN const char *str, + IN size_t str_len ) +{ + char *s; + + s = ( char * )malloc( str_len + 1 ); + if( s == NULL ) { + return NULL; // no mem + } + + memcpy( s, str, str_len ); + s[str_len] = '\0'; + + return s; +} + +/************************************************************************ +* memptr * +************************************************************************/ + +/************************************************************************ +* Function : memptr_cmp +* +* Parameters : +* IN memptr* m ; input memory object +* IN const char* s ; constatnt string for the memory object to be +* compared with +* +* Description : Compares characters of strings passed for number of +* bytes. If equal for the number of bytes, the length of the bytes +* determines which buffer is shorter. +* +* Return : int ; +* < 0 string1 substring less than string2 substring +* 0 string1 substring identical to string2 substring +* > 0 string1 substring greater than string2 substring +* +* Note : +************************************************************************/ +int +memptr_cmp( IN memptr * m, + IN const char *s ) +{ + int cmp; + + cmp = strncmp( m->buf, s, m->length ); + + if( cmp == 0 && m->length < strlen( s ) ) { + // both strings equal for 'm->length' chars + // if m is shorter than s, then s is greater + return -1; + } + + return cmp; +} + +/************************************************************************ +* Function : memptr_cmp_nocase +* +* Parameters : +* IN memptr* m ; input memory object +* IN const char* s ; constatnt string for the memory object to be +* compared with +* +* Description : Compares characters of 2 strings irrespective of the +* case for a specific count of bytes If the character comparison +* is the same the length of the 2 srings determines the shorter +* of the 2 strings. +* +* Return : int ; +* < 0 string1 substring less than string2 substring +* 0 string1 substring identical to string2 substring +* > 0 string1 substring greater than string2 substring +* +* Note : +************************************************************************/ +int +memptr_cmp_nocase( IN memptr * m, + IN const char *s ) +{ + int cmp; + + cmp = strncasecmp( m->buf, s, m->length ); + if( cmp == 0 && m->length < strlen( s ) ) { + // both strings equal for 'm->length' chars + // if m is shorter than s, then s is greater + return -1; + } + + return cmp; +} + +/************************************************************************ +* membuffer * +************************************************************************/ + +/************************************************************************ +* Function : membuffer_initialize +* +* Parameters : +* INOUT membuffer* m ; buffer to be initialized +* +* Description : Initialize the buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +static XINLINE void +membuffer_initialize( INOUT membuffer * m ) +{ + m->buf = NULL; + m->length = 0; + m->capacity = 0; +} + +/************************************************************************ +* Function : membuffer_set_size +* +* Parameters : +* INOUT membuffer* m ; buffer whose size is to be modified +* IN size_t new_length ; new size to which the buffer will be +* modified +* +* Description : Increases or decreases buffer cap so that at least +* 'new_length' bytes can be stored +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - On failure to allocate memory. +* +* Note : +************************************************************************/ +int +membuffer_set_size( INOUT membuffer * m, + IN size_t new_length ) +{ + size_t diff; + size_t alloc_len; + char *temp_buf; + + if( new_length >= m->length ) // increase length + { + // need more mem? + if( new_length <= m->capacity ) { + return 0; // have enough mem; done + } + + diff = new_length - m->length; + alloc_len = MAXVAL( m->size_inc, diff ) + m->capacity; + } else // decrease length + { + assert( new_length <= m->length ); + + // if diff is 0..m->size_inc, don't free + if( ( m->capacity - new_length ) <= m->size_inc ) { + return 0; + } + + alloc_len = new_length + m->size_inc; + } + + assert( alloc_len >= new_length ); + + temp_buf = realloc( m->buf, alloc_len + 1 ); //LEAK_FIX_MK + + //temp_buf = Realloc( m->buf,m->length, alloc_len + 1 );//LEAK_FIX_MK + + if( temp_buf == NULL ) { + // try smaller size + alloc_len = new_length; + temp_buf = realloc( m->buf, alloc_len + 1 ); //LEAK_FIX_MK + //temp_buf = Realloc( m->buf,m->length, alloc_len + 1 );//LEAK_FIX_MK + + if( temp_buf == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + } + // save + m->buf = temp_buf; + m->capacity = alloc_len; + return 0; +} + +/************************************************************************ +* Function : membuffer_init +* +* Parameters : +* INOUT membuffer* m ; buffer to be initialized +* +* Description : Wrapper to membuffer_initialize(). +* Set the size of the buffer to MEMBUF_DEF_SIZE_INC +* Initializes m->buf to NULL, length=0 +* +* Return : void ; +* +* Note : +************************************************************************/ +void +membuffer_init( INOUT membuffer * m ) +{ + assert( m != NULL ); + + m->size_inc = MEMBUF_DEF_SIZE_INC; + membuffer_initialize( m ); +} + +/************************************************************************ +* Function : membuffer_destroy +* +* Parameters : +* INOUT membuffer* m ; buffer to be destroyed +* +* Description : Free's memory allocated for membuffer* m. +* +* Return : void ; +* +* Note : +************************************************************************/ +void +membuffer_destroy( INOUT membuffer * m ) +{ + if( m == NULL ) { + return; + } + + free( m->buf ); + membuffer_init( m ); +} + +/************************************************************************ +* Function : membuffer_assign +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory is to be allocated and +* assigned. +* IN const void* buf ; source buffer whose contents will be copied +* IN size_t buf_len ; length of the source buffer +* +* Description : Allocate memory to membuffer* m and copy the contents +* of the in parameter IN const void* buf. +* +* Return : int ; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* +* Note : +************************************************************************/ +int +membuffer_assign( INOUT membuffer * m, + IN const void *buf, + IN size_t buf_len ) +{ + int return_code; + + assert( m != NULL ); + + // set value to null + if( buf == NULL ) { + membuffer_destroy( m ); + return 0; + } + // alloc mem + return_code = membuffer_set_size( m, buf_len ); + if( return_code != 0 ) { + return return_code; + } + // copy + memcpy( m->buf, buf, buf_len ); + m->buf[buf_len] = 0; // null-terminate + + m->length = buf_len; + + return 0; +} + +/************************************************************************ +* Function : membuffer_assign_str +* +* Parameters : +* INOUT membuffer* m ; buffer to be allocated and assigned +* IN const char* c_str ; source buffer whose contents will be +* copied +* +* Description : Wrapper function for membuffer_assign() +* +* Return : int ; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* +* Note : +************************************************************************/ +int +membuffer_assign_str( INOUT membuffer * m, + IN const char *c_str ) +{ + return membuffer_assign( m, c_str, strlen( c_str ) ); +} + +/************************************************************************ +* Function : membuffer_append +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory is to be appended. +* IN const void* buf ; source buffer whose contents will be +* copied +* IN size_t buf_len ; length of the source buffer +* +* Description : Invokes function to appends data from a constant buffer +* to the buffer +* +* Return : int ; +* +* Note : +************************************************************************/ +int +membuffer_append( INOUT membuffer * m, + IN const void *buf, + IN size_t buf_len ) +{ + assert( m != NULL ); + + return membuffer_insert( m, buf, buf_len, m->length ); +} + +/************************************************************************ +* Function : membuffer_append_str +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory is to be appended. +* IN const char* c_str ; source buffer whose contents will be +* copied +* +* Description : Invokes function to appends data from a constant string +* to the buffer +* +* Return : int ; +* +* Note : +************************************************************************/ +int +membuffer_append_str( INOUT membuffer * m, + IN const char *c_str ) +{ + return membuffer_insert( m, c_str, strlen( c_str ), m->length ); +} + +/************************************************************************ +* Function : membuffer_insert +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory size is to be increased +* and appended. +* IN const void* buf ; source buffer whose contents will be +* copied +* IN size_t buf_len ; size of the source buffer +* int index ; index to determine the bounds while movinf the data +* +* Description : Allocates memory for the new data to be inserted. Does +* memory management by moving the data from the existing memory to +* the newly allocated memory and then appending the new data. +* +* Return : int ; +* +* Note : +************************************************************************/ +int +membuffer_insert( INOUT membuffer * m, + IN const void *buf, + IN size_t buf_len, + int index ) +{ + int return_code; + + assert( m != NULL ); + + if( index < 0 || index > ( int )m->length ) + return UPNP_E_OUTOF_BOUNDS; + + if( buf == NULL || buf_len == 0 ) { + return 0; + } + // alloc mem + return_code = membuffer_set_size( m, m->length + buf_len ); + if( return_code != 0 ) { + return return_code; + } + // insert data + + // move data to right of insertion point + memmove( m->buf + index + buf_len, m->buf + index, m->length - index ); + memcpy( m->buf + index, buf, buf_len ); + m->length += buf_len; + m->buf[m->length] = 0; // null-terminate + + return 0; +} + +/************************************************************************ +* Function : membuffer_delete +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory size is to be decreased +* and copied to the odified location +* IN int index ; index to determine bounds while moving data +* IN size_t num_bytes ; number of bytes that the data needs to +* shrink by +* +* Description : Shrink the size of the buffer depending on the current +* size of the bufer and te input parameters. Move contents from the +* old buffer to the new sized buffer. +* +* Return : void ; +* +* Note : +************************************************************************/ +void +membuffer_delete( INOUT membuffer * m, + IN int index, + IN size_t num_bytes ) +{ + int return_value; + int new_length; + size_t copy_len; + + assert( m != NULL ); + + if( m->length == 0 ) { + return; + } + + assert( index >= 0 && index < ( int )m->length ); + + // shrink count if it goes beyond buffer + if( index + num_bytes > m->length ) { + num_bytes = m->length - ( size_t ) index; + copy_len = 0; // every thing at and after index purged + } else { + // calc num bytes after deleted string + copy_len = m->length - ( index + num_bytes ); + } + + memmove( m->buf + index, m->buf + index + num_bytes, copy_len ); + + new_length = m->length - num_bytes; + return_value = membuffer_set_size( m, new_length ); // trim buffer + assert( return_value == 0 ); // shrinking should always work + + // don't modify until buffer is set + m->length = new_length; + m->buf[new_length] = 0; +} + +/************************************************************************ +* Function : membuffer_detach +* +* Parameters : +* INOUT membuffer* m ; buffer to be returned and updated. +* +* Description : Detaches current buffer and returns it. The caller +* must free the returned buffer using free(). +* After this call, length becomes 0. +* +* Return : char* ; +* a pointer to the current buffer +* +* Note : +************************************************************************/ +char * +membuffer_detach( INOUT membuffer * m ) +{ + char *buf; + + assert( m != NULL ); + + buf = m->buf; + + // free all + membuffer_initialize( m ); + + return buf; +} + +/************************************************************************ +* Function : membuffer_attach +* +* Parameters : +* INOUT membuffer* m ; buffer to be updated +* IN char* new_buf ; source buffer which will be assigned to the +* buffer to be updated +* IN size_t buf_len ; length of the source buffer +* +* Description : Free existing memory in membuffer and assign the new +* buffer in its place. +* +* Return : void ; +* +* Note : 'new_buf' must be allocted using malloc or realloc so +* that it can be freed using free() +************************************************************************/ +void +membuffer_attach( INOUT membuffer * m, + IN char *new_buf, + IN size_t buf_len ) +{ + assert( m != NULL ); + + membuffer_destroy( m ); + m->buf = new_buf; + m->length = buf_len; + m->capacity = buf_len; +} diff --git a/libupnp/upnp/src/genlib/util/strintmap.c b/libupnp/upnp/src/genlib/util/strintmap.c new file mode 100644 index 0000000..0e9db10 --- /dev/null +++ b/libupnp/upnp/src/genlib/util/strintmap.c @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file contains string to integer and integer to string +* conversion functions +************************************************************************/ + +#include "config.h" +#include "strintmap.h" +#include "membuffer.h" + +/************************************************************************ +* Function : map_str_to_int +* +* Parameters : +* IN const char* name ; string containing the name to be matched +* IN size_t name_len ; size of the string to be matched +* IN str_int_entry* table ; table of entries that need to be +* matched. +* IN int num_entries ; number of entries in the table that need +* to be searched. +* IN xboolean case_sensitive ; whether the case should be case +* sensitive or not +* +* Description : Match the given name with names from the entries in the +* table. Returns the index of the table when the entry is found. +* +* Return : int ; +* index - On Success +* -1 - On failure +* +* Note : +************************************************************************/ +int +map_str_to_int( IN const char *name, + IN size_t name_len, + IN str_int_entry * table, + IN int num_entries, + IN xboolean case_sensitive ) +{ + int top, + mid, + bot; + int cmp; + memptr name_ptr; + + name_ptr.buf = ( char * )name; + name_ptr.length = name_len; + + top = 0; + bot = num_entries - 1; + + while( top <= bot ) { + mid = ( top + bot ) / 2; + if( case_sensitive ) { + //cmp = strcmp( name, table[mid].name ); + cmp = memptr_cmp( &name_ptr, table[mid].name ); + } else { + //cmp = strcasecmp( name, table[mid].name ); + cmp = memptr_cmp_nocase( &name_ptr, table[mid].name ); + } + + if( cmp > 0 ) { + top = mid + 1; // look below mid + } else if( cmp < 0 ) { + bot = mid - 1; // look above mid + } else // cmp == 0 + { + return mid; // match; return table index + } + } + + return -1; // header name not found +} + +/************************************************************************ +* Function : map_int_to_str +* +* Parameters : +* IN int id ; ID to be matched +* IN str_int_entry* table ; table of entries that need to be +* matched. +* IN int num_entries ; number of entries in the table that need +* to be searched. +* +* Description : Returns the index from the table where the id matches +* the entry from the table. +* +* Return : int ; +* +* Note : +************************************************************************/ +int +map_int_to_str( IN int id, + IN str_int_entry * table, + IN int num_entries ) +{ + int i; + + for( i = 0; i < num_entries; i++ ) { + if( table[i].id == id ) { + return i; + } + } + return -1; +} diff --git a/libupnp/upnp/src/genlib/util/upnp_timeout.c b/libupnp/upnp/src/genlib/util/upnp_timeout.c new file mode 100644 index 0000000..2138c4b --- /dev/null +++ b/libupnp/upnp/src/genlib/util/upnp_timeout.c @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file contains a function for freeing the memory associated +* wuth a upnp time out event. +************************************************************************/ + +#include "config.h" +#include "upnp_timeout.h" + +#include + +/************************************************************************ +* Function : free_upnp_timeout +* +* Parameters : +* upnp_timeout *event ; Event which needs to be freed +* +* Description : Free memory associated with event and memory for any +* sub-elements +* +* Return : void ; +* +* Note : +************************************************************************/ +void +free_upnp_timeout( upnp_timeout * event ) +{ + + if( event ) { + if( event->Event ) + free( event->Event ); + free( event ); + + } +} diff --git a/libupnp/upnp/src/genlib/util/util.c b/libupnp/upnp/src/genlib/util/util.c new file mode 100644 index 0000000..a30f7fe --- /dev/null +++ b/libupnp/upnp/src/genlib/util/util.c @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/************************************************************************ +* Purpose: This file contains functions for copying strings based on +* different options. +************************************************************************/ + +#include "config.h" +#include "upnp.h" +#include "util.h" + +/************************************************************************ +* Function : linecopy +* +* Parameters : +* OUT char dest[LINE_SIZE] ; output buffer +* IN const char* src ; input buffer +* +* Description : Copy no of bytes spcified by the LINE_SIZE constant, +* from the source buffer. Null terminate the destination buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +void +linecopy( OUT char dest[LINE_SIZE], + IN const char *src ) +{ + strncpy( dest, src, LINE_SIZE - 1 ); + dest[LINE_SIZE - 1] = '\0'; // null-terminate if len(src) >= LINE_SIZE +} + +/************************************************************************ +* Function : namecopy +* +* Parameters : +* OUT char dest[NAME_SIZE] ; output buffer +* IN const char* src ; input buffer +* +* Description : Copy no of bytes spcified by the NAME_SIZE constant, +* from the source buffer. Null terminate the destination buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +void +namecopy( OUT char dest[NAME_SIZE], + IN const char *src ) +{ + strncpy( dest, src, NAME_SIZE - 1 ); + dest[NAME_SIZE - 1] = '\0'; // null-terminate if len(src) >= NAME_SIZE +} + +/************************************************************************ +* Function : linecopylen +* +* Parameters : +* OUT char dest[LINE_SIZE] ; output buffer +* IN const char* src ; input buffer +* IN size_t srclen ; bytes to be copied. +* +* Description : Determine if the srclen passed in paramter is less than +* the permitted LINE_SIZE. If it is use the passed parameter, if not +* use the permitted LINE_SIZE as the length parameter +* Copy no of bytes spcified by the LINE_SIZE constant, +* from the source buffer. Null terminate the destination buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +void +linecopylen( OUT char dest[LINE_SIZE], + IN const char *src, + IN size_t srclen ) +{ + int len; + + len = srclen < ( LINE_SIZE - 1 ) ? srclen : ( LINE_SIZE - 1 ); + strncpy( dest, src, len ); + dest[len] = '\0'; +} diff --git a/libupnp/upnp/src/inc/client_table.h b/libupnp/upnp/src/inc/client_table.h new file mode 100644 index 0000000..0c33cf7 --- /dev/null +++ b/libupnp/upnp/src/inc/client_table.h @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _CLIENT_TABLE +#define _CLIENT_TABLE + +#ifdef __cplusplus +extern "C" { +#endif + +#include "upnp.h" + +#include +//#include +#include +#include +#include "uri.h" +#include "service_table.h" + +#include "TimerThread.h" +#include "upnp_timeout.h" + +extern TimerThread gTimerThread; + +CLIENTONLY( +typedef struct CLIENT_SUBSCRIPTION { + Upnp_SID sid; + char * ActualSID; + char * EventURL; + int RenewEventId; + struct CLIENT_SUBSCRIPTION * next; +} client_subscription; + +/************************************************************************ +* Function : copy_client_subscription +* +* Parameters : +* client_subscription * in ; - source client subscription +* client_subscription * out ; - destination client subscription +* +* Description : Make a copy of the client subscription data +* +* Return : int ; +* UPNP_E_OUTOF_MEMORY - On Failure to allocate memory +* HTTP_SUCCESS - On Success +* +* Note : +************************************************************************/ +int copy_client_subscription(client_subscription * in, client_subscription * out); + +/************************************************************************ +* Function : free_client_subscription +* +* Parameters : +* client_subscription * sub ; - Client subscription to be freed +* +* Description : Free memory allocated for client subscription data. +* Remove timer thread associated with this subscription event. +* +* Return : void ; +* +* Note : +************************************************************************/ +void free_client_subscription(client_subscription * sub); + + +/************************************************************************ +* Function : freeClientSubList +* +* Parameters : +* client_subscription * list ; Client subscription +* +* Description : Free the client subscription table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void freeClientSubList(client_subscription * list); + +/************************************************************************ +* Function : RemoveClientSubClientSID +* +* Parameters : +* client_subscription **head ; Head of the subscription list +* const Upnp_SID sid ; Subscription ID to be mactched +* +* Description : Remove the client subscription matching the +* subscritpion id represented by the const Upnp_SID sid parameter +* from the table and update the table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void RemoveClientSubClientSID(client_subscription **head, + const Upnp_SID sid); + +/************************************************************************ +* Function : GetClientSubClientSID +* +* Parameters : +* client_subscription *head ; Head of the subscription list +* const Upnp_SID sid ; Subscription ID to be matched +* +* Description : Return the client subscription from the client table +* that matches const Upnp_SID sid subscrition id value. +* +* Return : client_subscription * ; The matching subscription +* +* Note : +************************************************************************/ +client_subscription * GetClientSubClientSID(client_subscription *head + , const Upnp_SID sid); + +/************************************************************************ +* Function : GetClientSubActualSID +* +* Parameters : +* client_subscription *head ; Head of the subscription list +* token * sid ; Subscription ID to be matched +* +* Description : Returns the client subscription from the client +* subscription table that has the matching token * sid buffer +* value. +* +* Return : client_subscription * ; The matching subscription +* +* Note : +************************************************************************/ +client_subscription * GetClientSubActualSID(client_subscription *head + , token * sid); +) + +#ifdef __cplusplus +} +#endif + +#endif /* _CLIENT_TABLE */ diff --git a/libupnp/upnp/src/inc/config.h b/libupnp/upnp/src/inc/config.h new file mode 100644 index 0000000..7d97239 --- /dev/null +++ b/libupnp/upnp/src/inc/config.h @@ -0,0 +1,360 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef INTERNAL_CONFIG_H +#define INTERNAL_CONFIG_H + +#include "autoconfig.h" + + +/** @name Compile time configuration options + * The Linux SDK for UPnP Devices contains some compile-time parameters + * that effect the behavior of the SDK. All configuration options are + * located in {\tt src/inc/config.h}. + */ + +//@{ + +/** @name THREAD_IDLE_TIME + * The {\tt THREAD_IDLE_TIME} constant determines when a thread will be + * removed from the thread pool and returned to the operating system. When + * a thread in the thread pool has been idle for this number of milliseconds + * the thread will be released from the thread pool. The default value is + * 5000 milliseconds (5 seconds). + */ + +//@{ +#define THREAD_IDLE_TIME 5000 +//@} + +/** @name JOBS_PER_THREAD + * The {\tt JOBS_PER_THREAD} constant determines when a new thread will be + * allocated to the thread pool inside the SDK. The thread pool will + * try and maintain this jobs/thread ratio. When the jobs/thread ratio + * becomes greater than this, then a new thread (up to the max) will be + * allocated to the thread pool. The default ratio is 10 jobs/thread. + */ + +//@{ +#define JOBS_PER_THREAD 10 +//@} + +/** @name MIN_THREADS + * The {\tt MIN_THREADS} constant defines the minimum number of threads the + * thread pool inside the SDK will create. The thread pool will + * always have this number of threads. These threads are used + * for both callbacks into applications built on top of the SDK and also + * for making connections to other control points and devices. This number + * includes persistent threads. The default value is two threads. + */ + +//@{ +#define MIN_THREADS 2 +//@} + +/** @name MAX_THREADS + * The {\tt MAX_THREADS} constant defines the maximum number of threads the + * thread pool inside the SDK will create. These threads are used + * for both callbacks into applications built on top of the library and also + * for making connections to other control points and devices. It is not + * recommended that this value be below 10, since the threads are + * necessary for correct operation. This value can be increased for greater + * performance in operation at the expense of greater memory overhead. The + * default value is 12. + */ + +//@{ +#define MAX_THREADS 12 +//@} + +/** @name DEFAULT_SOAP_CONTENT_LENGTH + * SOAP messages will read at most {\tt DEFAULT_SOAP_CONTENT_LENGTH} bytes. + * This prevents devices that have a misbehaving web server to send + * a large amount of data to the control point causing it to crash. + * This can be adjusted dynamically with {\tt UpnpSetMaxContentLength}. + */ +//@{ +#define DEFAULT_SOAP_CONTENT_LENGTH 16000 +//@} + +/** @name NUM_SSDP_COPY + * This configuration parameter determines how many copies of each SSDP + * advertisement and search packets will be sent. By default it will send two + * copies of every packet. + */ +//@{ +#define NUM_SSDP_COPY 2 +//@} + +/** @name SSDP_PAUSE + * This configuration parameter determines the pause between identical SSDP + * advertisement and search packets. The pause is measured in milliseconds + * and defaults to 100. + */ +//@{ +#define SSDP_PAUSE 100 +//@} + +/** @name WEB_SERVER_BUF_SIZE + * This configuration parameter sets the maximum buffer size for the + * webserver. The default value is 1MB. + */ +//@{ +#define WEB_SERVER_BUF_SIZE (1024*1024) +//@} + +/** @name AUTO_RENEW_TIME + * The {\tt AUTO_RENEW_TIME} is the time, in seconds, before a subscription + * expires that the SDK automatically resubscribes. The default + * value is 10 seconds. Setting this value too low can result in the + * subscription renewal not making it to the device in time, causing the + * subscription to timeout. In order to avoid continually resubscribing + * the minimum subscription time is five seconds more than the auto renew + * time. + */ + +//@{ +#define AUTO_RENEW_TIME 10 +//@} + +/** @name CP_MINIMUM_SUBSCRIPTION_TIME + * The {\tt CP_MINIMUM_SUBSCRIPTION_TIME} is the minimum subscription time + * allowed for a control point using the SDK. Subscribing for less than + * this time automatically results in a subscription for this amount. The + * default value is 5 seconds more than the {\tt AUTO_RENEW_TIME}, or 15 + * seconds. + */ + +//@{ +#define CP_MINIMUM_SUBSCRIPTION_TIME (AUTO_RENEW_TIME + 5) +//@} + +/** @name MAX_SEARCH_TIME + * The {\tt MAX_SEARCH_TIME} is the maximum time + * allowed for an SSDP search by a control point. Searching for greater than + * this time automatically results in a search for this amount. The default + * value is 80 seconds. + */ + +//@{ +#define MAX_SEARCH_TIME 80 +//@} + +/** @name MIN_SEARCH_TIME + * The {\tt MIN_SEARCH_TIME} is the minimumm time + * allowed for an SSDP search by a control point. Searching for less than + * this time automatically results in a search for this amount. The default + * value is 2 seconds. + */ + +//@{ +#define MIN_SEARCH_TIME 2 +//@} + + +/** @name AUTO_ADVERTISEMENT_TIME + * The {\tt AUTO_ADVERTISEMENT_TIME} is the time, in seconds, before an + * device advertisements expires before a renewed advertisement is sent. + * The default time is 30 seconds. + */ + +//@{ +#define AUTO_ADVERTISEMENT_TIME 30 +//@} + +/** @name SSDP_PACKET_DISTRIBUTE + * The {\tt SSDP_PACKET_DISTRIBUTE} enables the SSDP packets to be sent + * at an interval equal to half of the expiration time of SSDP packets + * minus the AUTO_ADVERTISEMENT_TIME. This is used to increase + * the probability of SSDP packets reaching to control points. + * It is recommended that this flag be turned on for embedded wireless + * devices. + */ + +//@{ +#define SSDP_PACKET_DISTRIBUTE 1 +//@} + +/** @name Module Exclusion + * Depending on the requirements, the user can selectively discard any of + * the major modules like SOAP, GENA, SSDP or the Internal web server. By + * default everything is included inside the SDK. By setting any of + * the values below to 0, that component will not be included in the final + * SDK. + * \begin{itemize} + * \item {\tt EXCLUDE_SOAP[0,1]} + * \item {\tt EXCLUDE_GENA[0,1]} + * \item {\tt EXCLUDE_SSDP[0,1]} + * \item {\tt EXCLUDE_DOM [0,1]} + * \item {\tt EXCLUDE_MINISERVER[0,1]} + * \item {\tt EXCLUDE_WEB_SERVER[0,1]} + * \item {\tt EXCLUDE_JNI[0,1]} + * \end{itemize} + * + */ + +//@{ +#define EXCLUDE_SSDP 0 +#define EXCLUDE_SOAP 0 +#define EXCLUDE_GENA 0 +#define EXCLUDE_DOM 0 +#define EXCLUDE_MINISERVER 0 +#define EXCLUDE_WEB_SERVER 0 +#ifdef USE_JNI +# define EXCLUDE_JNI 0 +#else +# define EXCLUDE_JNI 1 +#endif +//@} + + +/** @name DEBUG_TARGET + * The user has the option to redirect the library output debug messages + * to either the screen or to a log file. All the output messages with + * debug level 0 will go to {\tt upnp.err} and messages with debug level + * greater than zero will be redirected to {\tt upnp.out}. + */ + +//@{ +#define DEBUG_TARGET 1 +//@} + + +/** @name Other debugging features + The UPnP SDK contains other features to aid in debugging: + see + */ + +#define DEBUG_ALL 1 +#define DEBUG_SSDP 0 +#define DEBUG_SOAP 0 +#define DEBUG_GENA 0 +#define DEBUG_TPOOL 0 +#define DEBUG_MSERV 0 +#define DEBUG_DOM 0 +#define DEBUG_HTTP 0 +#define DEBUG_API 0 + +//@} // Compile time configuration options + + +/*************************************************************************** + * Do not change, Internal purpose only!!! + ***************************************************************************/ + +//@{ + +/* + * Set additional defines based on requested configuration + */ + +// configure --enable-client +#if UPNP_HAVE_CLIENT +# define INCLUDE_CLIENT_APIS 1 +#endif + +// configure --enable-device +#if UPNP_HAVE_DEVICE +# define INCLUDE_DEVICE_APIS 1 +#endif + +// configure --enable-webserver --enable-device +#if UPNP_HAVE_WEBSERVER +# define INTERNAL_WEB_SERVER 1 +#endif + + + +/** @name DBGONLY + The {\bf DBGONLY} macro allows code to be marked so that it + is only included in the DEBUG build and not the release. To + use this macro, put the code inside of the parentheses: + + {\tt DBGONLY(int i;)} + + This will cause a declaration of the integer {\tt i} only + in the debug build. + */ + +//@{ +#ifdef DEBUG +# define DBGONLY(x) x +#else +# define DBGONLY(x) +#endif +//@} + + + +#undef EXCLUDE_WEB_SERVER +#undef EXCLUDE_MINISERVER +#ifdef INTERNAL_WEB_SERVER +# define EXCLUDE_WEB_SERVER 0 +# define EXCLUDE_MINISERVER 0 +#else +# define EXCLUDE_WEB_SERVER 1 +# define EXCLUDE_MINISERVER 1 +#endif + +#if EXCLUDE_GENA == 1 && EXCLUDE_SOAP == 1 && EXCLUDE_WEB_SERVER == 1 +# undef EXCLUDE_MINISERVER +# define EXCLUDE_MINISERVER 1 +# if INTERNAL_WEB_SERVER +# error "conflicting settings: use configure --disable-webserver" +# endif +#endif + +#if EXCLUDE_GENA == 0 || EXCLUDE_SOAP == 0 || EXCLUDE_WEB_SERVER == 0 +# undef EXCLUDE_MINISERVER +# define EXCLUDE_MINISERVER 0 +# if EXCLUDE_WEB_SERVER == 0 && !defined INTERNAL_WEB_SERVER +# error "conflicting settings : use configure --enable-webserver" +# endif +#endif + + + +#ifdef INCLUDE_CLIENT_APIS +# define CLIENTONLY(x) x +#else +# define CLIENTONLY(x) +#endif + +#ifdef INCLUDE_DEVICE_APIS +# define DEVICEONLY(x) x +#else +# define DEVICEONLY(x) +#endif + +//@} +#endif + + diff --git a/libupnp/upnp/src/inc/gena.h b/libupnp/upnp/src/inc/gena.h new file mode 100644 index 0000000..e947db0 --- /dev/null +++ b/libupnp/upnp/src/inc/gena.h @@ -0,0 +1,379 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _GENA_ +#define _GENA_ +#include "config.h" +#include "service_table.h" +#include "miniserver.h" +#include "uri.h" +#include "upnp.h" + +#include +#include "ThreadPool.h" +#include +#include "client_table.h" +#include "httpparser.h" +#include "sock.h" + +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif + +#define XML_VERSION "\n" +#define XML_PROPERTYSET_HEADER \ + "\n" + +#define UNABLE_MEMORY "HTTP/1.1 500 Internal Server Error\r\n\r\n" +#define UNABLE_SERVICE_UNKNOWN "HTTP/1.1 404 Not Found\r\n\r\n" +#define UNABLE_SERVICE_NOT_ACCEPT \ + "HTTP/1.1 503 Service Not Available\r\n\r\n" + + +#define NOT_IMPLEMENTED "HTTP/1.1 501 Not Implemented\r\n\r\n" +#define BAD_REQUEST "HTTP/1.1 400 Bad Request\r\n\r\n" +#define INVALID_NT BAD_CALLBACK +#define BAD_CALLBACK "HTTP/1.1 412 Precondition Failed\r\n\r\n" +#define HTTP_OK_CRLF "HTTP/1.1 200 OK\r\n\r\n" +#define HTTP_OK_STR "HTTP/1.1 200 OK\r\n" +#define INVALID_SID BAD_CALLBACK +#define MISSING_SID BAD_CALLBACK +#define MAX_CONTENT_LENGTH 20 +#define MAX_SECONDS 10 +#define MAX_EVENTS 20 +#define MAX_PORT_SIZE 10 +#define GENA_E_BAD_RESPONSE UPNP_E_BAD_RESPONSE +#define GENA_E_BAD_SERVICE UPNP_E_INVALID_SERVICE +#define GENA_E_SUBSCRIPTION_UNACCEPTED UPNP_E_SUBSCRIBE_UNACCEPTED +#define GENA_E_BAD_SID UPNP_E_INVALID_SID +#define GENA_E_UNSUBSCRIBE_UNACCEPTED UPNP_E_UNSUBSCRIBE_UNACCEPTED +#define GENA_E_NOTIFY_UNACCEPTED UPNP_E_NOTIFY_UNACCEPTED +#define GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB -9 +#define GENA_E_BAD_HANDLE UPNP_E_INVALID_HANDLE +#define XML_ERROR -5 +#define XML_SUCCESS UPNP_E_SUCCESS +#define GENA_SUCCESS UPNP_E_SUCCESS +#define CALLBACK_SUCCESS 0 +#define DEFAULT_TIMEOUT 1801 + + + +extern ithread_mutex_t GlobalClientSubscribeMutex; + +//Lock the subscription +#define SubscribeLock() \ + DBGONLY(UpnpPrintf(UPNP_INFO,GENA,__FILE__,__LINE__, \ + "Trying Subscribe Lock")); \ + ithread_mutex_lock(&GlobalClientSubscribeMutex); \ + DBGONLY(UpnpPrintf(UPNP_INFO,GENA,__FILE__,__LINE__,"Subscribe Lock");) + +//Unlock the subscription +#define SubscribeUnlock() \ + DBGONLY(UpnpPrintf(UPNP_INFO,GENA,__FILE__,__LINE__, \ + "Trying Subscribe UnLock")); \ + ithread_mutex_unlock(&GlobalClientSubscribeMutex); \ + DBGONLY(UpnpPrintf(UPNP_INFO,GENA,__FILE__,__LINE__,"Subscribe UnLock");) + + +//Structure to send NOTIFY message to all subscribed control points +typedef struct NOTIFY_THREAD_STRUCT { + char * headers; + DOMString propertySet; + char * servId; + char * UDN; + Upnp_SID sid; + int eventKey; + int *reference_count; + UpnpDevice_Handle device_handle; +} notify_thread_struct; + + +/************************************************************************ +* Function : genaCallback +* +* Parameters: +* IN http_parser_t *parser: represents the parse state of the request +* IN http_message_t* request: HTTP message containing GENA request +* INOUT SOCKINFO *info: Structure containing information about the socket +* +* Description: +* This is the callback function called by the miniserver to handle +* incoming GENA requests. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +EXTERN_C void genaCallback (IN http_parser_t *parser, + IN http_message_t* request, + IN SOCKINFO *info); + +/************************************************************************ +* Function : genaSubscribe +* +* Parameters: +* IN UpnpClient_Handle client_handle: +* IN char * PublisherURL: NULL Terminated, of the form : +* "http://134.134.156.80:4000/RedBulb/Event" +* INOUT int * TimeOut: requested Duration, if -1, then "infinite". +* in the OUT case: actual Duration granted +* by Service, -1 for infinite +* OUT Upnp_SID out_sid:sid of subscription, memory passed in by caller +* +* Description: +* This function subscribes to a PublisherURL ( also mentioned as EventURL +* some places). It sends SUBSCRIBE http request to service processes +* request. Finally adds a Subscription to +* the clients subscription list, if service responds with OK +* +* Returns: int +* return UPNP_E_SUCCESS if service response is OK else +* returns appropriate error +***************************************************************************/ +CLIENTONLY( + EXTERN_C int genaSubscribe(UpnpClient_Handle client_handle, + char * PublisherURL, + int * TimeOut, + Upnp_SID out_sid );) + + +/************************************************************************ +* Function : genaUnSubscribe +* +* Parameters: +* IN UpnpClient_Handle client_handle: UPnP client handle +* IN SID in_sid: The subscription ID +* +* Description: +* This function unsubscribes a SID. It first validates the SID and +* client_handle,copies the subscription, sends UNSUBSCRIBE http request +* to service processes request and finally removes the subscription +* +* Returns: int +* return UPNP_E_SUCCESS if service response is OK else +* returns appropriate error +***************************************************************************/ +CLIENTONLY(EXTERN_C int genaUnSubscribe(UpnpClient_Handle client_handle, + const Upnp_SID in_sid);) + +/************************************************************************ +* Function : genaUnregisterClient +* +* Parameters: +* IN UpnpClient_Handle client_handle: Handle containing all the control +* point related information +* +* Description: +* This function unsubcribes all the outstanding subscriptions and cleans +* the subscription list. This function is called when control point +* unregisters. +* +* Returns: int +* return UPNP_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +CLIENTONLY(EXTERN_C int genaUnregisterClient( + UpnpClient_Handle client_handle);) + +//server +/************************************************************************ +* Function : genaUnregisterDevice +* +* Parameters: +* IN UpnpDevice_Handle device_handle: Handle of the root device +* +* Description: +* This function cleans the service table of the device. +* +* Returns: int +* returns UPNP_E_SUCCESS if successful else returns GENA_E_BAD_HANDLE +****************************************************************************/ +DEVICEONLY(EXTERN_C int genaUnregisterDevice( + UpnpDevice_Handle device_handle);) + + +/************************************************************************ +* Function : genaRenewSubscription +* +* Parameters: +* IN UpnpClient_Handle client_handle: Client handle +* IN const Upnp_SID in_sid: subscription ID +* INOUT int * TimeOut: requested Duration, if -1, then "infinite". +* in the OUT case: actual Duration granted +* by Service, -1 for infinite +* +* Description: +* This function renews a SID. It first validates the SID and +* client_handle and copies the subscription. It sends RENEW +* (modified SUBSCRIBE) http request to service and processes +* the response. +* +* Returns: int +* return UPNP_E_SUCCESS if service response is OK else +* returns appropriate error +***************************************************************************/ +CLIENTONLY(EXTERN_C int genaRenewSubscription( + IN UpnpClient_Handle client_handle, + IN const Upnp_SID in_sid, + OUT int * TimeOut);) +/**************************************************************************** +* Function : genaNotifyAll +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN char **VarNames : array of varible names +* IN char **VarValues : array of variable values +* IN int var_count : number of variables +* +* Description : This function sends a notification to all the subscribed +* control points +* +* Return : int +* +* Note : This function is similar to the genaNotifyAllExt. The only difference +* is it takes event variable array instead of xml document. +****************************************************************************/ +DEVICEONLY(EXTERN_C int genaNotifyAll(UpnpDevice_Handle device_handle, + char *UDN, + char *servId, + char **VarNames, + char **VarValues, + int var_count + );) + +/**************************************************************************** +* Function : genaNotifyAllExt +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN IXML_Document *PropSet : XML document Event varible property set +* +* Description : This function sends a notification to all the subscribed +* control points +* +* Return : int +* +* Note : This function is similar to the genaNotifyAll. the only difference +* is it takes the document instead of event variable array +****************************************************************************/ +DEVICEONLY(EXTERN_C int genaNotifyAllExt(UpnpDevice_Handle device_handle, + char *UDN, char *servId,IN IXML_Document *PropSet);) + +/**************************************************************************** +* Function : genaInitNotify +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN char **VarNames : Array of variable names +* IN char **VarValues : Array of variable values +* IN int var_count : array size +* IN Upnp_SID sid : subscription ID +* +* Description : This function sends the intial state table dump to +* newly subscribed control point. +* +* Return : int +* returns GENA_E_SUCCESS if successful else returns appropriate error +* +* Note : No other event will be sent to this control point before the +* intial state table dump. +****************************************************************************/ +DEVICEONLY(EXTERN_C int genaInitNotify(IN UpnpDevice_Handle device_handle, + IN char *UDN, + IN char *servId, + IN char **VarNames, + IN char **VarValues, + IN int var_count, + IN Upnp_SID sid);) + +/**************************************************************************** +* Function : genaInitNotifyExt +* +* Parameters : +* IN UpnpDevice_Handle device_handle : Device handle +* IN char *UDN : Device udn +* IN char *servId : Service ID +* IN IXML_Document *PropSet : Document of the state table +* IN Upnp_SID sid : subscription ID +* +* Description : This function is similar to the genaInitNofity. The only +* difference is that it takes the xml document for the state table and +* sends the intial state table dump to newly subscribed control point. +* +* Return : int +* returns GENA_E_SUCCESS if successful else returns appropriate error +* +* Note : No other event will be sent to this control point before the +* intial state table dump. +****************************************************************************/ +DEVICEONLY(EXTERN_C int genaInitNotifyExt( + IN UpnpDevice_Handle device_handle, + IN char *UDN, + IN char *servId, + IN IXML_Document *PropSet, + IN Upnp_SID sid);) + + +/************************************************************************ +* Function : error_respond +* +* Parameters: +* IN SOCKINFO *info: Structure containing information about the socket +* IN int error_code: error code that will be in the GENA response +* IN http_message_t* hmsg: GENA request Packet +* +* Description: +* This function send an error message to the control point in the case +* incorrect GENA requests. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +void error_respond( IN SOCKINFO *info, IN int error_code, + IN http_message_t* hmsg ); + + +#endif // GENA + + + + + + + + + diff --git a/libupnp/upnp/src/inc/gena_ctrlpt.h b/libupnp/upnp/src/inc/gena_ctrlpt.h new file mode 100644 index 0000000..5896be2 --- /dev/null +++ b/libupnp/upnp/src/inc/gena_ctrlpt.h @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "sock.h" + +/************************************************************************ +* Function : gena_process_notification_event +* +* Parameters: +* IN SOCKINFO *info: Socket structure containing the device socket +* information +* IN http_message_t* event: The http message contains the GENA +* notification +* +* Description: +* This function processes NOTIFY events that are sent by devices. +* called by genacallback() +* +* Returns: void +* +* Note : called by genacallback() +****************************************************************************/ +void gena_process_notification_event( INOUT SOCKINFO *info, + IN http_message_t* request ); diff --git a/libupnp/upnp/src/inc/gena_device.h b/libupnp/upnp/src/inc/gena_device.h new file mode 100644 index 0000000..42077d0 --- /dev/null +++ b/libupnp/upnp/src/inc/gena_device.h @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "sock.h" + +/**************************************************************************** +* Function : gena_process_subscription_request +* +* Parameters : +* IN SOCKINFO *info : socket info of the device +* IN http_message_t* request : SUBSCRIPTION request from the control +* point +* +* Description : This function handles a subscription request from a +* ctrl point. The socket is not closed on return. +* +* Return : void +* +* Note : +****************************************************************************/ +void gena_process_subscription_request( IN SOCKINFO *info, + IN http_message_t* request ); + + +/**************************************************************************** +* Function : gena_process_subscription_renewal_request +* +* Parameters : +* IN SOCKINFO *info : socket info of the device +* IN http_message_t* request : subscription renewal request from the +* control point +* +* Description : This function handles a subscription renewal request +* from a ctrl point. The connection is not destroyed on return. +* +* Return : void +* +* Note : +****************************************************************************/ +void gena_process_subscription_renewal_request( IN SOCKINFO *info, + IN http_message_t* request ); + + +/**************************************************************************** +* Function : gena_process_unsubscribe_request +* +* Parameters : +* IN SOCKINFO *info : socket info of the device +* IN http_message_t* request : UNSUBSCRIBE request from the control +* point +* +* Description : This function Handles a subscription cancellation request +* from a ctrl point. The connection is not destroyed on return. +* +* Return : void +* +* Note : +****************************************************************************/ +void gena_process_unsubscribe_request( IN SOCKINFO *info, + IN http_message_t* request ); diff --git a/libupnp/upnp/src/inc/global.h b/libupnp/upnp/src/inc/global.h new file mode 100644 index 0000000..58d4b82 --- /dev/null +++ b/libupnp/upnp/src/inc/global.h @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +/* GLOBAL.H - RSAREF types and constants */ +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. +*/ + +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ + +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif diff --git a/libupnp/upnp/src/inc/gmtdate.h b/libupnp/upnp/src/inc/gmtdate.h new file mode 100644 index 0000000..50152bf --- /dev/null +++ b/libupnp/upnp/src/inc/gmtdate.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_UTIL_GMTDATE_H +#define GENLIB_UTIL_GMTDATE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// input: monthStr: 3-letter or full month +// returns month=0..11 or -1 on failure +// output: +// charsRead - num chars that match the month +// fullNameMatch - full name match(1) or 3-letter match(0) +// +int ParseMonth( IN const char* monthStr, + OUT int* charsRead, OUT int* fullNameMatch ); + +// input: dayOfWeek: 3-letter or full day of week ("mon" etc) +// returns dayOfWeek=0..6 or -1 on failure +// output: +// charsRead - num chars that match the month +// fullNameMatch - full name match(1) or 3-letter match(0) +// +int ParseDayOfWeek( IN const char* dayOfWeek, + OUT int* charsRead, OUT int* fullNameMatch ); + +// converts date to string format: RFC 1123 format: +// Sun, 06 Nov 1994 08:49:37 GMT +// String returned must be freed using free() function +// returns NULL if date is NULL +// +// throws OutOfMemoryException +char* DateToString( const struct tm* date ); + +// parses time in fmt hh:mm:ss, military fmt +// returns 0 on success; -1 on error +int ParseTime( const char* s, int* hour, int* minute, int* second ); + + + +// tries to parse date according to RFCs 1123, 850, or asctime() +// format +// params: +// str - contains date/time in string format +// dateTime - date and time obtained from 'str' +// returns: 0 on success, -1 on error +int ParseRFC850DateTime( IN const char* str, + OUT struct tm* dateTime, OUT int* numCharsParsed ); + +int ParseRFC1123DateTime( IN const char* str, + OUT struct tm* dateTime, OUT int* numCharsParsed ); + +int ParseAsctimeFmt( IN const char* str, + OUT struct tm* dateTime, OUT int* numCharsParsed ); + +// parses any of these formats: 1123, 850 or asctime() +int ParseDateTime( IN const char* str, + OUT struct tm* dateTime, OUT int* numCharsParsed ); + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif /* GENLIB_UTIL_GMTDATE_H */ diff --git a/libupnp/upnp/src/inc/http_client.h b/libupnp/upnp/src/inc/http_client.h new file mode 100644 index 0000000..11cad5c --- /dev/null +++ b/libupnp/upnp/src/inc/http_client.h @@ -0,0 +1,250 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _http_client_h_ +#define _http_client_h_ +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif +#include "genlib/closesocket/upnpclosesocket.h" +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tools/config.h" +#include "upnp.h" +//#include "upnp_debug.h" + + +#define HTTP_DATE_LENGTH 37 // length for HTTP DATE: + //"DATE: Sun, 01 Jul 2000 08:15:23 GMT" +#define SEPARATORS "()<>@,;:\\\"/[]?={} \t" +#define MARK "-_.!~*'()" +#define RESERVED ";/?:@&=+$," +#define HTTP_SUCCESS 1 +#define HTTP_E_BAD_URL UPNP_E_INVALID_URL +#define HTTP_E_READ_SOCKET UPNP_E_SOCKET_READ +#define HTTP_E_BIND_SOCKET UPNP_E_SOCKET_BIND +#define HTTP_E_WRITE_SOCKET UPNP_E_SOCKET_WRITE +#define HTTP_E_CONNECT_SOCKET UPNP_E_SOCKET_CONNECT +#define HTTP_E_SOCKET UPNP_E_OUTOF_SOCKET +#define HTTP_E_BAD_RESPONSE UPNP_E_BAD_RESPONSE +#define HTTP_E_BAD_REQUEST UPNP_E_BAD_REQUEST +#define HTTP_E_BAD_IP_ADDRESS UPNP_E_INVALID_URL +#define FALSE 0 +#define TAB 9 +#define CR 13 +#define LF 10 +#define RESPONSE_TIMEOUT 30 +#define SOCKET_BUFFER_SIZE 5000 + +enum hostType { HOSTNAME, IPv4address }; +enum pathType { ABS_PATH, REL_PATH, OPAQUE_PART }; +enum uriType { ABSOLUTE, RELATIVE }; + +//Buffer used to store data read from +//a socket during an http transfer +//in function read_bytes. +typedef struct SOCKET_BUFFER{ + char buff[SOCKET_BUFFER_SIZE]; + int size; + struct SOCKET_BUFFER *next; +} socket_buffer; + +//Buffer used in parsing +//http messages, urls, etc. +//generally this simply +//holds a pointer into a larger array +typedef struct TOKEN { + char * buff; + int size; +} token; + + +//Represents a host port: +//e.g. :"127.127.0.1:80" +//text is a token pointing to +//the full string representation +typedef struct HOSTPORT { + token text; //full host port + struct sockaddr_in IPv4address; //Network Byte Order +} hostport_type; + +//Represents a URI +//used in parse_uri and elsewhere +typedef struct URI{ + enum uriType type; + token scheme; + enum pathType path_type; + token pathquery; + token fragment; + hostport_type hostport; +} uri_type; + +//Represents a list of URLs as in +//the "callback" header of SUBSCRIBE +//message in GENA +//char * URLs holds dynamic memory +typedef struct URL_LIST { + int size; + char * URLs; //all the urls, delimited by <> + uri_type *parsedURLs; +} URL_list; + +typedef struct HTTP_HEADER { + token header; + token value; + struct HTTP_HEADER * next; +} http_header; + +typedef struct HTTP_STATUS_LINE{ + token http_version; + token status_code; + token reason_phrase; +} http_status; + +typedef struct HTTP_REQUEST_LINE { + token http_version; + uri_type request_uri; + token method; +} http_request; + +//Represents a parsed HTTP_MESSAGE +//head_list is dynamically allocated +typedef struct HTTP_MESSAGE { + http_status status; + http_request request; + http_header * header_list; + token content; +} http_message; + + +EXTERN_C int transferHTTP( char * request, char * toSend, + int toSendSize, char **out, char * Url); + + +EXTERN_C int transferHTTPRaw( char * toSend, int toSendSize, + char **out, char *URL); + +//helper function +EXTERN_C int transferHTTPparsedURL( char * request, + char * toSend, int toSendSize, + char **out, uri_type *URL); + +//assumes that char * out has enough space ( 38 characters) +//outputs the current time in the following null terminated string: +// "DATE: Sun, Jul 06 2000 08:53:01 GMT\r\n" +EXTERN_C void currentTmToHttpDate(char *out); + +//returns dynamic memory or NULL on error +EXTERN_C char * resolve_rel_url( char * base_url, char * rel_url); + +EXTERN_C int parse_uri( char * in, int max, uri_type * out); + +EXTERN_C int token_cmp( token *in1, token *in2); + +EXTERN_C int token_string_casecmp( token * in1, char * in2); + +EXTERN_C int token_string_cmp( token * in1, char * in2); + +EXTERN_C int parse_http_response( char * in, http_message * out, + int max_len); + +EXTERN_C int parse_http_request( char * in, http_message *out, + int max_len); + +EXTERN_C int search_for_header( http_message * in, + char * header, token *out_value); + + + +EXTERN_C int parse_hostport( char* in, int max, hostport_type *out ); + +EXTERN_C size_t write_bytes(int fd, char * bytes, size_t n, + int timeout); +EXTERN_C void free_http_message(http_message * message); +EXTERN_C int copy_URL_list( URL_list *in, URL_list *out); +EXTERN_C void free_URL_list(URL_list * list); +EXTERN_C int parse_port(int max, char * port, unsigned short int * out); + +EXTERN_C int parse_http_line( char * in, int max_size); +EXTERN_C int parse_not_LWS( char *in, token *out, int max_size); +EXTERN_C int parse_LWS( char * in, int max_size); +EXTERN_C int parse_token( char * in, token * out, int max_size); +EXTERN_C ssize_t readLine(int fd, char *out, int max, int *timeout); +EXTERN_C int remove_dots(char * in, int size); + + +DBGONLY(EXTERN_C void print_http_request(http_message + *message,Dbg_Level DLevel, + Dbg_Module Module,char *DbgFileName, + int DbgLineNo);); + +DBGONLY(EXTERN_C void print_http_response(http_message *message, + Dbg_Level DLevel, + Dbg_Module Module,char *DbgFileName, + int DbgLineNo);); + +DBGONLY(EXTERN_C void print_token( token * in, + Dbg_Level DLevel, + Dbg_Module Module, + char *DbgFileName, + int DbgLineNo);); + +DBGONLY(EXTERN_C void print_status_line(http_status *in, + Dbg_Level DLevel, + Dbg_Module Module, + char *DbgFileName, + int DbgLineNo);); + +DBGONLY(EXTERN_C void print_request_line(http_request *in, + Dbg_Level DLevel, + Dbg_Module Module, + char *DbgFileName,int DbgLineNo)); + +DBGONLY(EXTERN_C void print_uri( uri_type *in, + Dbg_Level DLevel, + Dbg_Module Module, + char *DbgFileName, + int DbgLineNo);); + +#endif diff --git a/libupnp/upnp/src/inc/httpparser.h b/libupnp/upnp/src/inc/httpparser.h new file mode 100644 index 0000000..5052aa3 --- /dev/null +++ b/libupnp/upnp/src/inc/httpparser.h @@ -0,0 +1,496 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_HTTPPARSER_H +#define GENLIB_NET_HTTP_HTTPPARSER_H + +#include "util.h" +#include "membuffer.h" +#include "uri.h" + +#include "LinkedList.h" + +////// private types //////////// + + +////////////////////// +// scanner +/////////////////////// +// Used to represent different types of tokens in input +typedef enum // token_type_t +{ + TT_IDENTIFIER, + TT_WHITESPACE, + TT_CRLF, + TT_CTRL, // needed ?? + TT_SEPARATOR, // ?? + TT_QUOTEDSTRING, // ?? +} token_type_t; + +typedef struct // scanner_t +{ + membuffer* msg; // raw http msg + size_t cursor; // current position in buffer + xboolean entire_msg_loaded; // set this to TRUE if the entire msg is loaded in + // in 'msg'; else FALSE if only partial msg in 'msg' + // (default is FALSE) +} scanner_t; + +typedef enum // parser_pos_t +{ + POS_REQUEST_LINE, + POS_RESPONSE_LINE, + POS_HEADERS, + POS_ENTITY, + POS_COMPLETE, +} parser_pos_t; + + +#define ENTREAD_DETERMINE_READ_METHOD 1 +#define ENTREAD_USING_CLEN 2 +#define ENTREAD_USING_CHUNKED 3 +#define ENTREAD_UNTIL_CLOSE 4 +#define ENTREAD_CHUNKY_BODY 5 +#define ENTREAD_CHUNKY_HEADERS 6 + + +// end of private section +////////////////// +// ################################################################################## + +// method in a HTTP request +typedef enum // http_method_t +{ + HTTPMETHOD_POST, + HTTPMETHOD_MPOST, + HTTPMETHOD_SUBSCRIBE, + HTTPMETHOD_UNSUBSCRIBE, + HTTPMETHOD_NOTIFY, + HTTPMETHOD_GET, + HTTPMETHOD_HEAD, + HTTPMETHOD_MSEARCH, + HTTPMETHOD_UNKNOWN, + SOAPMETHOD_POST, //murari + HTTPMETHOD_SIMPLEGET +} http_method_t; + +// different types of HTTP headers +#define HDR_UNKNOWN -1 +#define HDR_CACHE_CONTROL 1 +#define HDR_CALLBACK 2 +#define HDR_CONTENT_LENGTH 3 +#define HDR_CONTENT_TYPE 4 +#define HDR_DATE 5 +#define HDR_EXT 6 +#define HDR_HOST 7 +//#define HDR_IF_MODIFIED_SINCE 8 +//#define HDR_IF_UNMODIFIED_SINCE 9 +//#define HDR_LAST_MODIFIED 10 +#define HDR_LOCATION 11 +#define HDR_MAN 12 +#define HDR_MX 13 +#define HDR_NT 14 +#define HDR_NTS 15 +#define HDR_SERVER 16 +#define HDR_SEQ 17 +#define HDR_SID 18 +#define HDR_SOAPACTION 19 +#define HDR_ST 20 +#define HDR_TIMEOUT 21 +#define HDR_TRANSFER_ENCODING 22 +#define HDR_USN 23 +#define HDR_USER_AGENT 24 + +//Adding new header difinition//Beg_Murari +#define HDR_ACCEPT 25 +#define HDR_ACCEPT_ENCODING 26 +#define HDR_ACCEPT_CHARSET 27 +#define HDR_ACCEPT_LANGUAGE 28 +#define HDR_ACCEPT_RANGE 29 +#define HDR_CONTENT_ENCODING 30 +#define HDR_CONTENT_LANGUAGE 31 +#define HDR_CONTENT_LOCATION 32 +#define HDR_CONTENT_RANGE 33 +#define HDR_IF_RANGE 34 +#define HDR_RANGE 35 +#define HDR_TE 36 +//End_Murari + +// status of parsing +typedef enum // parse_status_t +{ + PARSE_SUCCESS = 0, // msg was parsed successfully + PARSE_INCOMPLETE, // need more data to continue + PARSE_INCOMPLETE_ENTITY, // for responses that don't have length specified + PARSE_FAILURE, // parse failed; check status code for details + PARSE_OK, // done partial + PARSE_NO_MATCH, // token not matched + + // private + PARSE_CONTINUE_1 +} parse_status_t; + +typedef struct // http_header_t +{ + memptr name; // header name as a string + int name_id; // header name id (for a selective group of headers only) + membuffer value; // raw-value; could be multi-lined; min-length = 0 + + // private + membuffer name_buf; +} http_header_t; + +typedef struct // http_message_t +{ + int initialized; + // request only + http_method_t method; + uri_type uri; + + // response only + http_method_t request_method; + int status_code; + membuffer status_msg; + + // fields used in both request or response messages + xboolean is_request; // if TRUE, msg is a request, else response + + int major_version; // http major.minor version + int minor_version; + + + LinkedList headers; +//NNS: dlist headers; // dlist + memptr entity; // message body(entity) + + // private fields + membuffer msg; // entire raw message + char *urlbuf; // storage for url string +} http_message_t; + +typedef struct // http_parser_t +{ + http_message_t msg; + int http_error_code; // read-only; in case of parse error, this + // contains the HTTP error code (4XX or 5XX) + + // read-only; this is set to true if a NOTIFY request has no content-length. + // used to read valid sspd notify msg. + xboolean valid_ssdp_notify_hack; + + // private data -- don't touch + parser_pos_t position; + int ent_position; + unsigned int content_length; + int chunk_size; + size_t entity_start_position; + scanner_t scanner; +} http_parser_t; + + +//-------------------------------------------------- +//////////////// functions ///////////////////////// +//-------------------------------------------------- + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +/************************************************************************ +* Function : httpmsg_init +* +* Parameters : +* INOUT http_message_t* msg ; HTTP Message Object +* +* Description : Initialize and allocate memory for http message +* +* Return : void ; +* +* Note : +************************************************************************/ +void httpmsg_init( INOUT http_message_t* msg ); + +/************************************************************************ +* Function : httpmsg_destroy +* +* Parameters : +* INOUT http_message_t* msg ; HTTP Message Object +* +* Description : Free memory allocated for the http message +* +* Return : void ; +* +* Note : +************************************************************************/ +void httpmsg_destroy( INOUT http_message_t* msg ); + +/************************************************************************ +* Function : httpmsg_find_hdr_str +* +* Parameters : +* IN http_message_t* msg ; HTTP Message Object +* IN const char* header_name ; Header name to be compared with +* +* Description : Compares the header name with the header names stored +* in the linked list of messages +* +* Return : http_header_t* - Pointer to a header on success; +* NULL on failure +* +* Note : +************************************************************************/ +http_header_t* httpmsg_find_hdr_str( IN http_message_t* msg, + IN const char* header_name ); + +/************************************************************************ +* Function : httpmsg_find_hdr +* +* Parameters : +* IN http_message_t* msg ; HTTP Message Object +* IN int header_name_id ; Header Name ID to be compared with +* OUT memptr* value ; Buffer to get the ouput to. +* +* Description : Finds header from a list, with the given 'name_id'. +* +* Return : http_header_t* - Pointer to a header on success; * +* NULL on failure +* +* Note : +************************************************************************/ +http_header_t* httpmsg_find_hdr( IN http_message_t* msg, + IN int header_name_id, OUT memptr* value ); + +/************************************************************************ +* Function: parser_request_init +* +* Parameters: +* OUT http_parser_t* parser ; HTTP Parser object +* +* Description: Initializes parser object for a request +* +* Returns: +* void +************************************************************************/ +void parser_request_init( OUT http_parser_t* parser ); + +/************************************************************************ +* Function: parser_response_init +* +* Parameters: +* OUT http_parser_t* parser ; HTTP Parser object +* IN http_method_t request_method ; Request method +* +* Description: Initializes parser object for a response +* +* Returns: +* void +************************************************************************/ +void parser_response_init( OUT http_parser_t* parser, + IN http_method_t request_method ); + +/************************************************************************ +* Function: parser_parse +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: The parser function. Depending on the position of the +* parser object the actual parsing function is invoked +* +* Returns: +* void +************************************************************************/ +parse_status_t parser_parse(INOUT http_parser_t * parser); + +/************************************************************************ +* Function: parser_parse_responseline +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Get HTTP Method, URL location and version information. +* +* Returns: +* PARSE_OK +* PARSE_SUCCESS +* PARSE_FAILURE +************************************************************************/ +parse_status_t parser_parse_responseline(INOUT http_parser_t *parser); + +/************************************************************************ +* Function: parser_parse_headers +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Get HTTP Method, URL location and version information. +* +* Returns: +* PARSE_OK +* PARSE_SUCCESS +* PARSE_FAILURE +************************************************************************/ +parse_status_t parser_parse_headers(INOUT http_parser_t *parser); + +/************************************************************************ +* Function: parser_parse_entity +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Determines method to read entity +* +* Returns: +* PARSE_OK +* PARSE_FAILURE +* PARSE_COMPLETE -- no more reading to do +************************************************************************/ +parse_status_t parser_parse_entity(INOUT http_parser_t *parser); + +/************************************************************************ +* Function: parser_get_entity_read_method +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser object +* +* Description: Determines method to read entity +* +* Returns: +* PARSE_OK +* PARSE_FAILURE +* PARSE_COMPLETE -- no more reading to do +************************************************************************/ +parse_status_t parser_get_entity_read_method( INOUT http_parser_t* parser ); + +/************************************************************************ +* Function: parser_append +* +* Parameters: +* INOUT http_parser_t* parser ; HTTP Parser Object +* IN const char* buf ; buffer to be appended to the parser +* buffer +* IN size_t buf_length ; Size of the buffer +* +* Description: The parser function. Depending on the position of the +* parser object the actual parsing function is invoked +* +* Returns: +* void +************************************************************************/ +parse_status_t parser_append( INOUT http_parser_t* parser, + IN const char* buf, + IN size_t buf_length ); + +/************************************************************************ +* Function: matchstr +* +* Parameters: +* IN char *str ; String to be matched +* IN size_t slen ; Length of the string +* IN const char* fmt ; Pattern format +* ... +* +* Description: Matches a variable parameter list with a string +* and takes actions based on the data type specified. +* +* Returns: +* PARSE_OK +* PARSE_NO_MATCH -- failure to match pattern 'fmt' +* PARSE_FAILURE -- 'str' is bad input +************************************************************************/ +int matchstr( IN char *str, IN size_t slen, IN const char* fmt, ... ); + +// ==================================================== +// misc functions + + +/************************************************************************ +* Function: raw_to_int +* +* Parameters: +* IN memptr* raw_value ; Buffer to be converted +* IN int base ; Base to use for conversion +* +* Description: Converts raw character data to long-integer value +* +* Returns: +* int +************************************************************************/ +int raw_to_int( IN memptr* raw_value, int base ); + +/************************************************************************ +* Function: raw_find_str +* +* Parameters: +* IN memptr* raw_value ; Buffer containg the string +* IN const char* str ; Substring to be found +* +* Description: Find a substring from raw character string buffer +* +* Returns: +* int - index at which the substring is found. +************************************************************************/ +int raw_find_str( IN memptr* raw_value, IN const char* str ); + +/************************************************************************ +* Function: method_to_str +* +* Parameters: +* IN http_method_t method ; HTTP method +* +* Description: A wrapper function that maps a method id to a method +* nameConverts a http_method id stored in the HTTP Method +* +* Returns: +* const char* ptr - Ptr to the HTTP Method * +************************************************************************/ +const char* method_to_str( IN http_method_t method ); + +/************************************************************************ +* Function: print_http_headers +* +* Parameters: +* http_message_t* hmsg ; HTTP Message object +* +* Description: +* +* Returns: +* void +************************************************************************/ +void print_http_headers( IN http_message_t* hmsg ); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + + +#endif // GENLIB_NET_HTTP_HTTPPARSER_H diff --git a/libupnp/upnp/src/inc/httpreadwrite.h b/libupnp/upnp/src/inc/httpreadwrite.h new file mode 100644 index 0000000..ee8b79c --- /dev/null +++ b/libupnp/upnp/src/inc/httpreadwrite.h @@ -0,0 +1,581 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_HTTPREADWRITE_H +#define GENLIB_NET_HTTP_HTTPREADWRITE_H + +#include "config.h" +#include "util.h" +#include "sock.h" +#include "httpparser.h" + +// timeout in secs +#define HTTP_DEFAULT_TIMEOUT 30 + + + +#ifdef __cplusplus +#extern "C" { +#endif + +int +http_CancelHttpGet( IN void *Handle ); + +/************************************************************************ +* Function: http_FixUrl +* +* Parameters: +* IN uri_type* url ; URL to be validated and fixed +* OUT uri_type* fixed_url ; URL after being fixed. +* +* Description: Validates URL +* +* Returns: +* UPNP_E_INVALID_URL +* UPNP_E_SUCCESS +************************************************************************/ +int http_FixUrl( IN uri_type* url, OUT uri_type* fixed_url ); + +/************************************************************************ +* Function: http_FixStrUrl +* +* Parameters: +* IN char* urlstr ; Character string as a URL +* IN int urlstrlen ; Length of the character string +* OUT uri_type* fixed_url ; Fixed and corrected URL +* +* Description: Parses URL and then validates URL +* +* Returns: +* UPNP_E_INVALID_URL +* UPNP_E_SUCCESS +************************************************************************/ +int http_FixStrUrl( IN char* urlstr, IN int urlstrlen, OUT uri_type* fixed_url ); + + +/************************************************************************ +* Function: http_Connect +* +* Parameters: +* IN uri_type* destination_url ; URL containing destination information +* OUT uri_type *url ; Fixed and corrected URL +* +* Description: Gets destination address from URL and then connects to the +* remote end +* +* Returns: +* socket descriptor on sucess +* UPNP_E_OUTOF_SOCKET +* UPNP_E_SOCKET_CONNECT on error +************************************************************************/ +int http_Connect( IN uri_type* destination_url, OUT uri_type *url ); + +/************************************************************************ +* Function: http_RecvMessage +* +* Parameters: +* IN SOCKINFO *info ; Socket information object +* OUT http_parser_t* parser, HTTP parser object +* IN http_method_t request_method ; HTTP request method +* IN OUT int* timeout_secs ; time out +* OUT int* http_error_code ; HTTP error code returned +* +* Description: Get the data on the socket and take actions based on the +* read data to modify the parser objects buffer. If an error is reported +* while parsing the data, the error code is passed in the http_errr_code +* parameter +* +* Returns: +* UPNP_E_BAD_HTTPMSG +* UPNP_E_SUCCESS +************************************************************************/ +int http_RecvMessage( IN SOCKINFO *info, OUT http_parser_t* parser, + IN http_method_t request_method, + IN OUT int* timeout_secs, + OUT int* http_error_code ); + + +/************************************************************************ +* Function: http_SendMessage +* +* Parameters: +* IN SOCKINFO *info ; Socket information object +* IN OUT int * TimeOut ; time out value +* IN const char* fmt, ... Pattern format to take actions upon +* +* Description: Sends a message to the destination based on the +* IN const char* fmt parameter +* fmt types: +* 'f': arg = const char * file name +* 'm': arg1 = const char * mem_buffer; arg2= size_t buf_length +* E.g.: +* char *buf = "POST /xyz.cgi http/1.1\r\n\r\n"; +* char *filename = "foo.dat"; +* int status = http_SendMessage( tcpsock, "mf", +* buf, strlen(buf), // args for memory buffer +* filename ); // arg for file +* +* Returns: +* UPNP_E_OUTOF_MEMORY +* UPNP_E_FILE_READ_ERROR +* UPNP_E_SUCCESS +************************************************************************/ +int http_SendMessage( IN SOCKINFO *info, IN OUT int* timeout_secs, + IN const char* fmt, ... ); + +/************************************************************************ +* Function: http_RequestAndResponse +* +* Parameters: +* IN uri_type* destination ; Destination URI object which contains +* remote IP address among other elements +* IN const char* request ; Request to be sent +* IN size_t request_length ; Length of the request +* IN http_method_t req_method ; HTTP Request method +* IN int timeout_secs ; time out value +* OUT http_parser_t* response ; Parser object to receive the repsonse +* +* Description: Initiates socket, connects to the destination, sends a +* request and waits for the response from the remote end +* +* Returns: +* UPNP_E_SOCKET_ERROR +* UPNP_E_SOCKET_CONNECT +* Error Codes returned by http_SendMessage +* Error Codes returned by http_RecvMessage +************************************************************************/ +int http_RequestAndResponse( IN uri_type* destination, + IN const char* request, IN size_t request_length, + IN http_method_t req_method, + IN int timeout_secs, + OUT http_parser_t* response ); + +/************************************************************************ +* return codes: +* 0 -- success +* UPNP_E_OUTOF_MEMORY +* UPNP_E_TIMEDOUT +* UPNP_E_BAD_REQUEST +* UPNP_E_BAD_RESPONSE +* UPNP_E_INVALID_URL +* UPNP_E_SOCKET_READ +* UPNP_E_SOCKET_WRITE +************************************************************************/ + +/************************************************************************ +* Function : http_Download +* +* Parameters : +* IN const char* url_str : String as a URL +* IN int timeout_secs : time out value +* OUT char** document : buffer to store the document extracted +* from the donloaded message. +* OUT int* doc_length : length of the extracted document +* OUT char* content_type : Type of content +* +* Description : Download the document message and extract the document +* from the message. +* +* Return : int; +* UPNP_E_SUCCESS; +* UPNP_E_INVALID_URL; +* +* +* Note : +************************************************************************/ +int http_Download( IN const char* url, + IN int timeout_secs, + OUT char** document, OUT int* doc_length, + OUT char* content_type ); + + +/************************************************************************ +* Function : http_WriteHttpPost +* +* Parameters : +* IN void *Handle : Handle to the http post object +* IN char *buf : Buffer to send to peer, if format used +* is not UPNP_USING_CHUNKED, +* IN unsigned int *size : Size of the data to be sent. +* IN int timeout : time out value +* +* Description : Formats data if format used is UPNP_USING_CHUNKED. +* Writes data on the socket connected to the peer. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_INVALID_PARAM - Invalid Parameter +* -1 - On Socket Error. +* +* Note : +************************************************************************/ +int http_WriteHttpPost(IN void *Handle, + IN char *buf, + IN unsigned int *size, + IN int timeout); + +/************************************************************************ +* Function : http_CloseHttpPost +* +* Parameters : +* IN void *Handle : Handle to the http post object +* IN OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Sends remaining data if using UPNP_USING_CHUNKED +* format. Receives any more messages. Destroys socket and any socket +* associated memory. Frees handle associated with the HTTP POST msg. +* +* Return : int ; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Parameter; +* +* Note : +************************************************************************/ +int http_CloseHttpPost(IN void *Handle, + IN OUT int *httpStatus, + IN int timeout); + +/************************************************************************ +* Function : http_OpenHttpPost +* +* Parameters : +* IN const char *url_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN const char *contentType : Type of content +* IN int contentLength : length of content +* IN int timeout : time out value +* +* Description : Makes the HTTP POST message, connects to the peer, +* sends the HTTP POST request. Adds the post handle to buffer of +* such handles +* +* Return : int; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Paramter ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_SOCKET_CONNECT ; +* +* Note : +************************************************************************/ +int http_OpenHttpPost(IN const char *url_str, + IN OUT void **Handle, + IN const char *contentType, + IN int contentLength, + IN int timeout); + +/************************************************************************ +* Function : http_ReadHttpGet +* +* Parameters : +* IN void *Handle : Handle to the HTTP get object +* IN OUT char *buf : Buffer to get the read and parsed data +* IN OUT unsigned int *size : Size of tge buffer passed +* IN int timeout : time out value +* +* Description : Parses already existing data, then gets new data. +* Parses and extracts information from the new data. +* +* Return : int ; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Parameter; +* UPNP_E_BAD_RESPONSE ; +* UPNP_E_BAD_HTTPMSG ; +* +* Note : +************************************************************************/ +int http_ReadHttpGet(IN void *Handle, + IN OUT char *buf, + IN OUT unsigned int *size, + IN int timeout); + +/************************************************************************ +* Function : http_HttpGetProgress +* +* Parameters : +* IN void *Handle : Handle to the HTTP get object +* OUT unsigned int *length : Buffer to get the read and parsed data +* OUT unsigned int *total : Size of tge buffer passed +* +* Description : Extracts information from the Handle to the HTTP get +* object. +* +* Return : int ; +* UPNP_E_SUCCESS - On Sucess ; +* UPNP_E_INVALID_PARAM - Invalid Parameter; +* +* Note : +************************************************************************/ +int http_HttpGetProgress(IN void *Handle, + OUT unsigned int *length, + OUT unsigned int *total ); + +/************************************************************************ +* Function : http_CloseHttpGet +* +* Parameters : +* IN void *Handle ; Handle to HTTP get object +* +* Description : Clears the handle allocated for the HTTP GET operation +* Clears socket states and memory allocated for socket operations. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_INVALID_PARAM - Invalid Parameter +* +* Note : +************************************************************************/ +int http_CloseHttpGet(IN void *Handle); + +/************************************************************************ +* Function : http_OpenHttpGet +* +* Parameters : +* IN const char *url_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN OUT char **contentType : Type of content +* OUT int *contentLength : length of content +* OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Makes the HTTP GET message, connects to the peer, +* sends the HTTP GET request, gets the response and parses the +* response. +* +* Return : int; +* UPNP_E_SUCCESS - On Success ; +* UPNP_E_INVALID_PARAM - Invalid Paramters ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_BAD_RESPONSE ; +* +* Note : +* +************************************************************************/ +int http_OpenHttpGet(IN const char *url_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int timeout); + +/************************************************************************ +* Function : http_OpenHttpGetProxy +* +* Parameters : +* IN const char *url_str : String as a URL +* IN const char *proxy_str : String as a URL to the proxy +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN OUT char **contentType : Type of content +* OUT int *contentLength : length of content +* OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Makes the HTTP GET message, connects to the peer, +* sends the HTTP GET request, gets the response and parses the +* response. +* +* Return : int; +* UPNP_E_SUCCESS - On Success ; +* UPNP_E_INVALID_PARAM - Invalid Paramters ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_BAD_RESPONSE ; +* +* Note : +* +************************************************************************/ +int http_OpenHttpGetProxy(IN const char *url_str, + IN const char *proxy_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int timeout); + + +/************************************************************************ +* Function : http_SendStatusResponse +* +* Parameters : +* IN SOCKINFO *info : Socket information object +* IN int http_status_code : error code returned while making +* or sending the response message +* IN int request_major_version : request major version +* IN int request_minor_version : request minor version +* +* Description : Generate a response message for the status query and +* send the status response. +* +* Return : int; +* 0 -- success +* UPNP_E_OUTOF_MEMORY +* UPNP_E_SOCKET_WRITE +* UPNP_E_TIMEDOUT +* +* Note : +************************************************************************/ +int http_SendStatusResponse( IN SOCKINFO *info, IN int http_status_code, + IN int request_major_version, + IN int request_minor_version ); + + +/************************************************************************ +* Function : http_MakeMessage +* +* Parameters : +* INOUT membuffer* buf : buffer with the contents of the +* message +* IN int http_major_version : HTTP major version +* IN int http_minor_version : HTTP minor version +* IN const char* fmt : Pattern format +* ... : +* +* Description : Generate an HTTP message based on the format that is +* specified in the input parameters. +* +* fmt types: +* 's': arg = const char* C_string +* 'b': arg1 = const char* buf; arg2 = size_t buf_length +* memory ptr +* 'c': (no args) appends CRLF "\r\n" +* 'd': arg = int number // appends decimal number +* 't': arg = time_t * gmt_time // appends time in RFC 1123 fmt +* 'D': (no args) appends HTTP DATE: header +* 'S': (no args) appends HTTP SERVER: header +* 'U': (no args) appends HTTP USER-AGENT: header +* 'C': (no args) appends a HTTP CONNECTION: close header +* depending on major,minor version +* 'N': arg1 = int content_length // content-length header +* 'Q': arg1 = http_method_t; arg2 = char* url; +* arg3 = int url_length // start line of request +* 'R': arg = int status_code // adds a response start line +* 'B': arg = int status_code +* appends content-length, content-type and HTML body for given code +* 'T': arg = char * content_type; format e.g: "text/html"; +* content-type header +* +* Return : int; +* 0 - On Success +* UPNP_E_OUTOF_MEMORY +* UPNP_E_INVALID_URL; +* +* Note : +************************************************************************/ +int http_MakeMessage( INOUT membuffer* buf, + IN int http_major_version, + IN int http_minor_version, + IN const char* fmt, ... ); + +/************************************************************************ +* Function : http_CalcResponseVersion +* +* Parameters : +* IN int request_major_vers : Request major version +* IN int request_minor_vers : Request minor version +* OUT int* response_major_vers : Response mojor version +* OUT int* response_minor_vers : Response minor version +* +* Description : Calculate HTTP response versions based on the request +* versions. +* +* Return : void +* +* Note : +************************************************************************/ +void http_CalcResponseVersion( + IN int request_major_vers, IN int request_minor_vers, + OUT int* response_major_vers, OUT int* response_minor_vers ); + +/************************************************************************ +* Function : http_OpenHttpGetEx +* +* Parameters : +* IN const char *url_str : String as a URL +* IN OUT void **Handle : Pointer to buffer to store HTTP +* post handle +* IN OUT char **contentType : Type of content +* OUT int *contentLength : length of content +* OUT int *httpStatus : HTTP status returned on receiving a +* response message +* IN int timeout : time out value +* +* Description : Makes the HTTP GET message, connects to the peer, +* sends the HTTP GET request, gets the response and parses the +* response. +* +* Return : int; +* UPNP_E_SUCCESS - On Success ; +* UPNP_E_INVALID_PARAM - Invalid Paramters ; +* UPNP_E_OUTOF_MEMORY ; +* UPNP_E_SOCKET_ERROR ; +* UPNP_E_BAD_RESPONSE ; +* +* Note : +* +************************************************************************/ +int http_OpenHttpGetEx(IN const char *url_str, + IN OUT void **Handle, + IN OUT char **contentType, + OUT int *contentLength, + OUT int *httpStatus, + IN int lowRange, + IN int highRange, + IN int timeout); + +/************************************************************************ +* Function : get_sdk_info +* +* Parameters : +* OUT char *info ; +* +* Description : Returns the server information for the operating +* system +* +* Return : void ; +* +* Note : +************************************************************************/ +void get_sdk_info( OUT char *info ); + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // GENLIB_NET_HTTP_HTTPREADWRITE_H diff --git a/libupnp/upnp/src/inc/md5.h b/libupnp/upnp/src/inc/md5.h new file mode 100644 index 0000000..c87c8a4 --- /dev/null +++ b/libupnp/upnp/src/inc/md5.h @@ -0,0 +1,39 @@ +/* MD5.H - header file for MD5C.C */ + + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. +*/ + + +/* MD5 context. */ +typedef struct { + + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ + +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + diff --git a/libupnp/upnp/src/inc/membuffer.h b/libupnp/upnp/src/inc/membuffer.h new file mode 100644 index 0000000..c9a61c9 --- /dev/null +++ b/libupnp/upnp/src/inc/membuffer.h @@ -0,0 +1,345 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_UTIL_MEMBUFFER_H +#define GENLIB_UTIL_MEMBUFFER_H + + +#include +#include "util.h" + +#define MINVAL( a, b ) ( (a) < (b) ? (a) : (b) ) +#define MAXVAL( a, b ) ( (a) > (b) ? (a) : (b) ) + +// pointer to a chunk of memory +typedef struct // memptr +{ + char *buf; // start of memory (read/write) + size_t length; // length of memory (read-only) +} memptr; + + +// maintains a block of dynamically allocated memory +// note: Total length/capacity should not exceed MAX_INT +typedef struct // membuffer +{ + char *buf; // mem buffer; must not write + // beyond buf[length-1] (read/write) + size_t length; // length of buffer (read-only) + size_t capacity; // total allocated memory (read-only) + size_t size_inc; // used to increase size; MUST be > 0; (read/write) + + // default value of size_inc + #define MEMBUF_DEF_SIZE_INC 5 +} membuffer; + +//-------------------------------------------------- +//////////////// functions ///////////////////////// +//-------------------------------------------------- + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/************************************************************************ +* Function : str_alloc +* +* Parameters : +* IN const char* str ; input string object +* IN size_t str_len ; input string length +* +* Description : Allocate memory and copy information from the input +* string to the newly allocated memory. +* +* Return : char* ; +* Pointer to the newly allocated memory. +* NULL if memory cannot be allocated. +* +* Note : +************************************************************************/ +char* str_alloc( IN const char* str, IN size_t str_len ); + +/************************************************************************ +* Function : memptr_cmp +* +* Parameters : +* IN memptr* m ; input memory object +* IN const char* s ; constatnt string for the memory object to be +* compared with +* +* Description : Compares characters of strings passed for number of +* bytes. If equal for the number of bytes, the length of the bytes +* determines which buffer is shorter. +* +* Return : int ; +* < 0 string1 substring less than string2 substring +* 0 string1 substring identical to string2 substring +* > 0 string1 substring greater than string2 substring +* +* Note : +************************************************************************/ +int memptr_cmp( IN memptr* m, IN const char* s ); + +/************************************************************************ +* Function : memptr_cmp_nocase +* +* Parameters : +* IN memptr* m ; input memory object +* IN const char* s ; constatnt string for the memory object to be +* compared with +* +* Description : Compares characters of 2 strings irrespective of the +* case for a specific count of bytes If the character comparison +* is the same the length of the 2 srings determines the shorter +* of the 2 strings. +* +* Return : int ; +* < 0 string1 substring less than string2 substring +* 0 string1 substring identical to string2 substring +* > 0 string1 substring greater than string2 substring +* +* Note : +************************************************************************/ +int memptr_cmp_nocase( IN memptr* m, IN const char* s ); + + +/************************************************************************ +* Function : membuffer_set_size +* +* Parameters : +* INOUT membuffer* m ; buffer whose size is to be modified +* IN size_t new_length ; new size to which the buffer will be +* modified +* +* Description : Increases or decreases buffer cap so that at least +* 'new_length' bytes can be stored +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - On failure to allocate memory. +* +* Note : +************************************************************************/ +int membuffer_set_size( INOUT membuffer* m, + IN size_t new_length ); + +/************************************************************************ +* Function : membuffer_init +* +* Parameters : +* INOUT membuffer* m ; buffer to be initialized +* +* Description : Wrapper to membuffer_initialize(). +* Set the size of the buffer to MEMBUF_DEF_SIZE_INC +* Initializes m->buf to NULL, length=0 +* +* Return : void ; +* +* Note : +************************************************************************/ +void membuffer_init( INOUT membuffer* m ); + +/************************************************************************ +* Function : membuffer_destroy +* +* Parameters : +* INOUT membuffer* m ; buffer to be destroyed +* +* Description : Free's memory allocated for membuffer* m. +* +* Return : void ; +* +* Note : +************************************************************************/ +void membuffer_destroy( INOUT membuffer* m ); + + +/************************************************************************ +* Function : membuffer_assign +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory is to be allocated and +* assigned. +* IN const void* buf ; source buffer whose contents will be copied +* IN size_t buf_len ; length of the source buffer +* +* Description : Allocate memory to membuffer* m and copy the contents +* of the in parameter IN const void* buf. +* +* Return : int ; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* +* Note : +************************************************************************/ +int membuffer_assign( INOUT membuffer* m, IN const void* buf, + IN size_t buf_len ); + +/************************************************************************ +* Function : membuffer_assign_str +* +* Parameters : +* INOUT membuffer* m ; buffer to be allocated and assigned +* IN const char* c_str ; source buffer whose contents will be +* copied +* +* Description : Wrapper function for membuffer_assign() +* +* Return : int ; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* +* Note : +************************************************************************/ +int membuffer_assign_str( INOUT membuffer* m, IN const char* c_str ); + +/************************************************************************ +* Function : membuffer_append +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory is to be appended. +* IN const void* buf ; source buffer whose contents will be +* copied +* IN size_t buf_len ; length of the source buffer +* +* Description : Invokes function to appends data from a constant buffer +* to the buffer +* +* Return : int ; +* +* Note : +************************************************************************/ +int membuffer_append( INOUT membuffer* m, IN const void* buf, + IN size_t buf_len ); + +/************************************************************************ +* Function : membuffer_append_str +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory is to be appended. +* IN const char* c_str ; source buffer whose contents will be +* copied +* +* Description : Invokes function to appends data from a constant string +* to the buffer +* +* Return : int ; +* +* Note : +************************************************************************/ +int membuffer_append_str( INOUT membuffer* m, IN const char* c_str ); + +/************************************************************************ +* Function : membuffer_insert +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory size is to be increased +* and appended. +* IN const void* buf ; source buffer whose contents will be +* copied +* IN size_t buf_len ; size of the source buffer +* int index ; index to determine the bounds while movinf the data +* +* Description : Allocates memory for the new data to be inserted. Does +* memory management by moving the data from the existing memory to +* the newly allocated memory and then appending the new data. +* +* Return : int ; +* +* Note : +************************************************************************/ +int membuffer_insert( INOUT membuffer* m, IN const void* buf, + IN size_t buf_len, int index ); + + +/************************************************************************ +* Function : membuffer_delete +* +* Parameters : +* INOUT membuffer* m ; buffer whose memory size is to be decreased +* and copied to the odified location +* IN int index ; index to determine bounds while moving data +* IN size_t num_bytes ; number of bytes that the data needs to +* shrink by +* +* Description : Shrink the size of the buffer depending on the current +* size of the bufer and te input parameters. Move contents from the +* old buffer to the new sized buffer. +* +* Return : void ; +* +* Note : +************************************************************************/ +void membuffer_delete( INOUT membuffer* m, IN int index, + IN size_t num_bytes ); + + +/************************************************************************ +* Function : membuffer_detach +* +* Parameters : +* INOUT membuffer* m ; buffer to be returned and updated. +* +* Description : Detaches current buffer and returns it. The caller +* must free the returned buffer using free(). +* After this call, length becomes 0. +* +* Return : char* ; +* a pointer to the current buffer +* +* Note : +************************************************************************/ +char* membuffer_detach( INOUT membuffer* m ); + +/************************************************************************ +* Function : membuffer_attach +* +* Parameters : +* INOUT membuffer* m ; buffer to be updated +* IN char* new_buf ; source buffer which will be assigned to the +* buffer to be updated +* IN size_t buf_len ; length of the source buffer +* +* Description : Free existing memory in membuffer and assign the new +* buffer in its place. +* +* Return : void ; +* +* Note : 'new_buf' must be allocted using malloc or realloc so +* that it can be freed using free() +************************************************************************/ +void membuffer_attach( INOUT membuffer* m, IN char* new_buf, + IN size_t buf_len ); +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // GENLIB_UTIL_H diff --git a/libupnp/upnp/src/inc/miniserver.h b/libupnp/upnp/src/inc/miniserver.h new file mode 100644 index 0000000..e546da8 --- /dev/null +++ b/libupnp/upnp/src/inc/miniserver.h @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef MINISERVER_H +#define MINISERVER_H + +#include "sock.h" +#include "httpparser.h" + +extern SOCKET gMiniServerStopSock; + +typedef struct MServerSockArray { + int miniServerSock; //socket for listening for miniserver + //requests + int miniServerStopSock; //socket for stopping miniserver + int ssdpSock; //socket for incoming advertisments and search requests + + int stopPort; + int miniServerPort; + + CLIENTONLY(int ssdpReqSock;) //socket for sending search + //requests and receiving + // search replies + +} MiniServerSockArray; + +//typedef void (*MiniServerCallback) ( const char* document, int sockfd ); + +typedef void (*MiniServerCallback) ( IN http_parser_t *parser, + IN http_message_t* request, + IN SOCKINFO *info ); + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************ +* Function : SetHTTPGetCallback +* +* Parameters : +* MiniServerCallback callback ; - HTTP Callback to be invoked +* +* Description : Set HTTP Get Callback +* +* Return : void +* +* Note : +************************************************************************/ +void SetHTTPGetCallback( MiniServerCallback callback ); + +/************************************************************************ +* Function : SetSoapCallback +* +* Parameters : +* MiniServerCallback callback ; - SOAP Callback to be invoked +* +* Description : Set SOAP Callback +* +* Return : void +* +* Note : +************************************************************************/ +void SetSoapCallback( MiniServerCallback callback ); + +/************************************************************************ +* Function : SetGenaCallback +* +* Parameters : +* MiniServerCallback callback ; - GENA Callback to be invoked +* +* Description : Set GENA Callback +* +* Return : void +* +* Note : +************************************************************************/ +void SetGenaCallback( MiniServerCallback callback ); + +/************************************************************************ +* Function : StartMiniServer +* +* Parameters : +* unsigned short listen_port ; Port on which the server listens for +* incoming connections +* +* Description : Initialize the sockets functionality for the +* Miniserver. Initialize a thread pool job to run the MiniServer +* and the job to the thread pool. If listen port is 0, port is +* dynamically picked +* +* Use timer mechanism to start the MiniServer, failure to meet the +* allowed delay aborts the attempt to launch the MiniServer. +* +* Return : int ; +* Actual port socket is bound to - On Success: +* A negative number UPNP_E_XXX - On Error +* Note : +************************************************************************/ +int StartMiniServer( unsigned short listen_port ); + +/************************************************************************ +* Function : StopMiniServer +* +* Parameters : +* void ; +* +* Description : Stop and Shutdown the MiniServer and free socket +* resources. +* +* Return : int ; +* Always returns 0 +* +* Note : +************************************************************************/ +int StopMiniServer( void ); + + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif /* MINISERVER_H */ diff --git a/libupnp/upnp/src/inc/netall.h b/libupnp/upnp/src/inc/netall.h new file mode 100644 index 0000000..d1ea4f0 --- /dev/null +++ b/libupnp/upnp/src/inc/netall.h @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include diff --git a/libupnp/upnp/src/inc/parsetools.h b/libupnp/upnp/src/inc/parsetools.h new file mode 100644 index 0000000..2f37285 --- /dev/null +++ b/libupnp/upnp/src/inc/parsetools.h @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_PARSETOOLS_H +#define GENLIB_NET_HTTP_PARSETOOLS_H + +#include "util.h" +#include "httpparser.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************ +* Function: has_xml_content_type +* +* Parameters: +* IN http_message_t* hmsg ; HTTP Message object +* +* Description: Find the header from the HTTP message and match the +* header for xml data. +* +* Returns: +* BOOLEAN +************************************************************************/ +xboolean has_xml_content_type( IN http_message_t* hmsg ); + +#ifdef __cplusplus +} // extern C +#endif + + +#endif // GENLIB_NET_HTTP_PARSETOOLS_H diff --git a/libupnp/upnp/src/inc/server.h b/libupnp/upnp/src/inc/server.h new file mode 100644 index 0000000..cb57eee --- /dev/null +++ b/libupnp/upnp/src/inc/server.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_SERVER_H +#define GENLIB_NET_HTTP_SERVER_H + +#ifdef __cplusplus + +#include + +int http_ServerCallback( IN HttpMessage& request, IN int sockfd ); + +// adds 'entity' to the alias list; the entity is referred using +// +// aliasRelURL: relative url for given entity +// entity: entity to be served +// actualAlias: [possibly] modified version of aliasResURL to resolve conflicts +// returns: +// 0 : success +// HTTP_E_OUT_OF_MEMORY +int http_AddAlias( IN const char* aliasRelURL, IN HttpEntity* entity, + OUT xstring& actualAlias ); + +extern "C" { +#endif /* __cplusplus */ + +void http_OldServerCallback( IN const char* msg, int sockfd ); + +void http_SetRootDir( const char* httpRootDir ); + +// removes a previously added entity +// returns: +// 0: success -- alias removed +// -1: alias not found +int http_RemoveAlias( IN const char* alias ); + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif /* GENLIB_NET_HTTP_SERVER_H */ diff --git a/libupnp/upnp/src/inc/service_table.h b/libupnp/upnp/src/inc/service_table.h new file mode 100644 index 0000000..fb5062d --- /dev/null +++ b/libupnp/upnp/src/inc/service_table.h @@ -0,0 +1,460 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _SERVICE_TABLE +#define _SERVICE_TABLE + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" +#include "uri.h" +#include "ixml.h" + +#include "upnp.h" +#include +//#include +#include + +#define SID_SIZE 41 + +DEVICEONLY( + +typedef struct SUBSCRIPTION { + Upnp_SID sid; + int eventKey; + int ToSendEventKey; + time_t expireTime; + int active; + URL_list DeliveryURLs; + struct SUBSCRIPTION *next; +} subscription; + + +typedef struct SERVICE_INFO { + DOMString serviceType; + DOMString serviceId; + char *SCPDURL ; + char *controlURL; + char *eventURL; + DOMString UDN; + int active; + int TotalSubscriptions; + subscription *subscriptionList; + struct SERVICE_INFO *next; +} service_info; + +typedef struct SERVICE_TABLE { + DOMString URLBase; + service_info *serviceList; + service_info *endServiceList; +} service_table; + + +/* Functions for Subscriptions */ + +/************************************************************************ +* Function : copy_subscription +* +* Parameters : +* subscription *in ; Source subscription +* subscription *out ; Destination subscription +* +* Description : Makes a copy of the subscription +* +* Return : int ; +* HTTP_SUCCESS - On Sucess +* +* Note : +************************************************************************/ +int copy_subscription(subscription *in, subscription *out); + +/************************************************************************ +* Function : RemoveSubscriptionSID +* +* Parameters : +* Upnp_SID sid ; subscription ID +* service_info * service ; service object providing the list of +* subscriptions +* +* Description : Remove the subscription represented by the +* const Upnp_SID sid parameter from the service table and update +* the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void RemoveSubscriptionSID(Upnp_SID sid, service_info * service); + +/************************************************************************ +* Function : GetSubscriptionSID +* +* Parameters : +* Upnp_SID sid ; subscription ID +* service_info * service ; service object providing the list of +* subscriptions +* +* Description : Return the subscription from the service table +* that matches const Upnp_SID sid value. +* +* Return : subscription * - Pointer to the matching subscription +* node; +* +* Note : +************************************************************************/ +subscription * GetSubscriptionSID(Upnp_SID sid,service_info * service); + +//returns a pointer to the subscription with the SID, NULL if not found + +subscription * CheckSubscriptionSID(Upnp_SID sid,service_info * service); + +//returns a pointer to the first subscription +subscription * GetFirstSubscription(service_info *service); + +/************************************************************************ +* Function : GetNextSubscription +* +* Parameters : +* service_info * service ; service object providing the list of +* subscriptions +* subscription *current ; current subscription object +* +* Description : Get current and valid subscription from the service +* table. +* +* Return : subscription * - Pointer to the next subscription node; +* +* Note : +************************************************************************/ +subscription * GetNextSubscription(service_info * service, subscription *current); + +/************************************************************************ +* Function : freeSubscription +* +* Parameters : +* subscription * sub ; subscription to be freed +* +* Description : Free's the memory allocated for storing the URL of +* the subscription. +* +* Return : void ; +* +* Note : +************************************************************************/ +void freeSubscription(subscription * sub); + +/************************************************************************ +* Function : freeSubscriptionList +* +* Parameters : +* subscription * head ; head of the subscription list +* +* Description : Free's memory allocated for all the subscriptions +* in the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void freeSubscriptionList(subscription * head); + +/************************************************************************ +* Function : FindServiceId +* +* Parameters : +* service_table *table ; service table +* const char * serviceId ;string representing the service id +* to be found among those in the table +* const char * UDN ; string representing the UDN +* to be found among those in the table +* +* Description : Traverses through the service table and returns a +* pointer to the service node that matches a known service id +* and a known UDN +* +* Return : service_info * - pointer to the matching service_info node; +* +* Note : +************************************************************************/ +service_info *FindServiceId( service_table * table, + const char * serviceId, const char * UDN); + +/************************************************************************ +* Function : FindServiceEventURLPath +* +* Parameters : +* service_table *table ; service table +* char * eventURLPath ; event URL path used to find a service +* from the table +* +* Description : Traverses the service table and finds the node whose +* event URL Path matches a know value +* +* Return : service_info * - pointer to the service list node from the +* service table whose event URL matches a known event URL; +* +* Note : +************************************************************************/ +service_info * FindServiceEventURLPath( service_table *table, + char * eventURLPath + ); + +/************************************************************************ +* Function : FindServiceControlURLPath +* +* Parameters : +* service_table * table ; service table +* char * controlURLPath ; control URL path used to find a service +* from the table +* +* Description : Traverses the service table and finds the node whose +* control URL Path matches a know value +* +* Return : service_info * - pointer to the service list node from the +* service table whose control URL Path matches a known value; +* +* Note : +************************************************************************/ +service_info * FindServiceControlURLPath( service_table *table, + char * controlURLPath); + +/************************************************************************ +* Function : printService +* +* Parameters : +* service_info *service ;Service whose information is to be printed +* Dbg_Level level ; Debug level specified to the print function +* Dbg_Module module ; Debug module specified to the print function +* +* Description : For debugging purposes prints information from the +* service passed into the function. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY(void printService(service_info *service,Dbg_Level + level, + Dbg_Module module)); + +/************************************************************************ +* Function : printServiceList +* +* Parameters : +* service_info *service ; Service whose information is to be printed +* Dbg_Level level ; Debug level specified to the print function +* Dbg_Module module ; Debug module specified to the print function +* +* Description : For debugging purposes prints information of each +* service from the service table passed into the function. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY(void printServiceList(service_info *service, + Dbg_Level level, Dbg_Module module)); + +/************************************************************************ +* Function : printServiceTable +* +* Parameters : +* service_table * table ; Service table to be printed +* Dbg_Level level ; Debug level specified to the print function +* Dbg_Module module ; Debug module specified to the print function +* +* Description : For debugging purposes prints the URL base of the table +* and information of each service from the service table passed into +* the function. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY(void printServiceTable(service_table * + table,Dbg_Level + level,Dbg_Module module)); + +/************************************************************************ +* Function : freeService +* +* Parameters : +* service_info *in ; service information that is to be freed +* +* Description : Free's memory allocated for the various components +* of the service entry in the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void freeService(service_info * in); + +/************************************************************************ +* Function : freeServiceList +* +* Parameters : +* service_info * head ; Head of the service list to be freed +* +* Description : Free's memory allocated for the various components +* of each service entry in the service table. +* +* Return : void ; +* +* Note : +************************************************************************/ +void freeServiceList(service_info * head); + + +/************************************************************************ +* Function : freeServiceTable +* +* Parameters : +* service_table * table ; Service table whose memory needs to be +* freed +* +* Description : Free's dynamic memory in table. +* (does not free table, only memory within the structure) +* +* Return : void ; +* +* Note : +************************************************************************/ +void freeServiceTable(service_table * table); + +/************************************************************************ +* Function : removeServiceTable +* +* Parameters : +* IXML_Node *node ; XML node information +* service_table *in ; service table from which services will be +* removed +* +* Description : This function assumes that services for a particular +* root device are placed linearly in the service table, and in the +* order in which they are found in the description document +* all services for this root device are removed from the list +* +* Return : int ; +* +* Note : +************************************************************************/ +int removeServiceTable(IXML_Node *node, + service_table *in); + + +/************************************************************************ +* Function : addServiceTable +* +* Parameters : +* IXML_Node *node ; XML node information +* service_table *in ; service table that will be initialized with +* services +* const char *DefaultURLBase ; Default base URL on which the URL +* will be returned to the service list. +* +* Description : Add Service to the table. +* +* Return : int ; +* +* Note : +************************************************************************/ +int addServiceTable(IXML_Node *node, service_table *in, const char *DefaultURLBase); + +/************************************************************************ +* Function : getServiceTable +* +* Parameters : +* IXML_Node *node ; XML node information +* service_table *out ; output parameter which will contain the +* service list and URL +* const char *DefaultURLBase ; Default base URL on which the URL +* will be returned. +* +* Description : Retrieve service from the table +* +* Return : int ; +* +* Note : +************************************************************************/ +int getServiceTable(IXML_Node *node, service_table * out, const char * DefaultURLBase); + + +/* Misc helper functions */ + + +/************************************************************************ +* Function : getElementValue +* +* Parameters : +* IXML_Node *node ; Input node which provides the list of child +* nodes +* +* Description : Returns the clone of the element value +* +* Return : DOMString ; +* +* Note : value must be freed with DOMString_free +************************************************************************/ +DOMString getElementValue(IXML_Node *node); + +/************************************************************************ +* Function : getSubElement +* +* Parameters : +* const char *element_name ; sub element name to be searched for +* IXML_Node *node ; Input node which provides the list of child +* nodes +* IXML_Node **out ; Ouput node to which the matched child node is +* returned. +* +* Description : Traverses through a list of XML nodes to find the +* node with the known element name. +* +* Return : int ; +* 1 - On Success +* 0 - On Failure +* +* Note : +************************************************************************/ +int getSubElement(const char *element_name, IXML_Node *node, + IXML_Node **out); + + +) /* DEVICEONLY */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SERVICE_TABLE */ diff --git a/libupnp/upnp/src/inc/soaplib.h b/libupnp/upnp/src/inc/soaplib.h new file mode 100644 index 0000000..71fc7cb --- /dev/null +++ b/libupnp/upnp/src/inc/soaplib.h @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef SOAPLIB_H +#define SOAPLIB_H + + +//SOAP module API to be called in Upnp-Dk API +/**************************************************************************** +* Function : soap_device_callback +* +* Parameters : +* IN http_parser_t *parser : Parsed request received by the device +* IN http_message_t* request : HTTP request +* INOUT SOCKINFO *info : socket info +* +* Description : 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 +* +* Return : void +* +* Note : +****************************************************************************/ +void soap_device_callback( + IN http_parser_t *parser, + IN http_message_t* request, + INOUT SOCKINFO *info ); + + +/**************************************************************************** +* Function : SoapSendAction +* +* Parameters : +* IN char* action_url : device contrl URL +* IN char *service_type : device service type +* IN IXML_Document *action_node : SOAP action node +* OUT IXML_Document **response_node : SOAP response node +* +* Description : This function is called by UPnP API to send the SOAP +* action request and waits till it gets the response from the device +* pass the response to the API layer +* +* Return : int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +int SoapSendAction( + IN char* action_url, + IN char *service_type, + IN IXML_Document *action_node, + OUT IXML_Document **response_node ); + +/**************************************************************************** +* Function : SoapSendActionEx +* +* Parameters : +* IN char* action_url : device contrl URL +* IN char *service_type : device service type + IN IXML_Document *Header: Soap header +* IN IXML_Document *action_node : SOAP action node ( SOAP body) +* OUT IXML_Document **response_node : SOAP response node +* +* Description : This function is called by UPnP API to send the SOAP +* action request and waits till it gets the response from the device +* pass the response to the API layer. This action is similar to the +* the SoapSendAction with only difference that it allows users to +* pass the SOAP header along the SOAP body ( soap action request) +* +* Return : int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +int SoapSendActionEx( + IN char * ActionURL, + IN char *ServiceType, + IN IXML_Document *Header, + IN IXML_Document *ActNode , + OUT IXML_Document **RespNode) ; + +/**************************************************************************** +* Function : SoapGetServiceVarStatus +* +* Parameters : +* IN char * action_url : Address to send this variable +* query message. +* IN char *var_name : Name of the variable. +* OUT char **var_value : Output value. +* +* Description : This function creates a status variable query message +* send it to the specified URL. It also collect the response. +* +* Return : int +* +* Note : +****************************************************************************/ +int SoapGetServiceVarStatus( + IN char * ActionURL, + IN DOMString VarName, + OUT DOMString *StVar) ; + +extern const char* ContentTypeHeader; + +#endif //SOAPLIB_H diff --git a/libupnp/upnp/src/inc/sock.h b/libupnp/upnp/src/inc/sock.h new file mode 100644 index 0000000..7de7034 --- /dev/null +++ b/libupnp/upnp/src/inc/sock.h @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_SOCK_H +#define GENLIB_NET_SOCK_H + +#include "util.h" + +#include + +//Following variable is not defined under winsock.h +#ifndef SD_RECEIVE +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 +#endif + + +typedef struct +{ + int socket; // handle/descriptor to a socket + + // the following two fields are filled only in incoming requests; + struct in_addr foreign_ip_addr; + unsigned short foreign_ip_port; + +} SOCKINFO; + +#ifdef __cplusplus +#extern "C" { +#endif + +/************************************************************************ +* Function : sock_init +* +* Parameters : +* OUT SOCKINFO* info ; Socket Information Object +* IN int sockfd ; Socket Descriptor +* +* Description : Assign the passed in socket descriptor to socket +* descriptor in the SOCKINFO structure. +* +* Return : int; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* UPNP_E_SOCKET_ERROR +* Note : +************************************************************************/ +int sock_init( OUT SOCKINFO* info, IN int sockfd ); + +/************************************************************************ +* Function : sock_init_with_ip +* +* Parameters : +* OUT SOCKINFO* info ; Socket Information Object +* IN int sockfd ; Socket Descriptor +* IN struct in_addr foreign_ip_addr ; Remote IP Address +* IN unsigned short foreign_ip_port ; Remote Port number +* +* Description : Calls the sock_init function and assigns the passed in +* IP address and port to the IP address and port in the SOCKINFO +* structure. +* +* Return : int; +* UPNP_E_SUCCESS +* UPNP_E_OUTOF_MEMORY +* UPNP_E_SOCKET_ERROR +* +* Note : +************************************************************************/ +int sock_init_with_ip( OUT SOCKINFO* info, IN int sockfd, + IN struct in_addr foreign_ip_addr, IN unsigned short foreign_ip_port ); + +/************************************************************************ +* Function : sock_read +* +* Parameters : +* IN SOCKINFO *info ; Socket Information Object +* OUT char* buffer ; Buffer to get data to +* IN size_t bufsize ; Size of the buffer +* IN int *timeoutSecs ; timeout value +* +* Description : Reads data on socket in sockinfo +* +* Return : int; +* numBytes - On Success, no of bytes received +* UPNP_E_TIMEDOUT - Timeout +* UPNP_E_SOCKET_ERROR - Error on socket calls +* +* Note : +************************************************************************/ +int sock_read( IN SOCKINFO *info, OUT char* buffer, IN size_t bufsize, + INOUT int *timeoutSecs ); + +/************************************************************************ +* Function : sock_write +* +* Parameters : +* IN SOCKINFO *info ; Socket Information Object +* IN char* buffer ; Buffer to send data from +* IN size_t bufsize ; Size of the buffer +* IN int *timeoutSecs ; timeout value +* +* Description : Writes data on the socket in sockinfo +* +* Return : int; +* numBytes - On Success, no of bytes sent +* UPNP_E_TIMEDOUT - Timeout +* UPNP_E_SOCKET_ERROR - Error on socket calls +* +* Note : +************************************************************************/ +int sock_write( IN SOCKINFO *info, IN char* buffer, IN size_t bufsize, + INOUT int *timeoutSecs ); + +/************************************************************************ +* Function : sock_destroy +* +* Parameters : +* INOUT SOCKINFO* info ; Socket Information Object +* int ShutdownMethod ; How to shutdown the socket. Used by +* sockets's shutdown() +* +* Description : Shutsdown the socket using the ShutdownMethod to +* indicate whether sends and receives on the socket will be +* dis-allowed. After shutting down the socket, closesocket is called +* to release system resources used by the socket calls. +* +* Return : int; +* UPNP_E_SOCKET_ERROR on failure +* UPNP_E_SUCCESS on success +* +* Note : +************************************************************************/ +int sock_destroy( INOUT SOCKINFO* info,int ); + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // GENLIB_NET_SOCK_H diff --git a/libupnp/upnp/src/inc/ssdplib.h b/libupnp/upnp/src/inc/ssdplib.h new file mode 100644 index 0000000..ff57122 --- /dev/null +++ b/libupnp/upnp/src/inc/ssdplib.h @@ -0,0 +1,551 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef SSDPLIB_H +#define SSDPLIB_H + +#include +#include +#include +#include +#include +#include "httpparser.h" +#include "httpreadwrite.h" +#include "miniserver.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//Enumeration to define all different types of ssdp searches +typedef enum SsdpSearchType{ + SSDP_SERROR=-1, + SSDP_ALL,SSDP_ROOTDEVICE, + SSDP_DEVICEUDN, + SSDP_DEVICETYPE, + SSDP_SERVICE +} SType; + + +//Enumeration to define all different type of ssdp messages +typedef enum SsdpCmdType{SSDP_ERROR=-1, + SSDP_OK, + SSDP_ALIVE, + SSDP_BYEBYE, + SSDP_SEARCH, + SSDP_NOTIFY, + SSDP_TIMEOUT +} Cmd; + + + +//Constant +#define BUFSIZE 2500 +#define SSDP_IP "239.255.255.250" +#define SSDP_PORT 1900 +#define NUM_TRY 3 +#define NUM_COPY 2 +#define THREAD_LIMIT 50 +#define COMMAND_LEN 300 + +#ifndef X_USER_AGENT // can be overwritten by configure CFLAGS argument +/** @name X_USER_AGENT + * The {\tt X_USER_AGENT} constant specifies the value of the X-User-Agent: + * HTTP header. The value "redsonic" is needed for the DSM-320. See + * https://sourceforge.net/forum/message.php?msg_id=3166856 for more + * information + */ + #define X_USER_AGENT "redsonic" +#endif + +//Error code +#define NO_ERROR_FOUND 0 +#define E_REQUEST_INVALID -3 +#define E_RES_EXPIRED -4 +#define E_MEM_ALLOC -5 +#define E_HTTP_SYNTEX -6 +#define E_SOCKET -7 +#define RQST_TIMEOUT 20 + + + +//Structure to store the SSDP information +typedef struct SsdpEventStruct +{ + enum SsdpCmdType Cmd; + enum SsdpSearchType RequestType; + int ErrCode; + int MaxAge; + int Mx; + char UDN[LINE_SIZE]; + char DeviceType[LINE_SIZE]; + char ServiceType[LINE_SIZE]; //NT or ST + char Location[LINE_SIZE]; + char HostAddr[LINE_SIZE]; + char Os[LINE_SIZE]; + char Ext[LINE_SIZE]; + char Date[LINE_SIZE]; + struct sockaddr_in * DestAddr; + void * Cookie; +} Event; + +typedef void (* SsdpFunPtr)(Event *); + +typedef Event SsdpEvent ; + +//Structure to contain Discovery response +typedef struct resultData +{ + struct Upnp_Discovery param; + void *cookie; + Upnp_FunPtr ctrlpt_callback; +}ResultData; + + +typedef struct TData +{ + int Mx; + void * Cookie; + char * Data; + struct sockaddr_in DestAddr; + +}ThreadData; + +typedef struct ssdpsearchreply +{ + int MaxAge; + UpnpDevice_Handle handle; + struct sockaddr_in dest_addr; + SsdpEvent event; + +}SsdpSearchReply; + +typedef struct ssdpsearcharg +{ + int timeoutEventId; + char * searchTarget; + void *cookie; + enum SsdpSearchType requestType; +} SsdpSearchArg; + + +typedef struct +{ + http_parser_t parser; + struct sockaddr_in dest_addr; +} ssdp_thread_data; + + +/* globals */ + +CLIENTONLY(extern SOCKET gSsdpReqSocket;); + +typedef int (*ParserFun)(char *, Event *); + + +//void InitParser(); + +//int AnalyzeCommand(char * szCommand, Event * Evt); + +/************************************************************************ +* Function : Make_Socket_NoBlocking +* +* Parameters: +* IN int sock: socket +* +* Description: +* This function to make ssdp socket non-blocking. +* +* Returns: int +* 0 if successful else -1 +***************************************************************************/ +int Make_Socket_NoBlocking (int sock); + +/************************************************************************ +* Function : ssdp_handle_device_request +* +* Parameters: +* IN void *data: +* +* Description: +* This function handles the search request. It do the sanity checks of +* the request and then schedules a thread to send a random time reply ( +* random within maximum time given by the control point to reply). +* +* Returns: void * +* 1 if successful else appropriate error +***************************************************************************/ +void ssdp_handle_device_request( IN http_message_t* hmsg, + IN struct sockaddr_in* dest_addr ); + +/************************************************************************ +* Function : ssdp_handle_ctrlpt_msg +* +* Parameters: +* IN http_message_t* hmsg: SSDP message from the device +* IN struct sockaddr_in* dest_addr: Address of the device +* IN xboolean timeout: timeout kept by the control point while sending +* search message +* IN void* cookie: Cookie stored by the control point application. +* This cookie will be returned to the control point +* in the callback +* +* Description: +* This function handles the ssdp messages from the devices. These +* messages includes the search replies, advertisement of device coming +* alive and bye byes. +* +* Returns: void +* +***************************************************************************/ +void ssdp_handle_ctrlpt_msg( IN http_message_t* hmsg, + IN struct sockaddr_in* dest_addr, + IN xboolean timeout, + IN void* cookie ); + +/************************************************************************ +* Function : unique_service_name +* +* Parameters: +* IN char *cmd: Service Name string +* OUT SsdpEvent *Evt: The SSDP event structure partially filled +* by all the function. +* +* Description: +* This function fills the fields of the event structure like DeviceType, +* Device UDN and Service Type +* +* Returns: int +* 0 if successful else -1 +***************************************************************************/ +int unique_service_name(char * cmd, SsdpEvent * Evt); + + +/************************************************************************ +* Function : get_ssdp_sockets +* +* Parameters: +* OUT MiniServerSockArray *out: Arrays of SSDP sockets +* +* Description: +* This function creates the ssdp sockets. It set their option to listen +* for multicast traffic. +* +* Returns: int +* return UPNP_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +int get_ssdp_sockets(MiniServerSockArray *out); + + +/************************************************************************ +* Function : readFromSSDPSocket +* +* Parameters: +* IN SOCKET socket: SSDP socket +* +* Description: +* This function reads the data from the ssdp socket. +* +* Returns: void +* +***************************************************************************/ +void readFromSSDPSocket(SOCKET socket); + + +/************************************************************************ +* Function : ssdp_request_type1 +* +* Parameters: +* IN char *cmd: command came in the ssdp request +* +* Description: +* This function figures out the type of the SSDP search in the +* in the request. +* +* Returns: enum SsdpSearchType +* return appropriate search type else returns SSDP_ERROR +***************************************************************************/ +enum SsdpSearchType ssdp_request_type1(IN char *cmd); + + +/************************************************************************ +* Function : ssdp_request_type +* +* Parameters: +* IN char *cmd: command came in the ssdp request +* OUT SsdpEvent *Evt: The event structure partially filled by +* this function. +* +* Description: +* This function starts filling the SSDP event structure based upon the +* request received. +* +* Returns: int +* 0 on success; -1 on error +***************************************************************************/ +int ssdp_request_type(IN char * cmd, OUT SsdpEvent * Evt); + + +/************************************************************************ +* Function : SearchByTarget +* +* Parameters: +* IN int Mx:Number of seconds to wait, to collect all the +* responses. +* char *St: Search target. +* void *Cookie: cookie provided by control point application. This +* cokie will be returned to application in the +* callback. +* +* Description: +* This function creates and send the search request for a specific URL. +* +* Returns: int +* 1 if successful else appropriate error +***************************************************************************/ +int SearchByTarget(IN int Mx, IN char *St, IN void *Cookie); + +/************************************************************************ +* Function : DeviceAdvertisement +* +* Parameters: +* IN char * DevType : type of the device +* IN int RootDev: flag to indicate if the device is root device +* IN char * nt : value of NT +* IN char * usn : +* IN char * location :Location URL. +* IN int duration :Service duration in sec. +* +* Description: +* This function creates the device advertisement request based on +* the input parameter, and send it to the multicast channel. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int DeviceAdvertisement(IN char * DevType, int RootDev,char * Udn, + IN char * Location, IN int Duration); + + +/************************************************************************ +* Function : DeviceShutdown +* +* Parameters: +* IN char *DevType: Device Type. +* IN int RootDev:1 means root device. +* IN char * Udn: Device UDN +* IN char *_Server: +* IN char * Location: Location URL +* IN int Duration :Device duration in sec. +* +* Description: +* This function creates a HTTP device shutdown request packet +* and sent it to the multicast channel through RequestHandler. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int DeviceShutdown( IN char * DevType, + IN int RootDev, + IN char * Udn, + IN char * _Server, + IN char * Location, + IN int Duration ); + +/************************************************************************ +* Function : DeviceReply +* +* Parameters: +* IN struct sockaddr_in * DestAddr:destination IP address. +* IN char *DevType: Device type +* IN int RootDev: 1 means root device 0 means embedded device. +* IN char * Udn: Device UDN +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. + +* Description: +* This function creates the reply packet based on the input parameter, +* and send it to the client address given in its input parameter DestAddr. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int DeviceReply(IN struct sockaddr_in * DestAddr, + IN char *DevType, + IN int RootDev, + IN char * Udn, + IN char * Location, IN int Duration); + +/************************************************************************ +* Function : SendReply +* +* Parameters: +* IN struct sockaddr_in * DestAddr:destination IP address. +* IN char *DevType: Device type +* IN int RootDev: 1 means root device 0 means embedded device. +* IN char * Udn: Device UDN +* IN char *_Server: +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. +* IN int ByType: +* +* Description: +* This function creates the reply packet based on the input parameter, +* and send it to the client addesss given in its input parameter DestAddr. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int SendReply(IN struct sockaddr_in * DestAddr, + IN char *DevType, + IN int RootDev, + IN char * Udn, + IN char * Location, + IN int Duration, + IN int ByType ); + +/************************************************************************ +* Function : ServiceAdvertisement +* +* Parameters: +* IN char * Udn: Device UDN +* IN char *ServType: Service Type. +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. + +* Description: +* This function creates the advertisement packet based +* on the input parameter, and send it to the multicast channel. + +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int ServiceAdvertisement( IN char * Udn, + IN char * ServType, + IN char * Location, + IN int Duration); + +/************************************************************************ +* Function : ServiceReply +* +* Parameters: +* IN struct sockaddr_in *DestAddr: +* IN char * Udn: Device UDN +* IN char *ServType: Service Type. +* IN char *Server: Not used +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. + +* Description: +* This function creates the advertisement packet based +* on the input parameter, and send it to the multicast channel. + +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int ServiceReply(IN struct sockaddr_in *DestAddr, + IN char * ServType, + IN char * Udn, + IN char * Location, + IN int Duration); + +/************************************************************************ +* Function : ServiceShutdown +* +* Parameters: +* IN char * Udn: Device UDN +* IN char *ServType: Service Type. +* IN char * Location: Location of Device description document. +* IN int Duration :Service duration in sec. + +* Description: +* This function creates a HTTP service shutdown request packet +* and sent it to the multicast channel through RequestHandler. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int ServiceShutdown( IN char * Udn, IN char * ServType, + IN char * Location, + IN int Duration); + + +/************************************************************************ +* Function : advertiseAndReplyThread +* +* Parameters: +* IN void *data: Structure containing the search request +* +* Description: +* This function is a wrapper function to reply the search request +* coming from the control point. +* +* Returns: void * +* always return NULL +***************************************************************************/ +void * advertiseAndReplyThread(IN void * data); + +/************************************************************************ +* Function : AdvertiseAndReply +* +* Parameters: +* IN int AdFlag: -1 = Send shutdown, 0 = send reply, +* 1 = Send Advertisement +* IN UpnpDevice_Handle Hnd: Device handle +* IN enum SsdpSearchType SearchType:Search type for sending replies +* IN struct sockaddr_in *DestAddr:Destination address +* IN char *DeviceType:Device type +* IN char *DeviceUDN:Device UDN +* IN char *ServiceType:Service type +* IN int Exp:Advertisement age +* +* Description: +* This function to send SSDP advertisements, replies and shutdown messages. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int AdvertiseAndReply(IN int AdFlag, + IN UpnpDevice_Handle Hnd, + IN enum SsdpSearchType SearchType, + IN struct sockaddr_in *DestAddr, + IN char *DeviceType, + IN char *DeviceUDN, + IN char *ServiceType, int Exp); + +#endif + diff --git a/libupnp/upnp/src/inc/statcodes.h b/libupnp/upnp/src/inc/statcodes.h new file mode 100644 index 0000000..9ca7abb --- /dev/null +++ b/libupnp/upnp/src/inc/statcodes.h @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_STATCODES_H +#define GENLIB_NET_HTTP_STATCODES_H + +// HTTP response status codes + +#define HTTP_CONTINUE 100 +#define HTTP_SWITCHING_PROCOTOLS 101 + +#define HTTP_OK 200 +#define HTTP_CREATED 201 +#define HTTP_ACCEPTED 202 +#define HTTP_NON_AUTHORATATIVE 203 +#define HTTP_NO_CONTENT 204 +#define HTTP_RESET_CONTENT 205 +#define HTTP_PARTIAL_CONTENT 206 + +#define HTTP_MULTIPLE_CHOICES 300 +#define HTTP_MOVED_PERMANENTLY 301 +#define HTTP_FOUND 302 +#define HTTP_SEE_OTHER 303 +#define HTTP_NOT_MODIFIED 304 +#define HTTP_USE_PROXY 305 +#define HTTP_UNUSED_3XX 306 +#define HTTP_TEMPORARY_REDIRECT 307 + +#define HTTP_BAD_REQUEST 400 +#define HTTP_UNAUTHORIZED 401 +#define HTTP_PAYMENT_REQD 402 +#define HTTP_FORBIDDEN 403 +#define HTTP_NOT_FOUND 404 +#define HTTP_METHOD_NOT_ALLOWED 405 +#define HTTP_NOT_ACCEPTABLE 406 +#define HTTP_PROXY_AUTH_REQD 407 +#define HTTP_REQUEST_TIMEOUT 408 +#define HTTP_CONFLICT 409 +#define HTTP_GONE 410 +#define HTTP_LENGTH_REQUIRED 411 +#define HTTP_PRECONDITION_FAILED 412 +#define HTTP_REQ_ENTITY_TOO_LARGE 413 +#define HTTP_REQ_URI_TOO_LONG 414 +#define HTTP_UNSUPPORTED_MEDIA_TYPE 415 +#define HTTP_REQUEST_RANGE_NOT_SATISFIABLE 416 +#define HTTP_EXPECTATION_FAILED 417 + +#define HTTP_INTERNAL_SERVER_ERROR 500 +#define HTTP_NOT_IMPLEMENTED 501 +#define HTTP_BAD_GATEWAY 502 +#define HTTP_SERVICE_UNAVAILABLE 503 +#define HTTP_GATEWAY_TIMEOUT 504 +#define HTTP_HTTP_VERSION_NOT_SUPPORTED 505 + +// *********** HTTP lib error codes ********** + +#define HTTP_E_OUT_OF_MEMORY -2 +#define HTTP_E_BAD_MSG_FORMAT -3 +#define HTTP_E_TIMEDOUT -4 +#define HTTP_E_FILE_READ -5 + +// ******************************************* + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************ +* Function: http_get_code_text +* +* Parameters: +* int statusCode ; Status code based on which the status table and +* status message is returned +* +* Description: Return the right status message based on the passed in +* int statusCode input parameter +* +* Returns: +* const char* ptr - pointer to the status message string +************************************************************************/ +const char* http_get_code_text( int statusCode ); + +#ifdef __cplusplus +} // extern C +#endif + +#endif /* GENLIB_NET_HTTP_STATCODES_H */ diff --git a/libupnp/upnp/src/inc/statuscodes.h b/libupnp/upnp/src/inc/statuscodes.h new file mode 100644 index 0000000..0793141 --- /dev/null +++ b/libupnp/upnp/src/inc/statuscodes.h @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_STATUSCODES_H +#define GENLIB_NET_HTTP_STATUSCODES_H + +// HTTP response status codes + +#define HTTP_CONTINUE 100 +#define HTTP_SWITCHING_PROCOTOLS 101 + +#define HTTP_OK 200 +#define HTTP_CREATED 201 +#define HTTP_ACCEPTED 202 +#define HTTP_NON_AUTHORATATIVE 203 +#define HTTP_NO_CONTENT 204 +#define HTTP_RESET_CONTENT 205 +#define HTTP_PARTIAL_CONTENT 206 + +#define HTTP_MULTIPLE_CHOICES 300 +#define HTTP_MOVED_PERMANENTLY 301 +#define HTTP_FOUND 302 +#define HTTP_SEE_OTHER 303 +#define HTTP_NOT_MODIFIED 304 +#define HTTP_USE_PROXY 305 +#define HTTP_UNUSED_3XX 306 +#define HTTP_TEMPORARY_REDIRECT 307 + +#define HTTP_BAD_REQUEST 400 +#define HTTP_UNAUTHORIZED 401 +#define HTTP_PAYMENT_REQD 402 +#define HTTP_FORBIDDEN 403 +#define HTTP_NOT_FOUND 404 +#define HTTP_METHOD_NOT_ALLOWED 405 +#define HTTP_NOT_ACCEPTABLE 406 +#define HTTP_PROXY_AUTH_REQD 407 +#define HTTP_REQUEST_TIMEOUT 408 +#define HTTP_CONFLICT 409 +#define HTTP_GONE 410 +#define HTTP_LENGTH_REQUIRED 411 +#define HTTP_PRECONDITION_FAILED 412 +#define HTTP_REQ_ENTITY_TOO_LARGE 413 +#define HTTP_REQ_URI_TOO_LONG 414 +#define HTTP_UNSUPPORTED_MEDIA_TYPE 415 +#define HTTP_REQUEST_RANGE_NOT_SATISFIABLE 416 +#define HTTP_EXPECTATION_FAILED 417 + +#define HTTP_INTERNAL_SERVER_ERROR 500 +#define HTTP_NOT_IMPLEMENTED 501 +#define HTTP_BAD_GATEWAY 502 +#define HTTP_SERVICE_UNAVAILABLE 503 +#define HTTP_GATEWAY_TIMEOUT 504 +#define HTTP_HTTP_VERSION_NOT_SUPPORTED 505 + +// *********** HTTP lib error codes ********** + +#define HTTP_E_OUT_OF_MEMORY -2 +#define HTTP_E_BAD_MSG_FORMAT -3 +#define HTTP_E_TIMEDOUT -4 +#define HTTP_E_FILE_READ -5 + +// ******************************************* + +#ifdef __cplusplus +extern "C" { +#endif + +const char* http_GetCodeText( int statusCode ); + +#ifdef __cplusplus +} // extern C +#endif + +#endif /* GENLIB_NET_HTTP_STATUSCODES_H */ diff --git a/libupnp/upnp/src/inc/strintmap.h b/libupnp/upnp/src/inc/strintmap.h new file mode 100644 index 0000000..6ddcb1c --- /dev/null +++ b/libupnp/upnp/src/inc/strintmap.h @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_UTIL_STRINTMAP_H +#define GENLIB_UTIL_STRINTMAP_H + +#include +#include "util.h" + +// Util to map from a string to an integer and vice versa + +typedef struct // str_int_entry +{ + char *name; // a value in string form + int id; // same value in integer form +} str_int_entry; + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************ +* Function : map_str_to_int +* +* Parameters : +* IN const char* name ; string containing the name to be matched +* IN size_t name_len ; size of the string to be matched +* IN str_int_entry* table ; table of entries that need to be +* matched. +* IN int num_entries ; number of entries in the table that need +* to be searched. +* IN xboolean case_sensitive ; whether the case should be case +* sensitive or not +* +* Description : Match the given name with names from the entries in the +* table. Returns the index of the table when the entry is found. +* +* Return : int ; +* index - On Success +* -1 - On failure +* +* Note : +************************************************************************/ +int map_str_to_int( IN const char* name, IN size_t name_len, + IN str_int_entry* table, IN int num_entries, + IN xboolean case_sensitive ); + + +/************************************************************************ +* Function : map_int_to_str +* +* Parameters : +* IN int id ; ID to be matched +* IN str_int_entry* table ; table of entries that need to be +* matched. +* IN int num_entries ; number of entries in the table that need +* to be searched. +* +* Description : Returns the index from the table where the id matches +* the entry from the table. +* +* Return : int ; +* +* Note : +************************************************************************/ +int map_int_to_str( IN int id, IN str_int_entry* table, + IN int num_entries ); + +#ifdef __cplusplus +} // extern C +#endif + + +#endif // GENLIB_UTIL_STRINTMAP_H diff --git a/libupnp/upnp/src/inc/sysdep.h b/libupnp/upnp/src/inc/sysdep.h new file mode 100644 index 0000000..8b1b153 --- /dev/null +++ b/libupnp/upnp/src/inc/sysdep.h @@ -0,0 +1,59 @@ + /* + ** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. + ** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & + ** Digital Equipment Corporation, Maynard, Mass. + ** Copyright (c) 1998 Microsoft. + ** To anyone who acknowledges that this file is provided "AS IS" + ** without any express or implied warranty: permission to use, copy, + ** modify, and distribute this file for any purpose is hereby + ** granted without fee, provided that the above copyright notices and + ** this notice appears in all source code copies, and that none of + ** the names of Open Software Foundation, Inc., Hewlett-Packard + ** Company, or Digital Equipment Corporation be used in advertising + ** or publicity pertaining to distribution of the software without + ** specific, written prior permission. Neither Open Software + ** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment + ** Corporation makes any representations about the suitability of + ** this software for any purpose. + */ + +#include +#include +//#include +#include "ithread.h" + +/* change to point to where MD5 .h's live */ +/* get MD5 sample implementation from RFC 1321 */ +#include "global.h" +#include "md5.h" + +/* set the following to the number of 100ns ticks of the actual + resolution of + your system's clock */ +#define UUIDS_PER_TICK 1024 + +/* Set the following to a call to acquire a system wide global lock + */ +extern ithread_mutex_t gUUIDMutex; + +#define UUIDLock() ithread_mutex_lock(&gUUIDMutex) +#define UUIDUnlock() ithread_mutex_unlock(&gUUIDMutex) + +typedef unsigned long unsigned32; +typedef unsigned short unsigned16; +typedef unsigned char unsigned8; +typedef unsigned char byte; + +/* Set this to what your compiler uses for 64 bit data type */ +#define unsigned64_t unsigned long long +#define I64(C) C##LL + +typedef unsigned64_t uuid_time_t; +typedef struct { + char nodeID[6]; +} uuid_node_t; + +void get_ieee_node_identifier(uuid_node_t *node); +void get_system_time(uuid_time_t *uuid_time); +void get_random_info(char seed[16]); + diff --git a/libupnp/upnp/src/inc/unixutil.h b/libupnp/upnp/src/inc/unixutil.h new file mode 100644 index 0000000..0048bdf --- /dev/null +++ b/libupnp/upnp/src/inc/unixutil.h @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +// Unix-specific network utilities + +#ifndef GENLIB_NET_UNIXUTIL_H +#define GENLIB_NET_UNIXUTIL_H + +#include +#include + +#endif // GENLIB_NET_UNIXUTIL_H diff --git a/libupnp/upnp/src/inc/upnp_timeout.h b/libupnp/upnp/src/inc/upnp_timeout.h new file mode 100644 index 0000000..65ab657 --- /dev/null +++ b/libupnp/upnp/src/inc/upnp_timeout.h @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef _UPNPTIMEOUTH_ +#define _UPNPTIMEOUTH_ + +typedef struct UPNP_TIMEOUT { + int EventType; + int handle; + int eventId; + void *Event; +} upnp_timeout; + + +/************************************************************************ +* Function : free_upnp_timeout +* +* Parameters : +* upnp_timeout *event ; Event which needs to be freed +* +* Description : Free memory associated with event and memory for any +* sub-elements +* +* Return : void ; +* +* Note : +************************************************************************/ +void free_upnp_timeout(upnp_timeout *event); + +#endif diff --git a/libupnp/upnp/src/inc/upnpapi.h b/libupnp/upnp/src/inc/upnpapi.h new file mode 100644 index 0000000..6bb9b9b --- /dev/null +++ b/libupnp/upnp/src/inc/upnpapi.h @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +// File : upnpapi.h + +#ifndef UPNPDK_H +#define UPNPDK_H + +#include "upnp.h" +#include "client_table.h" + +//#include "../ssdp/ssdplib.h" + + + + +#define MAX_INTERFACES 256 + +#define DEFAULT_INTERFACE 1 + +#define DEV_LIMIT 200 + +#define NUM_HANDLE 200 + +#define DEFAULT_MX 5 + +#define DEFAULT_MAXAGE 1800 + +extern size_t g_maxContentLength; + +// 30-second timeout +#define UPNP_TIMEOUT 30 + +typedef enum {HND_INVALID=-1,HND_CLIENT,HND_DEVICE} Upnp_Handle_Type; + +// Data to be stored in handle table for +struct Handle_Info +{ + Upnp_Handle_Type HType; + Upnp_FunPtr Callback; // Callback function pointer. + char * Cookie; + + DEVICEONLY(char DescURL[LINE_SIZE];) // URL for the use of SSDP + DEVICEONLY(char DescXML[LINE_SIZE];) // XML file path for device + //description + + DEVICEONLY(int MaxAge;) // Advertisement timeout + DEVICEONLY(IXML_Document *DescDocument;) // Description parsed in + //terms of DOM document + DEVICEONLY(IXML_NodeList *DeviceList;) // List of devices in the + //description document + DEVICEONLY(IXML_NodeList *ServiceList;) // List of services in the + // description document + DEVICEONLY(service_table ServiceTable;) //table holding subscriptions and + //URL information + DEVICEONLY(int MaxSubscriptions;) + DEVICEONLY(int MaxSubscriptionTimeOut;) + + //Client only + CLIENTONLY(client_subscription * ClientSubList;) //client subscription list + CLIENTONLY(LinkedList SsdpSearchList;) // active ssdp searches + int aliasInstalled; // 0 = not installed; otherwise installed +} ; + +extern ithread_mutex_t GlobalHndMutex; +Upnp_Handle_Type GetHandleInfo(int Hnd, struct Handle_Info **HndInfo); + +#define HandleLock() DBGONLY(UpnpPrintf(UPNP_INFO,API,__FILE__,__LINE__,"Trying Lock")); ithread_mutex_lock(&GlobalHndMutex); DBGONLY(UpnpPrintf(UPNP_INFO,API,__FILE__,__LINE__,"LOCK")); +#define HandleUnlock() DBGONLY(UpnpPrintf(UPNP_INFO,API,__FILE__,__LINE__,"Trying Unlock")); ithread_mutex_unlock(&GlobalHndMutex); DBGONLY(UpnpPrintf(UPNP_INFO,API,__FILE__,__LINE__,"Unlock")); +Upnp_Handle_Type GetClientHandleInfo(int *client_handle_out, + struct Handle_Info **HndInfo); +Upnp_Handle_Type GetDeviceHandleInfo(int *device_handle_out, + struct Handle_Info **HndInfo); + + +extern char LOCAL_HOST[LINE_SIZE]; + +extern unsigned short LOCAL_PORT; + +extern TimerThread gTimerThread; +extern ThreadPool gRecvThreadPool; +extern ThreadPool gSendThreadPool; + + +typedef enum { + SUBSCRIBE, + UNSUBSCRIBE, + DK_NOTIFY, + QUERY, + ACTION, + STATUS, + DEVDESCRIPTION, + SERVDESCRIPTION, + MINI, + RENEW} UpnpFunName; + +struct UpnpNonblockParam +{ + UpnpFunName FunName; + int Handle; + int TimeOut; + char VarName[NAME_SIZE]; + char NewVal[NAME_SIZE]; + char DevType[NAME_SIZE]; + char DevId[NAME_SIZE]; + char ServiceType[NAME_SIZE]; + char ServiceVer[NAME_SIZE]; + char Url[NAME_SIZE]; + Upnp_SID SubsId; + char *Cookie; + Upnp_FunPtr Fun; + IXML_Document *Header; + IXML_Document *Act; + struct DevDesc *Devdesc; +}; + + +extern virtualDirList *pVirtualDirList; +extern struct UpnpVirtualDirCallbacks virtualDirCallback; + + +typedef enum { WEB_SERVER_DISABLED, WEB_SERVER_ENABLED } WebServerState; + +#define E_HTTP_SYNTAX -6 + +void InitHandleList(); +int GetFreeHandle(); +int FreeHandle(int Handle); +void UpnpThreadDistribution(struct UpnpNonblockParam * Param); + + +void AutoAdvertise(void *input); +int getlocalhostname(char *out); + +virtualDirList *pVirtualDirList; +extern WebServerState bWebServerState; + +#endif + + + +/************************ END OF upnpapi.h **********************/ diff --git a/libupnp/upnp/src/inc/upnpclosesocket.h b/libupnp/upnp/src/inc/upnpclosesocket.h new file mode 100644 index 0000000..fb1ec89 --- /dev/null +++ b/libupnp/upnp/src/inc/upnpclosesocket.h @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UPNPCLOSESOCKET_H +#define UPNPCLOSESOCKET_H + +#define UpnpCloseSocket close + +#endif diff --git a/libupnp/upnp/src/inc/uri.h b/libupnp/upnp/src/inc/uri.h new file mode 100644 index 0000000..70f5063 --- /dev/null +++ b/libupnp/upnp/src/inc/uri.h @@ -0,0 +1,508 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_URI_H +#define GENLIB_NET_URI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "upnp.h" +//#include + + +#define HTTP_DATE_LENGTH 37 // length for HTTP DATE: + //"DATE: Sun, 01 Jul 2000 08:15:23 GMT" +#define SEPARATORS "()<>@,;:\\\"/[]?={} \t" +#define MARK "-_.!~*'()" +#define RESERVED ";/?:@&=+$,{}" //added {} for compatibility +#define HTTP_SUCCESS 1 + +#define FALSE 0 +#define TAB 9 +#define CR 13 +#define LF 10 +#define SOCKET_BUFFER_SIZE 5000 + +enum hostType { HOSTNAME, IPv4address }; +enum pathType { ABS_PATH, REL_PATH, OPAQUE_PART }; +enum uriType { ABSOLUTE, RELATIVE }; + +/* Buffer used in parsinghttp messages, urls, etc. generally this simply +* holds a pointer into a larger array */ +typedef struct TOKEN { + char * buff; + int size; +} token; + + +/* Represents a host port:e.g. :"127.127.0.1:80" +* text is a token pointing to the full string representation */ +typedef struct HOSTPORT { + token text; //full host port + struct sockaddr_in IPv4address; //Network Byte Order +} hostport_type; + +/* Represents a URI used in parse_uri and elsewhere */ +typedef struct URI{ + enum uriType type; + token scheme; + enum pathType path_type; + token pathquery; + token fragment; + hostport_type hostport; +} uri_type; + +/* Represents a list of URLs as in the "callback" header of SUBSCRIBE +* message in GENA +* char * URLs holds dynamic memory */ +typedef struct URL_LIST { + int size; + char * URLs; //all the urls, delimited by <> + uri_type *parsedURLs; +} URL_list; + + +/************************************************************************ +* Function : replace_escaped +* +* Parameters : +* char * in ; string of characters +* int index ; index at which to start checking the characters +* int *max ; +* +* Description : Replaces an escaped sequence with its unescaped version +* as in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) +* Size of array is NOT checked (MUST be checked by caller) +* +* Return : int ; +* +* Note : This function modifies the string. If the sequence is an +* escaped sequence it is replaced, the other characters in the +* string are shifted over, and NULL characters are placed at the +* end of the string. +************************************************************************/ +int replace_escaped(char * in, int index, int *max); + +/************************************************************************ +* Function : copy_URL_list +* +* Parameters : +* URL_list *in ; Source URL list +* URL_list *out ; Destination URL list +* +* Description : Copies one URL_list into another. This includes +* dynamically allocating the out->URLs field (the full string), +* and the structures used to hold the parsedURLs. This memory MUST +* be freed by the caller through: free_URL_list(&out) +* +* Return : int ; +* HTTP_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - On Failure to allocate memory +* +* Note : +************************************************************************/ +int copy_URL_list( URL_list *in, URL_list *out); + +/************************************************************************ +* Function : free_URL_list +* +* Parameters : +* URL_list * list ; URL List object +* +* Description : Frees the memory associated with a URL_list. Frees the +* dynamically allocated members of of list. Does NOT free the +* pointer to the list itself ( i.e. does NOT free(list)) +* +* Return : void ; +* +* Note : +************************************************************************/ +void free_URL_list(URL_list * list); + +/************************************************************************ +* Function : print_uri +* +* Parameters : +* uri_type *in ; URI object +* +* Description : Function useful in debugging for printing a parsed uri. +* Compiled out with DBGONLY macro. +* +* Return : void ; +* +* Note : +************************************************************************/ +DBGONLY(void print_uri( uri_type *in);) + +/************************************************************************ +* Function : print_token +* +* Parameters : +* token * in ; +* +* Description : Function useful in debugging for printing a token. +* Compiled out with DBGONLY macro. +* +* Return : void ; +* +* Note : +************************************************************************/ +void print_token( token * in); + +/************************************************************************ +* Function : token_string_casecmp +* +* Parameters : +* token * in1 ; Token object whose buffer is to be compared +* char * in2 ; string of characters to compare with +* +* Description : Compares buffer in the token object with the buffer +* in in2 +* +* Return : int ; +* < 0 string1 less than string2 +* 0 string1 identical to string2 +* > 0 string1 greater than string2 +* +* Note : +************************************************************************/ +int token_string_casecmp( token * in1, char * in2); + +/************************************************************************ +* Function : token_string_cmp +* +* Parameters : +* token * in1 ; Token object whose buffer is to be compared +* char * in2 ; string of characters to compare with +* +* Description : Compares a null terminated string to a token (exact) +* +* Return : int ; +* < 0 string1 less than string2 +* 0 string1 identical to string2 +* > 0 string1 greater than string2 +* +* Note : +************************************************************************/ +int token_string_cmp( token * in1, char * in2); + +/************************************************************************ +* Function : token_cmp +* +* Parameters : +* token *in1 ; First token object whose buffer is to be compared +* token *in2 ; Second token object used for the comparison +* +* Description : Compares two tokens +* +* Return : int ; +* < 0 string1 less than string2 +* 0 string1 identical to string2 +* > 0 string1 greater than string2 +* +* Note : +************************************************************************/ +int token_cmp( token *in1, token *in2); + +/************************************************************************ +* Function : parse_port +* +* Parameters : +* int max ; sets a maximum limit +* char * port ; port to be parsed. +* unsigned short * out ; out parameter where the port is parsed +* and converted into network format +* +* Description : parses a port (i.e. '4000') and converts it into a +* network ordered unsigned short int. +* +* Return : int ; +* +* Note : +************************************************************************/ +int parse_port(int max, char * port, unsigned short int * out); + +/************************************************************************ +* Function : parse_hostport +* +* Parameters : +* char *in ; string of characters representing host and port +* int max ; sets a maximum limit +* hostport_type *out ; out parameter where the host and port +* are represented as an internet address +* +* Description : Parses a string representing a host and port +* (e.g. "127.127.0.1:80" or "localhost") and fills out a +* hostport_type struct with internet address and a token +* representing the full host and port. uses gethostbyname. +* +* Return : int ; +* +* Note : +************************************************************************/ +int parse_hostport( char* in, int max, hostport_type *out ); + +/************************************************************************ +* Function : remove_escaped_chars +* +* Parameters : +* INOUT char *in ; string of characters to be modified +* INOUT int *size ; size limit for the number of characters +* +* Description : removes http escaped characters such as: "%20" and +* replaces them with their character representation. i.e. +* "hello%20foo" -> "hello foo". The input IS MODIFIED in place. +* (shortened). Extra characters are replaced with NULL. +* +* Return : int ; +* UPNP_E_SUCCESS +* +* Note : +************************************************************************/ +int remove_escaped_chars(char *in,int *size); + +/************************************************************************ +* Function : remove_dots +* +* Parameters : +* char *in ; string of characters from which "dots" have to be +* removed +* int size ; size limit for the number of characters +* +* Description : Removes ".", and ".." from a path. If a ".." can not +* be resolved (i.e. the .. would go past the root of the path) an +* error is returned. The input IS modified in place.) +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - On failure to allocate memory +* UPNP_E_INVALID_URL - Failure to resolve URL +* +* Note : +* Examples +* char path[30]="/../hello"; +* remove_dots(path, strlen(path)) -> UPNP_E_INVALID_URL +* char path[30]="/./hello"; +* remove_dots(path, strlen(path)) -> UPNP_E_SUCCESS, +* in = "/hello" +* char path[30]="/./hello/foo/../goodbye" -> +* UPNP_E_SUCCESS, in = "/hello/goodbye" + +************************************************************************/ +int remove_dots(char * in, int size); + +/************************************************************************ +* Function : resolve_rel_url +* +* Parameters : +* char * base_url ; Base URL +* char * rel_url ; Relative URL +* +* Description : resolves a relative url with a base url returning a NEW +* (dynamically allocated with malloc) full url. If the base_url is +* NULL, then a copy of the rel_url is passed back if the rel_url +* is absolute then a copy of the rel_url is passed back if neither +* the base nor the rel_url are Absolute then NULL is returned. +* otherwise it tries and resolves the relative url with the base +* as described in: http://www.ietf.org/rfc/rfc2396.txt (RFCs +* explaining URIs) +* : resolution of '..' is NOT implemented, but '.' is resolved +* +* Return : char * ; +* +* Note : +************************************************************************/ +char * resolve_rel_url( char * base_url, char * rel_url); + +/************************************************************************ +* Function : parse_uri +* +* Parameters : +* char * in ; character string containing uri information to be +* parsed +* int max ; maximum limit on the number of characters +* uri_type * out ; out parameter which will have the parsed uri +* information +* +* Description : parses a uri as defined in http://www.ietf.org/rfc/ +* rfc2396.txt (RFC explaining URIs) +* Handles absolute, relative, and opaque uris. Parses into the +* following pieces: scheme, hostport, pathquery, fragment (path and +* query are treated as one token) +* Caller should check for the pieces they require. +* +* Return : int ; +* +* Note : +************************************************************************/ +int parse_uri( char * in, int max, uri_type * out); + +/************************************************************************ +* Function : parse_uri_and_unescape +* +* Parameters : +* char * in ; +* int max ; +* uri_type * out ; +* +* Description : Same as parse_uri, except that all strings are +* unescaped (%XX replaced by chars) +* +* Return : int ; +* +* Note: This modifies 'pathquery' and 'fragment' parts of the input +************************************************************************/ +int parse_uri_and_unescape(char * in, int max, uri_type * out); + +int parse_token( char * in, token * out, int max_size); + +/************************************************************************ +* commented #defines, functions and typdefs * +************************************************************************/ + +/************************************************************************ +* Commented #defines * +************************************************************************/ +//#define HTTP_E_BAD_URL UPNP_E_INVALID_URL +//#define HTTP_E_READ_SOCKET UPNP_E_SOCKET_READ +//#define HTTP_E_BIND_SOCKET UPNP_E_SOCKET_BIND +//#define HTTP_E_WRITE_SOCKET UPNP_E_SOCKET_WRITE +//#define HTTP_E_CONNECT_SOCKET UPNP_E_SOCKET_CONNECT +//#define HTTP_E_SOCKET UPNP_E_OUTOF_SOCKET +//#define HTTP_E_BAD_RESPONSE UPNP_E_BAD_RESPONSE +//#define HTTP_E_BAD_REQUEST UPNP_E_BAD_REQUEST +//#define HTTP_E_BAD_IP_ADDRESS UPNP_E_INVALID_URL + +//#define RESPONSE_TIMEOUT 30 + +/************************************************************************ +* Commented typedefs * +************************************************************************/ +//Buffer used to store data read from a socket during an http transfer +//in function read_bytes. +//typedef struct SOCKET_BUFFER{ +// char buff[SOCKET_BUFFER_SIZE]; +// int size; +// struct SOCKET_BUFFER *next; +//} socket_buffer; + +//typedef struct HTTP_HEADER { +// token header; +// token value; +// struct HTTP_HEADER * next; +//} http_header; + +//typedef struct HTTP_STATUS_LINE{ +// token http_version; +// token status_code; +// token reason_phrase; +//} http_status; + +//typedef struct HTTP_REQUEST_LINE { +// token http_version; +// uri_type request_uri; +// token method; +//} http_request; + +//Represents a parsed HTTP_MESSAGE head_list is dynamically allocated +//typedef struct HTTP_MESSAGE { +// http_status status; +// http_request request; +// http_header * header_list; +// token content; +//} http_message; + +/************************************************************************ +* Commented functions * +************************************************************************ + +EXTERN_C int transferHTTP( char * request, char * toSend, + int toSendSize, char **out, char * Url); + + +EXTERN_C int transferHTTPRaw( char * toSend, int toSendSize, + char **out, char *URL); + +helper function +EXTERN_C int transferHTTPparsedURL( char * request, + char * toSend, int toSendSize, + char **out, uri_type *URL); + +assumes that char * out has enough space ( 38 characters) +outputs the current time in the following null terminated string: + "DATE: Sun, Jul 06 2000 08:53:01 GMT\r\n" +EXTERN_C void currentTmToHttpDate(char *out); + +EXTERN_C int parse_http_response( char * in, http_message * out, + int max_len); + +EXTERN_C int parse_http_request( char * in, http_message *out, + int max_len); + +EXTERN_C void print_http_message( http_message * message); +EXTERN_C int search_for_header( http_message * in, + char * header, token *out_value); + +EXTERN_C void print_status_line(http_status *in); +EXTERN_C void print_request_line(http_request *in); +EXTERN_C int parse_http_line( char * in, int max_size); +EXTERN_C int parse_not_LWS( char *in, token *out, int max_size); +EXTERN_C int parse_LWS( char * in, int max_size); + +EXTERN_C size_t write_bytes(int fd, char * bytes, size_t n, + int timeout); +EXTERN_C void free_http_message(http_message * message); + +************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif // GENLIB_NET_URI_H + diff --git a/libupnp/upnp/src/inc/urlconfig.h b/libupnp/upnp/src/inc/urlconfig.h new file mode 100644 index 0000000..6885210 --- /dev/null +++ b/libupnp/upnp/src/inc/urlconfig.h @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef URLCONFIG_H +#define URLCONFIG_H +#include +#include +#include + +// functions available only if the web server is included + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************ +* Function : configure_urlbase +* +* Parameters : +* INOUT IXML_Document *doc ; IXML Description document +* IN const struct sockaddr_in* serverAddr ; socket address object +* providing the IP address and port information +* IN const char* alias ; string containing the alias +* IN time_t last_modified ; time when the XML document was +* downloaded +* OUT char docURL[LINE_SIZE] ; buffer to hold the URL of the +* document. +* +* Description : Configure the full URL for the description document. +* Create the URL document and add alias, description information. +* The doc is added to the web server to be served using the given +* alias. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - Default Error +* +* Note : +****************************************************************************/ +int configure_urlbase( INOUT IXML_Document *doc, + IN const struct sockaddr_in* serverAddr, + IN const char* alias, + IN time_t last_modified, + OUT char docURL[LINE_SIZE] ); + + +#ifdef __cplusplus +} // extern C +#endif + +#endif /* URLCONFIG_H */ + diff --git a/libupnp/upnp/src/inc/util.h b/libupnp/upnp/src/inc/util.h new file mode 100644 index 0000000..c95f8f1 --- /dev/null +++ b/libupnp/upnp/src/inc/util.h @@ -0,0 +1,153 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_H +#define UTIL_H +#include "upnp.h" + +// usually used to specify direction of parameters in functions +#ifndef IN +#define IN +#endif + +#ifndef OUT +#define OUT +#endif + +#ifndef INOUT +#define INOUT +#endif + + +#ifdef NO_DEBUG +#define DBG(x) +#else +#define DBG(x) x +#endif + +#define GEMD_OUT_OF_MEMORY -1 +#define EVENT_TIMEDOUT -2 +#define EVENT_TERMINATE -3 + + + +#define max(a, b) (((a)>(b))? (a):(b)) +#define min(a, b) (((a)<(b))? (a):(b)) + + + +// boolean type in C +typedef char xboolean; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/////////////////////////// +// funcs + +#ifdef __cplusplus +extern "C" { +#endif + +//void log_error( IN const char *fmt, ... ); + +/************************************************************************ +* Function : linecopy +* +* Parameters : +* OUT char dest[LINE_SIZE] ; output buffer +* IN const char* src ; input buffer +* +* Description : Copy no of bytes spcified by the LINE_SIZE constant, +* from the source buffer. Null terminate the destination buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +void linecopy( OUT char dest[LINE_SIZE], IN const char* src ); + +/************************************************************************ +* Function : namecopy +* +* Parameters : +* OUT char dest[NAME_SIZE] ; output buffer +* IN const char* src ; input buffer +* +* Description : Copy no of bytes spcified by the NAME_SIZE constant, +* from the source buffer. Null terminate the destination buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +void namecopy( OUT char dest[NAME_SIZE], IN const char* src ); + +/************************************************************************ +* Function : linecopylen +* +* Parameters : +* OUT char dest[LINE_SIZE] ; output buffer +* IN const char* src ; input buffer +* IN size_t srclen ; bytes to be copied. +* +* Description : Determine if the srclen passed in paramter is less than +* the permitted LINE_SIZE. If it is use the passed parameter, if not +* use the permitted LINE_SIZE as the length parameter +* Copy no of bytes spcified by the LINE_SIZE constant, +* from the source buffer. Null terminate the destination buffer +* +* Return : void ; +* +* Note : +************************************************************************/ +void linecopylen( OUT char dest[LINE_SIZE], IN const char* src, IN size_t srclen ); + + +#ifdef __cplusplus +} // extern C +#endif + +////////////////////////////////// + +// C specific +#ifndef __cplusplus + +#define XINLINE inline + +#endif // __cplusplus + +#endif /* GENLIB_UTIL_UTIL_H */ diff --git a/libupnp/upnp/src/inc/utilall.h b/libupnp/upnp/src/inc/utilall.h new file mode 100644 index 0000000..bfe8e4c --- /dev/null +++ b/libupnp/upnp/src/inc/utilall.h @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include + diff --git a/libupnp/upnp/src/inc/uuid.h b/libupnp/upnp/src/inc/uuid.h new file mode 100644 index 0000000..54980f8 --- /dev/null +++ b/libupnp/upnp/src/inc/uuid.h @@ -0,0 +1,50 @@ + /* + ** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. + ** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & + ** Digital Equipment Corporation, Maynard, Mass. + ** Copyright (c) 1998 Microsoft. + ** To anyone who acknowledges that this file is provided "AS IS" + ** without any express or implied warranty: permission to use, copy, + ** modify, and distribute this file for any purpose is hereby + ** granted without fee, provided that the above copyright notices and + ** this notice appears in all source code copies, and that none of + ** the names of Open Software Foundation, Inc., Hewlett-Packard + ** Company, or Digital Equipment Corporation be used in advertising + ** or publicity pertaining to distribution of the software without + ** specific, written prior permission. Neither Open Software + ** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment + ** Corporation makes any representations about the suitability of + ** this software for any purpose. + */ + + typedef struct _uuid_upnp { + unsigned32 time_low; + unsigned16 time_mid; + unsigned16 time_hi_and_version; + unsigned8 clock_seq_hi_and_reserved; + unsigned8 clock_seq_low; + byte node[6]; + } uuid_upnp; + + /* uuid_create -- generate a UUID */ + int uuid_create(uuid_upnp * id); + void uuid_unpack(uuid_upnp *u, char *out); // out will be xxxx-xx-xx-xx-xxxxxx format + + /* uuid_create_from_name -- create a UUID using a "name" + from a "name space" */ + void uuid_create_from_name( + uuid_upnp * uid, /* resulting UUID */ + uuid_upnp nsid, /* UUID to serve as context, so identical + names from different name spaces generate + different UUIDs */ + void * name, /* the name from which to generate a UUID */ + int namelen /* the length of the name */ + ); + + /* uuid_compare -- Compare two UUID's "lexically" and return + -1 u1 is lexically before u2 + 0 u1 is equal to u2 + 1 u1 is lexically after u2 + Note: lexical ordering is not temporal ordering! + */ + int uuid_compare(uuid_upnp *u1, uuid_upnp *u2); diff --git a/libupnp/upnp/src/inc/webserver.h b/libupnp/upnp/src/inc/webserver.h new file mode 100644 index 0000000..0f9bc55 --- /dev/null +++ b/libupnp/upnp/src/inc/webserver.h @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef GENLIB_NET_HTTP_WEBSERVER_H +#define GENLIB_NET_HTTP_WEBSERVER_H + +#include +#include "sock.h" +#include "httpparser.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct SendInstruction +{ + int IsVirtualFile; + int IsChunkActive; + int IsRangeActive; + int IsTrailers; + char RangeHeader[200]; + long RangeOffset; + long ReadSendSize; // Read from local source and send on the network. + long RecvWriteSize; // Recv from the network and write into local file. + + //Later few more member could be added depending on the requirement. +}; + +/************************************************************************ +* Function: web_server_init +* +* Parameters: +* none +* +* Description: Initilialize the different documents. Initialize the +* memory for root directory for web server. Call to initialize global +* XML document. Sets bWebServerState to WEB_SERVER_ENABLED +* +* Returns: +* 0 - OK +* UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here +************************************************************************/ +int web_server_init( void ); + +/************************************************************************ +* Function: web_server_destroy +* +* Parameters: +* none +* +* Description: Release memory allocated for the global web server root +* directory and the global XML document +* Resets the flag bWebServerState to WEB_SERVER_DISABLED +* +* Returns: +* void +************************************************************************/ +void web_server_destroy( void ); + +/************************************************************************ +* Function: web_server_set_alias +* +* Parameters: +* alias_name: webserver name of alias; created by caller and freed by +* caller (doesn't even have to be malloc()d .) +* alias_content: the xml doc; this is allocated by the caller; and +* freed by the web server +* alias_content_length: length of alias body in bytes +* last_modified: time when the contents of alias were last +* changed (local time) +* +* Description: Replaces current alias with the given alias. To remove +* the current alias, set alias_name to NULL. +* +* Returns: +* 0 - OK +* UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here +************************************************************************/ +int web_server_set_alias( IN const char* alias_name, + IN const char* alias_content, IN size_t alias_content_length, + IN time_t last_modified ); + +/************************************************************************ +* Function: web_server_set_root_dir +* +* Parameters: +* IN const char* root_dir ; String having the root directory for the +* document +* +* Description: Assign the path specfied by the IN const char* root_dir +* parameter to the global Document root directory. Also check for +* path names ending in '/' +* +* Returns: +* int +************************************************************************/ +int web_server_set_root_dir( IN const char* root_dir ); + +/************************************************************************ +* Function: web_server_callback * +* * +* Parameters: * +* IN http_parser_t *parser, * +* INOUT http_message_t* req, * +* IN SOCKINFO *info * +* * +* Description: main entry point into web server; * +* handles HTTP GET and HEAD requests * +* * +* Returns: * +* void * +************************************************************************/ +void web_server_callback( IN http_parser_t *parser, IN http_message_t* req, INOUT SOCKINFO *info ); + + +#ifdef __cplusplus +} // extern C +#endif + + +#endif // GENLIB_NET_HTTP_WEBSERVER_H diff --git a/libupnp/upnp/src/soap/soap_common.c b/libupnp/upnp/src/soap/soap_common.c new file mode 100644 index 0000000..90e72bb --- /dev/null +++ b/libupnp/upnp/src/soap/soap_common.c @@ -0,0 +1,15 @@ + +#include "config.h" +#if EXCLUDE_SOAP == 0 + +#include "httpparser.h" +#include "sock.h" +#include "soaplib.h" + + +const char *ContentTypeHeader = + "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"; + + +#endif // EXCLUDE_SOAP + diff --git a/libupnp/upnp/src/soap/soap_ctrlpt.c b/libupnp/upnp/src/soap/soap_ctrlpt.c new file mode 100644 index 0000000..56464fd --- /dev/null +++ b/libupnp/upnp/src/soap/soap_ctrlpt.c @@ -0,0 +1,931 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#ifdef INCLUDE_CLIENT_APIS +#if EXCLUDE_SOAP == 0 + +#include +#include +#include +#include +#include + +#include "miniserver.h" +#include "membuffer.h" +#include "httpparser.h" +#include "httpreadwrite.h" +#include "statcodes.h" +#include "parsetools.h" +#include "upnpapi.h" +#include "soaplib.h" +#include "uri.h" +#include "upnp.h" + +#include "unixutil.h" + +#define SOAP_ACTION_RESP 1 +#define SOAP_VAR_RESP 2 +//#define SOAP_ERROR_RESP 3 +#define SOAP_ACTION_RESP_ERROR 3 +#define SOAP_VAR_RESP_ERROR 4 + +/**************************************************************************** +* Function : dom_cmp_name +* +* Parameters : +* IN char *name : lookup name +* IN IXML_Node *node : xml node +* +* Description : This function compares 'name' and node's name +* +* Return : int +* 0 if both are equal; 1 if not equal, and UPNP_E_OUTOF_MEMORY +* +* Note : +****************************************************************************/ +static int +dom_cmp_name( IN char *name, + IN IXML_Node * node ) +{ + const DOMString node_name = NULL; + memptr nameptr, + dummy; + int ret_code; + + assert( name ); + assert( node ); + + node_name = ixmlNode_getNodeName( node ); + if( node_name == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + if( strcmp( name, node_name ) == 0 ) { + ret_code = 0; + } else if( matchstr( ( char * )node_name, strlen( node_name ), + "%s:%s%0", &dummy, &nameptr ) == PARSE_OK && + strcmp( nameptr.buf, name ) == 0 ) { + ret_code = 0; + } else { + ret_code = 1; // names are not the same + } + + return ret_code; +} + +/**************************************************************************** +* Function : dom_find_node +* +* Parameters : +* IN char* node_name : name of the node +* IN IXML_Node *start_node : complete xml node +* OUT IXML_Node ** matching_node : matched node +* +* Description : This function goes thru each child of 'start_node' +* looking for a node having the name 'node_name'. +* +* Return : int +* return UPNP_E_SUCCESS if successful else returns appropriate error +* +* Note : +****************************************************************************/ +static int +dom_find_node( IN char *node_name, + IN IXML_Node * start_node, + OUT IXML_Node ** matching_node ) +{ + IXML_Node *node; + + // invalid args + if( node_name == NULL || start_node == NULL ) { + return UPNP_E_NOT_FOUND; + } + + node = ixmlNode_getFirstChild( start_node ); + while( node != NULL ) { + // match name + if( dom_cmp_name( node_name, node ) == 0 ) { + *matching_node = node; + return UPNP_E_SUCCESS; + } + // free and next node + node = ixmlNode_getNextSibling( node ); // next node + } + + return UPNP_E_NOT_FOUND; +} + +/**************************************************************************** +* Function : dom_find_deep_node +* +* Parameters : +* IN char* names[] : array of names +* IN int num_names : size of array +* IN IXML_Node *start_node : Node from where it should should be +* searched +* OUT IXML_Node ** matching_node : Node that matches the last name +* of the array +* +* Description : This function searches for the node specifed by the last +* name in the 'name' array. +* +* Return : int +* return UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +static int +dom_find_deep_node( IN char *names[], + IN int num_names, + IN IXML_Node * start_node, + OUT IXML_Node ** matching_node ) +{ + int i; + IXML_Node *node; + IXML_Node *match_node; + + assert( num_names > 0 ); + + node = start_node; + if( dom_cmp_name( names[0], start_node ) == 0 ) { + if( num_names == 1 ) { + *matching_node = start_node; + return UPNP_E_SUCCESS; + } + } + + for( i = 1; i < num_names; i++ ) { + if( dom_find_node( names[i], node, &match_node ) != + UPNP_E_SUCCESS ) { + return UPNP_E_NOT_FOUND; + } + + if( i == num_names - 1 ) { + *matching_node = match_node; + return UPNP_E_SUCCESS; + } + + node = match_node; // try again + } + + return UPNP_E_NOT_FOUND; // this line not reached +} + +/**************************************************************************** +* Function : get_node_value +* +* Parameters : +* IN IXML_Node *node : input node +* +* Description : This function returns the value of the text node +* +* Return : DOMString +* string containing the node value +* +* Note :The given node must have a text node as its first child +****************************************************************************/ +static DOMString +get_node_value( IN IXML_Node * node ) +{ + IXML_Node *text_node = NULL; + DOMString text_value = NULL; + + text_node = ixmlNode_getFirstChild( node ); + if( text_node == NULL ) { + return NULL; + } + + text_value = ixmlNode_getNodeValue( text_node ); + return text_value; +} + +/**************************************************************************** +* Function : get_host_and_path +* +* Parameters : +* IN char *ctrl_url : URL +* OUT memptr *host : host string +* OUT memptr *path : path string +* OUT uri_type* url : URL type +* +* Description : This function retrives the host and path from the +* control URL +* +* Return : int +* returns 0 on sucess; -1 on error +* +* Note : +****************************************************************************/ +static XINLINE int +get_host_and_path( IN char *ctrl_url, + OUT memptr * host, + OUT memptr * path, + OUT uri_type * url ) +{ + if( parse_uri( ctrl_url, strlen( ctrl_url ), url ) != HTTP_SUCCESS ) { + return -1; + } + host->buf = url->hostport.text.buff; + host->length = url->hostport.text.size; + + path->buf = url->pathquery.buff; + path->length = url->pathquery.size; + + return 0; +} + +/**************************************************************************** +* Function : get_action_name +* +* Parameters : +* IN char* action : string containing action name +* OUT memptr* name : name of the action +* +* Description : This functions retirves the action name in the buffer +* +* Return : int +* returns 0 on success; -1 on error +* +* Note : +****************************************************************************/ +static XINLINE int +get_action_name( IN char *action, + OUT memptr * name ) +{ + memptr dummy; + int ret_code; + + ret_code = + matchstr( action, strlen( action ), " <%s:%s", &dummy, name ); + + return ret_code == PARSE_OK ? 0 : -1; +} + +/**************************************************************************** +* Function : add_man_header +* +* Parameters : +* INOUT membuffer* headers : HTTP header +* +* Description : This function adds "MAN" field in the HTTP header +* +* Return : int +* returns 0 on success; UPNP_E_OUTOFMEMORY on error +* +* Note : +****************************************************************************/ +static XINLINE int +add_man_header( INOUT membuffer * headers ) +{ + char *soap_action_hdr; + char *man_hdr = "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; " + "ns=01\r\n01-"; + + // change POST to M-POST + if( membuffer_insert( headers, "M-", 2, 0 ) != 0 ) { + return UPNP_E_OUTOF_MEMORY; + } + + soap_action_hdr = strstr( headers->buf, "SOAPACTION:" ); + assert( soap_action_hdr != NULL ); // can't fail + + // insert MAN header + if( membuffer_insert( headers, man_hdr, strlen( man_hdr ), + soap_action_hdr - headers->buf ) != 0 ) { + return UPNP_E_OUTOF_MEMORY; + } + + return 0; +} + +/**************************************************************************** +* Function : soap_request_and_response +* +* Parameters : +* IN membuffer* request : request that will be sent to the device +* IN uri_type* destination_url : destination address string +* OUT http_parser_t *response : response from the device +* +* Description : This function sends the control point's request to the +* device and receives a response from it. +* +* Return : int +* +* Note : +****************************************************************************/ +static int +soap_request_and_response( IN membuffer * request, + IN uri_type * destination_url, + OUT http_parser_t * response ) +{ + int ret_code; + + ret_code = http_RequestAndResponse( destination_url, request->buf, + request->length, + SOAPMETHOD_POST, + UPNP_TIMEOUT, response ); + if( ret_code != 0 ) { + httpmsg_destroy( &response->msg ); + return ret_code; + } + // method-not-allowed error + if( response->msg.status_code == HTTP_METHOD_NOT_ALLOWED ) { + ret_code = add_man_header( request ); // change to M-POST msg + if( ret_code != 0 ) { + return ret_code; + } + + httpmsg_destroy( &response->msg ); // about to reuse response + + // try again + ret_code = http_RequestAndResponse( destination_url, request->buf, + HTTPMETHOD_MPOST, + request->length, UPNP_TIMEOUT, + response ); + if( ret_code != 0 ) { + httpmsg_destroy( &response->msg ); + } + + } + + return ret_code; +} + +/**************************************************************************** +* Function : get_response_value +* +* Parameters : +* IN http_message_t* hmsg : HTTP response message +* IN int code : return code in the HTTP response +* IN char*name : name of the action +* OUT int *upnp_error_code : UPnP error code +* OUT IXML_Node ** action_value : SOAP response node +* OUT DOMString * str_value : state varible value ( in the case of +* querry state variable request) +* +* Description : This function handles the response coming back from the +* device. This function parses the response and gives back the SOAP +* response node. +* +* Return : int +* return the type of the SOAP message if successful else returns +* appropriate error. +* +* Note : +****************************************************************************/ +static int +get_response_value( IN http_message_t * hmsg, + IN int code, + IN char *name, + OUT int *upnp_error_code, + OUT IXML_Node ** action_value, + OUT DOMString * str_value ) +{ + IXML_Node *node = NULL; + IXML_Node *root_node = NULL; + IXML_Node *error_node = NULL; + IXML_Document *doc = NULL; + char *node_str = NULL; + char *temp_str = NULL; + DOMString error_node_str = NULL; + int err_code; + xboolean done = FALSE; + char *names[5]; + DOMString nodeValue; + + err_code = UPNP_E_BAD_RESPONSE; // default error + + // only 200 and 500 status codes are relevant + if( ( hmsg->status_code != HTTP_OK && + hmsg->status_code != HTTP_INTERNAL_SERVER_ERROR ) || + !has_xml_content_type( hmsg ) ) { + + goto error_handler; + } + + if( ixmlParseBufferEx( hmsg->entity.buf, &doc ) != IXML_SUCCESS ) { + + goto error_handler; + } + + root_node = ixmlNode_getFirstChild( ( IXML_Node * ) doc ); + if( root_node == NULL ) { + + goto error_handler; + } + + if( code == SOAP_ACTION_RESP ) { + // + // try reading soap action response + // + assert( action_value != NULL ); + + *action_value = NULL; + + names[0] = "Envelope"; + names[1] = "Body"; + names[2] = name; + if( dom_find_deep_node( names, 3, root_node, &node ) == + UPNP_E_SUCCESS ) { + node_str = ixmlPrintNode( node ); + if( node_str == NULL ) { + err_code = UPNP_E_OUTOF_MEMORY; + goto error_handler; + } + + if( ixmlParseBufferEx( node_str, + ( IXML_Document ** ) action_value ) != + IXML_SUCCESS ) { + err_code = UPNP_E_BAD_RESPONSE; + goto error_handler; + } + + err_code = SOAP_ACTION_RESP; + done = TRUE; + } + } else if( code == SOAP_VAR_RESP ) { + // try reading var response + assert( str_value != NULL ); + *str_value = NULL; + + names[0] = "Envelope"; + names[1] = "Body"; + names[2] = "QueryStateVariableResponse"; + names[3] = "return"; + if( dom_find_deep_node( names, 4, root_node, &node ) + == UPNP_E_SUCCESS ) { + nodeValue = get_node_value( node ); + if( nodeValue == NULL ) { + goto error_handler; + } + + *str_value = ixmlCloneDOMString( nodeValue ); + err_code = SOAP_VAR_RESP; + done = TRUE; + } + } + + if( !done ) { + // not action or var resp; read error code and description + *str_value = NULL; + + names[0] = "Envelope"; + names[1] = "Body"; + names[2] = "Fault"; + names[3] = "detail"; + names[4] = "UPnPError"; + if( dom_find_deep_node( names, 5, root_node, &error_node ) + != UPNP_E_SUCCESS ) { + goto error_handler; + } + + if( dom_find_node( "errorCode", error_node, &node ) + != UPNP_E_SUCCESS ) { + goto error_handler; + } + + temp_str = get_node_value( node ); + if( temp_str == NULL ) { + goto error_handler; + } + + *upnp_error_code = atoi( temp_str ); + if( *upnp_error_code < 400 ) { + err_code = *upnp_error_code; + goto error_handler; // bad SOAP error code + } + + if( code == SOAP_VAR_RESP ) { + if( dom_find_node( "errorDescription", error_node, &node ) + != UPNP_E_SUCCESS ) { + goto error_handler; + } + + nodeValue = get_node_value( node ); + if( nodeValue == NULL ) { + goto error_handler; + } + *str_value = ixmlCloneDOMString( nodeValue ); + if( *str_value == NULL ) { + goto error_handler; + } + err_code = SOAP_VAR_RESP_ERROR; + } + + else if( code == SOAP_ACTION_RESP ) { + error_node_str = ixmlPrintNode( error_node ); + if( error_node_str == NULL ) { + err_code = UPNP_E_OUTOF_MEMORY; + goto error_handler; + } + + if( ixmlParseBufferEx( error_node_str, + ( IXML_Document ** ) action_value ) != + IXML_SUCCESS ) { + err_code = UPNP_E_BAD_RESPONSE; + + goto error_handler; + } + err_code = SOAP_ACTION_RESP_ERROR; + } + } + + error_handler: + + ixmlDocument_free( doc ); + ixmlFreeDOMString( node_str ); + ixmlFreeDOMString( error_node_str ); + return err_code; +} + +/**************************************************************************** +* Function : SoapSendAction +* +* Parameters : +* IN char* action_url : device contrl URL +* IN char *service_type : device service type +* IN IXML_Document *action_node : SOAP action node +* OUT IXML_Document **response_node : SOAP response node +* +* Description : This function is called by UPnP API to send the SOAP +* action request and waits till it gets the response from the device +* pass the response to the API layer +* +* Return : int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +int +SoapSendAction( IN char *action_url, + IN char *service_type, + IN IXML_Document * action_node, + OUT IXML_Document ** response_node ) +{ + char *action_str = NULL; + memptr name; + membuffer request; + membuffer responsename; + int err_code; + int ret_code; + http_parser_t response; + uri_type url; + int upnp_error_code; + char *upnp_error_str; + xboolean got_response = FALSE; + + char *xml_start = + "\n" + ""; + char *xml_end = "\n" "\n"; + int xml_start_len; + int xml_end_len; + int action_str_len; + + *response_node = NULL; // init + + err_code = UPNP_E_OUTOF_MEMORY; // default error + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "Inside SoapSendAction():" ); + ) + // init + membuffer_init( &request ); + membuffer_init( &responsename ); + + // print action + action_str = ixmlPrintNode( ( IXML_Node * ) action_node ); + if( action_str == NULL ) { + goto error_handler; + } + // get action name + if( get_action_name( action_str, &name ) != 0 ) { + err_code = UPNP_E_INVALID_ACTION; + goto error_handler; + } + // parse url + if( http_FixStrUrl( action_url, strlen( action_url ), &url ) != 0 ) { + err_code = UPNP_E_INVALID_URL; + goto error_handler; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "path=%.*s, hostport=%.*s\n", + url.pathquery.size, url.pathquery.buff, + url.hostport.text.size, + url.hostport.text.buff ); ) + + xml_start_len = strlen( xml_start ); + xml_end_len = strlen( xml_end ); + action_str_len = strlen( action_str ); + + // make request msg + request.size_inc = 50; + if( http_MakeMessage( &request, 1, 1, "q" "N" "s" "sssbs" "U" "c" "bbb", SOAPMETHOD_POST, &url, xml_start_len + action_str_len + xml_end_len, // content-length + ContentTypeHeader, + "SOAPACTION: \"", service_type, "#", name.buf, + name.length, "\"\r\n", xml_start, xml_start_len, + action_str, action_str_len, xml_end, + xml_end_len ) != 0 ) { + goto error_handler; + } + + ret_code = soap_request_and_response( &request, &url, &response ); + got_response = TRUE; + if( ret_code != UPNP_E_SUCCESS ) { + err_code = ret_code; + goto error_handler; + } + + if( membuffer_append( &responsename, name.buf, name.length ) != 0 || + membuffer_append_str( &responsename, "Response" ) != 0 ) { + goto error_handler; + } + // get action node from the response + ret_code = get_response_value( &response.msg, SOAP_ACTION_RESP, + responsename.buf, &upnp_error_code, + ( IXML_Node ** ) response_node, + &upnp_error_str ); + + if( ret_code == SOAP_ACTION_RESP ) { + err_code = UPNP_E_SUCCESS; + } else if( ret_code == SOAP_ACTION_RESP_ERROR ) { + err_code = upnp_error_code; + } else { + err_code = ret_code; + } + + error_handler: + ixmlFreeDOMString( action_str ); + membuffer_destroy( &request ); + membuffer_destroy( &responsename ); + if( got_response ) { + httpmsg_destroy( &response.msg ); + } + + return err_code; +} + +/**************************************************************************** +* Function : SoapSendActionEx +* +* Parameters : +* IN char* action_url : device contrl URL +* IN char *service_type : device service type + IN IXML_Document *Header: Soap header +* IN IXML_Document *action_node : SOAP action node ( SOAP body) +* OUT IXML_Document **response_node : SOAP response node +* +* Description : This function is called by UPnP API to send the SOAP +* action request and waits till it gets the response from the device +* pass the response to the API layer. This action is similar to the +* the SoapSendAction with only difference that it allows users to +* pass the SOAP header along the SOAP body ( soap action request) +* +* Return : int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +int +SoapSendActionEx( IN char *action_url, + IN char *service_type, + IN IXML_Document * header, + IN IXML_Document * action_node, + OUT IXML_Document ** response_node ) +{ + char *xml_header_str = NULL; + char *action_str = NULL; + memptr name; + membuffer request; + membuffer responsename; + int err_code; + int ret_code; + http_parser_t response; + uri_type url; + int upnp_error_code; + char *upnp_error_str; + xboolean got_response = FALSE; + + char *xml_start = + "\n"; + char *xml_body_start = ""; + char *xml_end = "\n" "\n"; + int xml_start_len; + int xml_end_len; + char *xml_header_start = "\n"; + char *xml_header_end = "\n"; + int xml_header_start_len; + int xml_header_end_len; + int xml_header_str_len; + int action_str_len; + int xml_body_start_len; + + *response_node = NULL; // init + + err_code = UPNP_E_OUTOF_MEMORY; // default error + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "Inside SoapSendActionEx():" ); + ) + // init + membuffer_init( &request ); + membuffer_init( &responsename ); + + // header string + xml_header_str = ixmlPrintNode( ( IXML_Node * ) header ); + if( xml_header_str == NULL ) { + goto error_handler; + } + // print action + action_str = ixmlPrintNode( ( IXML_Node * ) action_node ); + if( action_str == NULL ) { + goto error_handler; + } + // get action name + if( get_action_name( action_str, &name ) != 0 ) { + err_code = UPNP_E_INVALID_ACTION; + goto error_handler; + } + // parse url + if( http_FixStrUrl( action_url, strlen( action_url ), &url ) != 0 ) { + err_code = UPNP_E_INVALID_URL; + goto error_handler; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "path=%.*s, hostport=%.*s\n", + url.pathquery.size, url.pathquery.buff, + url.hostport.text.size, + url.hostport.text.buff ); ) + + xml_start_len = strlen( xml_start ); + xml_body_start_len = strlen( xml_body_start ); + xml_end_len = strlen( xml_end ); + action_str_len = strlen( action_str ); + + xml_header_start_len = strlen( xml_header_start ); + xml_header_end_len = strlen( xml_header_end ); + xml_header_str_len = strlen( xml_header_str ); + + // make request msg + request.size_inc = 50; + if( http_MakeMessage( &request, 1, 1, "q" "N" "s" "sssbs" "U" "c" "bbbbbbb", SOAPMETHOD_POST, &url, xml_start_len + xml_header_start_len + xml_header_str_len + xml_header_end_len + xml_body_start_len + action_str_len + xml_end_len, // content-length + ContentTypeHeader, + "SOAPACTION: \"", service_type, "#", name.buf, + name.length, "\"\r\n", + xml_start, xml_start_len, + xml_header_start, xml_header_start_len, + xml_header_str, xml_header_str_len, + xml_header_end, xml_header_end_len, + xml_body_start, xml_body_start_len, + action_str, action_str_len, + xml_end, xml_end_len ) != 0 ) { + goto error_handler; + } + + ret_code = soap_request_and_response( &request, &url, &response ); + got_response = TRUE; + if( ret_code != UPNP_E_SUCCESS ) { + err_code = ret_code; + goto error_handler; + } + + if( membuffer_append( &responsename, name.buf, name.length ) != 0 || + membuffer_append_str( &responsename, "Response" ) != 0 ) { + goto error_handler; + } + // get action node from the response + ret_code = get_response_value( &response.msg, SOAP_ACTION_RESP, + responsename.buf, &upnp_error_code, + ( IXML_Node ** ) response_node, + &upnp_error_str ); + + if( ret_code == SOAP_ACTION_RESP ) { + err_code = UPNP_E_SUCCESS; + } else if( ret_code == SOAP_ACTION_RESP_ERROR ) { + err_code = upnp_error_code; + } else { + err_code = ret_code; + } + + error_handler: + + ixmlFreeDOMString( action_str ); + ixmlFreeDOMString( xml_header_str ); + membuffer_destroy( &request ); + membuffer_destroy( &responsename ); + if( got_response ) { + httpmsg_destroy( &response.msg ); + } + + return err_code; +} + +/**************************************************************************** +* Function : SoapGetServiceVarStatus +* +* Parameters : +* IN char * action_url : Address to send this variable +* query message. +* IN char *var_name : Name of the variable. +* OUT char **var_value : Output value. +* +* Description : This function creates a status variable query message +* send it to the specified URL. It also collect the response. +* +* Return : int +* +* Note : +****************************************************************************/ +int +SoapGetServiceVarStatus( IN char *action_url, + IN char *var_name, + OUT char **var_value ) +{ + memptr host; // value for HOST header + memptr path; // ctrl path in first line in msg + uri_type url; + membuffer request; + int ret_code; + http_parser_t response; + int upnp_error_code; + + char *xml_start = + "\n" + "\n" + "\n" + ""; + + char *xml_end = "\n" + "\n" "\n" "\n"; + + *var_value = NULL; // return NULL in case of an error + + membuffer_init( &request ); + + // get host hdr and url path + if( get_host_and_path( action_url, &host, &path, &url ) == -1 ) { + return UPNP_E_INVALID_URL; + } + // make headers + request.size_inc = 50; + if( http_MakeMessage( &request, 1, 1, "Q" "sbc" "N" "s" "s" "U" "c" "sss", SOAPMETHOD_POST, path.buf, path.length, "HOST: ", host.buf, host.length, strlen( xml_start ) + strlen( var_name ) + strlen( xml_end ), // content-length + ContentTypeHeader, + "SOAPACTION: \"urn:schemas" + "-upnp-org:control-1-0#QueryStateVariable\"\r\n", + xml_start, var_name, xml_end ) != 0 ) { + return UPNP_E_OUTOF_MEMORY; + } + // send msg and get reply + ret_code = soap_request_and_response( &request, &url, &response ); + membuffer_destroy( &request ); + if( ret_code != UPNP_E_SUCCESS ) { + return ret_code; + } + // get variable value from the response + ret_code = get_response_value( &response.msg, SOAP_VAR_RESP, NULL, + &upnp_error_code, NULL, var_value ); + + httpmsg_destroy( &response.msg ); + + if( ret_code == SOAP_VAR_RESP ) { + return UPNP_E_SUCCESS; + } else if( ret_code == SOAP_VAR_RESP_ERROR ) { + return upnp_error_code; + } else { + return ret_code; + } +} + +#endif // EXCLUDE_SOAP +#endif // INCLUDE_CLIENT_APIS diff --git a/libupnp/upnp/src/soap/soap_device.c b/libupnp/upnp/src/soap/soap_device.c new file mode 100644 index 0000000..f521774 --- /dev/null +++ b/libupnp/upnp/src/soap/soap_device.c @@ -0,0 +1,1067 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#ifdef INCLUDE_DEVICE_APIS +#if EXCLUDE_SOAP == 0 + +#define SOAP_BODY "Body" +#define SOAP_URN "http://schemas.xmlsoap.org/soap/envelope/" + +#define QUERY_STATE_VAR_URN "urn:schemas-upnp-org:control-1-0" + +#include "upnpapi.h" +#include "parsetools.h" +#include "statcodes.h" +#include "httpparser.h" +#include "httpreadwrite.h" +#include "unixutil.h" +#include "soaplib.h" +#include "ssdplib.h" + +// timeout duration in secs for transmission/reception +#define SOAP_TIMEOUT UPNP_TIMEOUT + +#define SREQ_HDR_NOT_FOUND -1 +#define SREQ_BAD_HDR_FORMAT -2 + +#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 + +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"; + + +/**************************************************************************** +* Function : get_request_type +* +* Parameters : +* IN http_message_t* request : HTTP request +* OUT memptr* action_name : SOAP action name +* +* Description : This function retrives the name of the SOAP action +* +* Return : int +* 0 if successful else returns appropriate error. +* Note : +****************************************************************************/ +static XINLINE int +get_request_type( IN http_message_t * request, + OUT memptr * action_name ) +{ + memptr value; + memptr ns_value, + dummy_quote; + http_header_t *hdr; + char save_char; + char *s; + membuffer soap_action_name; + + // find soapaction header + // + if( request->method == SOAPMETHOD_POST ) { + if( httpmsg_find_hdr( request, HDR_SOAPACTION, &value ) + == NULL ) { + 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 == NULL ) { + 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; + } + + s++; // move to value + + if( matchstr( s, value.length - ( s - value.buf ), "%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; + } + + value.buf[value.length] = save_char; // restore + return 0; +} + +/**************************************************************************** +* Function : send_error_response +* +* Parameters : +* IN SOCKINFO *info : socket info +* IN int error_code : error code +* IN const char* err_msg : error message +* IN http_message_t* hmsg : HTTP request +* +* Description : This function sends SOAP error response +* +* Return : void +* +* Note : +****************************************************************************/ +static void +send_error_response( IN SOCKINFO * info, + IN int error_code, + IN const char *err_msg, + IN http_message_t * hmsg ) +{ + int content_length; + int timeout_secs = SOAP_TIMEOUT; + int major, + minor; + const char *start_body = + "\n" + "\n" + "\n" + "s:Client\n" + "UPnPError\n" + "\n" + "\n" + ""; + + const char *mid_body = "\n" ""; + + const char *end_body = + "\n" + "\n" + "\n" "\n" "\n" "\n"; + + char err_code_str[30]; + + membuffer headers; + + sprintf( err_code_str, "%d", error_code ); + + // calc body len + content_length = strlen( start_body ) + strlen( err_code_str ) + + strlen( mid_body ) + strlen( err_msg ) + strlen( end_body ); + + http_CalcResponseVersion( hmsg->major_version, hmsg->minor_version, + &major, &minor ); + + // make headers + membuffer_init( &headers ); +/* -- PATCH START - Sergey 'Jin' Bostandzhyan */ + if( http_MakeMessage( &headers, major, minor, + "RNsDsSXc" "sssss", + 500, + content_length, + ContentTypeHeader, + "EXT:\r\n", + X_USER_AGENT, + start_body, err_code_str, mid_body, err_msg, + end_body ) != 0 ) { + membuffer_destroy( &headers ); + return; // out of mem + } +/*-- PATCH END - */ + // send err msg + http_SendMessage( info, &timeout_secs, "b", + headers.buf, headers.length ); + + membuffer_destroy( &headers ); +} + +/**************************************************************************** +* Function : send_var_query_response +* +* Parameters : +* IN SOCKINFO *info : socket info +* IN const char* var_value : value of the state variable +* IN http_message_t* hmsg : HTTP request +* +* Description : This function sends response of get var status +* +* Return : void +* +* Note : +****************************************************************************/ +static XINLINE void +send_var_query_response( IN SOCKINFO * info, + IN const char *var_value, + IN http_message_t * hmsg ) +{ + int content_length; + int timeout_secs = SOAP_TIMEOUT; + int major, + minor; + const char *start_body = + "\n" + "\n" + "\n" ""; + + const char *end_body = + "\n" + "\n" + "\n" "\n"; + + membuffer response; + + http_CalcResponseVersion( hmsg->major_version, hmsg->minor_version, + &major, &minor ); + + content_length = strlen( start_body ) + strlen( var_value ) + + strlen( end_body ); + + // make headers + membuffer_init( &response ); + +/* -- PATCH START - Sergey 'Jin' Bostandzhyan */ + if( http_MakeMessage( &response, major, minor, + "RNsDsSXcc" "sss", + HTTP_OK, + content_length, + ContentTypeHeader, + "EXT:\r\n", + X_USER_AGENT, + start_body, var_value, end_body ) != 0 ) { + membuffer_destroy( &response ); + return; // out of mem + } +/* -- PATCH END - */ + + // send msg + http_SendMessage( info, &timeout_secs, "b", + response.buf, response.length ); + + membuffer_destroy( &response ); +} + +/**************************************************************************** +* Function : get_action_node +* +* Parameters : +* IN IXML_Document *TempDoc : The root DOM node. +* IN char *NodeName : IXML_Node name to be searched. +* OUT IXML_Document ** RespNode : Response/Output node. +* +* Description : This function separates the action node from +* the root DOM node. +* +* Return : static XINLINE int +* 0 if successful, or -1 if fails. +* +* Note : +****************************************************************************/ +static XINLINE int +get_action_node( IN IXML_Document * TempDoc, + IN char *NodeName, + OUT 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; + + DBGONLY( 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 == NULL ) { + goto error_handler; + } + + nl = ixmlElement_getElementsByTagNameNS( ( IXML_Element * ) EnvpNode, + "*", "Body" ); + + if( nl == NULL ) { + goto error_handler; + } + + BodyNode = ixmlNodeList_item( nl, 0 ); + + if( BodyNode == NULL ) { + goto error_handler; + } + // Got action node here + ActNode = ixmlNode_getFirstChild( BodyNode ); + if( ActNode == NULL ) { + goto error_handler; + } + //Test whether this is the action node + nodeName = ixmlNode_getNodeName( ActNode ); + if( nodeName == NULL ) { + goto error_handler; + } + + if( strstr( nodeName, NodeName ) == NULL ) { + goto error_handler; + } else { + ActNodeName = ixmlPrintNode( ActNode ); + if( ActNodeName == NULL ) { + goto error_handler; + } + + ret_code = ixmlParseBufferEx( ActNodeName, RespNode ); + if( ret_code != IXML_SUCCESS ) { + ixmlFreeDOMString( ActNodeName ); + ret_code = -1; + goto error_handler; + } + } + + ret_code = 0; // success + + error_handler: + + ixmlFreeDOMString( ActNodeName ); + + if( nl ) + ixmlNodeList_free( nl ); + return ret_code; +} + +/**************************************************************************** +* Function : check_soap_body +* +* Parameters : +* IN IXML_Document *doc : soap body xml document +* IN const char *urn : +* IN const char *actionName : Name of the requested action +* +* Description : This function checks the soap body xml came in the +* SOAP request. +* +* Return : int +* UPNP_E_SUCCESS if successful else returns appropriate error +* +* Note : +****************************************************************************/ +static int +check_soap_body( IN IXML_Document * doc, + IN const char *urn, + 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 ); + + if( ( !strcmp( actionName, name ) ) + && ( !strcmp( urn, ns ) ) ) { + ret_code = UPNP_E_SUCCESS; + } + } + } + ixmlNodeList_free( nl ); + } + return ret_code; + +} + +/**************************************************************************** +* Function : check_soap_action_header +* +* Parameters : +* IN http_message_t *request : HTTP request +* IN const char *urn : +* OUT char **actionName : name of the SOAP action +* +* Description : This function checks the HTTP header of the SOAP request +* coming from the control point +* +* Return : static int +* UPNP_E_SUCCESS if successful else returns appropriate error +* +* Note : +****************************************************************************/ +static int +check_soap_action_header( IN http_message_t * request, + IN const char *urn, + OUT char **actionName ) +{ + memptr header_name; + http_header_t *soap_action_header = NULL; + char *ns_compare = NULL; + int 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 = + ( char * )malloc( soap_action_header->value.length + 1 ); + + if( !temp_header_value ) { + ret_code = UPNP_E_OUTOF_MEMORY; + free( temp_header_value ); + 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 = ( char * )malloc( tempSize ); + + if( !ns_compare ) { + ret_code = UPNP_E_OUTOF_MEMORY; + free( temp_header_value ); + return ret_code; + } + + snprintf( ns_compare, tempSize, "\"%s", urn ); + + if( strcmp( temp_header_value, ns_compare ) ) { + ret_code = UPNP_E_INVALID_ACTION; + } else { + ret_code = UPNP_E_SUCCESS; + temp++; + temp2 = strchr( temp, '\"' ); + + if( temp2 ) //remove ending " if present + { + ( *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; +} + +/**************************************************************************** +* Function : get_device_info +* +* Parameters : +* IN http_message_t* request : HTTP request +* IN int isQuery : flag for a querry +* IN IXML_Document *actionDoc : action request document +* OUT char device_udn[LINE_SIZE] : Device UDN string +* OUT char service_id[LINE_SIZE] : Service ID string +* OUT Upnp_FunPtr *callback : callback function of the device +* application +* OUT void** cookie : cookie stored by device application +* +* Description : This function 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 : int +* UPNP_E_SUCCESS if successful else returns appropriate error +* +* Note : +****************************************************************************/ +static int +get_device_info( IN http_message_t * request, + IN int isQuery, + IN IXML_Document * actionDoc, + OUT char device_udn[LINE_SIZE], + OUT char service_id[LINE_SIZE], + OUT Upnp_FunPtr * callback, + OUT void **cookie ) +{ + struct Handle_Info *device_info; + int device_hnd; + service_info *serv_info; + char save_char; + int ret_code = -1; // error by default + 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]; + control_url[request->uri.pathquery.size] = '\0'; + + HandleLock( ); + + if( GetDeviceHandleInfo( &device_hnd, &device_info ) != HND_DEVICE ) { + goto error_handler; + } + + if( ( serv_info = + FindServiceControlURLPath( &device_info->ServiceTable, + control_url ) ) == NULL ) { + 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: + control_url[request->uri.pathquery.size] = save_char; // restore + HandleUnlock( ); + return ret_code; +} + +/**************************************************************************** +* Function : send_action_response +* +* Parameters : +* IN SOCKINFO *info : socket info +* IN IXML_Document *action_resp : The response document +* IN http_message_t* request : action request document +* +* Description : This function sends the SOAP response +* +* Return : void +* +* Note : +****************************************************************************/ +static XINLINE void +send_action_response( IN SOCKINFO * info, + IN IXML_Document * action_resp, + IN http_message_t * request ) +{ + char *xml_response = NULL; + membuffer headers; + int major, + minor; + int err_code; + int content_length; + int ret_code; + int timeout_secs = SOAP_TIMEOUT; + static char *start_body = + "\n"; + static char *end_body = " "; + + // init + http_CalcResponseVersion( request->major_version, + request->minor_version, &major, &minor ); + membuffer_init( &headers ); + err_code = UPNP_E_OUTOF_MEMORY; // one error only + + // get xml + xml_response = ixmlPrintNode( ( IXML_Node * ) action_resp ); + if( xml_response == NULL ) { + goto error_handler; + } + + content_length = strlen( start_body ) + strlen( xml_response ) + + strlen( end_body ); + + // make headers +/* -- PATCH START - Sergey 'Jin' Bostandzhyan */ + if( http_MakeMessage( &headers, major, minor, "RNsDsSXcc", HTTP_OK, // status code + content_length, ContentTypeHeader, "EXT:\r\n", X_USER_AGENT // EXT header + ) != 0 ) { + goto error_handler; + } +/* -- PATCH END - */ + + // send whole msg + ret_code = http_SendMessage( info, &timeout_secs, "bbbb", + headers.buf, headers.length, + start_body, strlen( start_body ), + xml_response, strlen( xml_response ), + end_body, strlen( end_body ) ); + + DBGONLY( if( ret_code != 0 ) { + UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "Failed to send response: err code = %d\n", + ret_code );} + ) + + err_code = 0; + + error_handler: + ixmlFreeDOMString( xml_response ); + membuffer_destroy( &headers ); + if( err_code != 0 ) { + // only one type of error to worry about - out of mem + send_error_response( info, SOAP_ACTION_FAILED, "Out of memory", + request ); + } +} + +/**************************************************************************** +* Function : get_var_name +* +* Parameters : +* IN IXML_Document *TempDoc : Document containing variable request +* OUT char* VarName : Name of the state varible +* +* Description : This function finds the name of the state variable +* asked in the SOAP request. +* +* Return : int +* returns 0 if successful else returns -1. +* Note : +****************************************************************************/ +static XINLINE int +get_var_name( IN IXML_Document * TempDoc, + OUT 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; + 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 ); + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "Received query for variable name %s\n", + VarName ); + ) + + ret_val = 0; // success + + error_handler: + return ret_val; +} + +/**************************************************************************** +* Function : handle_query_variable +* +* Parameters : +* IN SOCKINFO *info : Socket info +* IN http_message_t* request : HTTP request +* IN IXML_Document *xml_doc : Document containing the variable request +* SOAP message +* +* Description : This action handles the SOAP requests to querry the +* state variables. This functionality has been deprecated in +* the UPnP V1.0 architecture +* +* Return : void +* +* Note : +****************************************************************************/ +static XINLINE void +handle_query_variable( IN SOCKINFO * info, + IN http_message_t * request, + IN IXML_Document * xml_doc ) +{ + 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; + + // 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 + if( get_device_info( request, 1, xml_doc, variable.DevUDN, + variable.ServiceID, + &soap_event_callback, &cookie ) != 0 ) { + send_error_response( info, SOAP_INVALID_VAR, + Soap_Invalid_Var, request ); + return; + } + + linecopy( variable.ErrStr, "" ); + variable.ErrCode = UPNP_E_SUCCESS; + namecopy( variable.StateVarName, var_name ); + variable.CurrentVal = NULL; + variable.CtrlPtIPAddr = info->foreign_ip_addr; + + // send event + soap_event_callback( UPNP_CONTROL_GET_VAR_REQUEST, &variable, cookie ); + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "Return from callback for var request\n" ) ); + + // validate, and handle result + if( variable.CurrentVal == NULL ) { + err_code = SOAP_ACTION_FAILED; + err_str = Soap_Action_Failed; + send_error_response( info, SOAP_INVALID_VAR, + Soap_Invalid_Var, request ); + return; + } + if( variable.ErrCode != UPNP_E_SUCCESS ) { + if( strlen( variable.ErrStr ) > 0 ) { + err_code = SOAP_INVALID_VAR; + err_str = Soap_Invalid_Var; + } else { + err_code = variable.ErrCode; + err_str = variable.ErrStr; + } + send_error_response( info, err_code, err_str, request ); + return; + } + // send response + send_var_query_response( info, variable.CurrentVal, request ); + ixmlFreeDOMString( variable.CurrentVal ); + +} + +/**************************************************************************** +* Function : handle_invoke_action +* +* Parameters : +* IN SOCKINFO *info : Socket info +* IN http_message_t* request : HTTP Request +* IN memptr action_name : Name of the SOAP Action +* IN IXML_Document *xml_doc : document containing the SOAP action +* request +* +* Description : This functions handle the SOAP action request. It checks +* the integrity of the SOAP action request and gives the call back to +* the device application. +* +* Return : void +* +* Note : +****************************************************************************/ +static void +handle_invoke_action( IN SOCKINFO * info, + IN http_message_t * request, + IN memptr action_name, + IN IXML_Document * xml_doc ) +{ + char save_char; + IXML_Document *resp_node = NULL; + struct Upnp_Action_Request action; + Upnp_FunPtr soap_event_callback; + void *cookie = NULL; + int err_code; + const char *err_str; + + action.ActionResult = NULL; + + // null-terminate + 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 ) { + goto error_handler; + } + // get device info for action event + err_code = get_device_info( request, 0, xml_doc, action.DevUDN, + action.ServiceID, &soap_event_callback, + &cookie ); + + if( err_code != UPNP_E_SUCCESS ) { + goto error_handler; + } + + namecopy( action.ActionName, action_name.buf ); + linecopy( action.ErrStr, "" ); + action.ActionRequest = resp_node; + action.ActionResult = NULL; + action.ErrCode = UPNP_E_SUCCESS; + action.CtrlPtIPAddr = info->foreign_ip_addr; + + DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, + "Calling Callback\n" ) ); + + soap_event_callback( UPNP_CONTROL_ACTION_REQUEST, &action, cookie ); + + if( action.ErrCode != UPNP_E_SUCCESS ) { + if( strlen( action.ErrStr ) <= 0 ) { + err_code = SOAP_ACTION_FAILED; + err_str = Soap_Action_Failed; + } else { + err_code = action.ErrCode; + err_str = action.ErrStr; + } + goto error_handler; + } + // validate, and handle action error + if( action.ActionResult == NULL ) { + err_code = SOAP_ACTION_FAILED; + err_str = Soap_Action_Failed; + goto error_handler; + } + // send response + send_action_response( info, action.ActionResult, request ); + + err_code = 0; + + // error handling and cleanup + error_handler: + ixmlDocument_free( action.ActionResult ); + ixmlDocument_free( resp_node ); + action_name.buf[action_name.length] = save_char; // restore + if( err_code != 0 ) { + send_error_response( info, err_code, err_str, request ); + } +} + +/**************************************************************************** +* Function : soap_device_callback +* +* Parameters : +* IN http_parser_t *parser : Parsed request received by the device +* IN http_message_t* request : HTTP request +* INOUT SOCKINFO *info : socket info +* +* Description : 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 +* +* Return : void +* +* Note : +****************************************************************************/ +void +soap_device_callback( IN http_parser_t * parser, + IN http_message_t * request, + INOUT SOCKINFO * info ) +{ + int err_code; + const char *err_str; + memptr action_name; + IXML_Document *xml_doc = NULL; + + // set default error + err_code = SOAP_INVALID_ACTION; + err_str = Soap_Invalid_Action; + + // validate: content-type == text/xml + if( !has_xml_content_type( request ) ) { + goto error_handler; + } + // type of request + if( get_request_type( request, &action_name ) != 0 ) { + 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; + } else { + err_code = SOAP_ACTION_FAILED; + } + + err_str = "XML error"; + goto error_handler; + } + + if( action_name.length == 0 ) { + // query var + handle_query_variable( info, request, xml_doc ); + } else { + // invoke action + handle_invoke_action( info, request, action_name, xml_doc ); + } + + err_code = 0; // no error + + error_handler: + ixmlDocument_free( xml_doc ); + if( err_code != 0 ) { + send_error_response( info, err_code, err_str, request ); + } +} + +#endif // EXCLUDE_SOAP + +#endif // INCLUDE_DEVICE_APIS diff --git a/libupnp/upnp/src/soap/soaplib.h b/libupnp/upnp/src/soap/soaplib.h new file mode 100644 index 0000000..baf05f0 --- /dev/null +++ b/libupnp/upnp/src/soap/soaplib.h @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef SOAPLIB_H +#define SOAPLIB_H + + +//SOAP module API to be called in Upnp-Dk API +/**************************************************************************** +* Function : soap_device_callback +* +* Parameters : +* IN http_parser_t *parser : Parsed request received by the device +* IN http_message_t* request : HTTP request +* INOUT SOCKINFO *info : socket info +* +* Description : 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 +* +* Return : void +* +* Note : +****************************************************************************/ +void soap_device_callback( + IN http_parser_t *parser, + IN http_message_t* request, + INOUT SOCKINFO *info ); + + +/**************************************************************************** +* Function : SoapSendAction +* +* Parameters : +* IN char* action_url : device contrl URL +* IN char *service_type : device service type +* IN IXML_Document *action_node : SOAP action node +* OUT IXML_Document **response_node : SOAP response node +* +* Description : This function is called by UPnP API to send the SOAP +* action request and waits till it gets the response from the device +* pass the response to the API layer +* +* Return : int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +int SoapSendAction( + IN char* action_url, + IN char *service_type, + IN IXML_Document *action_node, + OUT IXML_Document **response_node ); + +/**************************************************************************** +* Function : SoapSendActionEx +* +* Parameters : +* IN char* action_url : device contrl URL +* IN char *service_type : device service type + IN IXML_Document *Header: Soap header +* IN IXML_Document *action_node : SOAP action node ( SOAP body) +* OUT IXML_Document **response_node : SOAP response node +* +* Description : This function is called by UPnP API to send the SOAP +* action request and waits till it gets the response from the device +* pass the response to the API layer. This action is similar to the +* the SoapSendAction with only difference that it allows users to +* pass the SOAP header along the SOAP body ( soap action request) +* +* Return : int +* returns UPNP_E_SUCCESS if successful else returns appropriate error +* Note : +****************************************************************************/ +int SoapSendActionEx( + IN char * ActionURL, + IN char *ServiceType, + IN IXML_Document *Header, + IN IXML_Document *ActNode , + OUT IXML_Document **RespNode) ; + +/**************************************************************************** +* Function : SoapGetServiceVarStatus +* +* Parameters : +* IN char * action_url : Address to send this variable +* query message. +* IN char *var_name : Name of the variable. +* OUT char **var_value : Output value. +* +* Description : This function creates a status variable query message +* send it to the specified URL. It also collect the response. +* +* Return : int +* +* Note : +****************************************************************************/ +int SoapGetServiceVarStatus( + IN char * ActionURL, + IN DOMString VarName, + OUT DOMString *StVar) ; + + +// extern form the HTTP parser. +extern const char* ContentTypeHeader; + +#endif //SOAPLIB_H diff --git a/libupnp/upnp/src/ssdp/ssdp_ctrlpt.c b/libupnp/upnp/src/ssdp/ssdp_ctrlpt.c new file mode 100644 index 0000000..24a50ec --- /dev/null +++ b/libupnp/upnp/src/ssdp/ssdp_ctrlpt.c @@ -0,0 +1,621 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#include "util.h" +#ifdef INCLUDE_CLIENT_APIS +#if EXCLUDE_SSDP == 0 + +#include "ssdplib.h" +#include "upnpapi.h" +#include +#include "ThreadPool.h" + +#include "httpparser.h" +#include "httpreadwrite.h" +#include "statcodes.h" + +#include "unixutil.h" + +/************************************************************************ +* Function : send_search_result +* +* Parameters: +* IN void *data: Search reply from the device +* +* Description: +* This function sends a callback to the control point application with +* a SEARCH result +* +* Returns: void +* +***************************************************************************/ +void +send_search_result( IN void *data ) +{ + ResultData *temp = ( ResultData * ) data; + + temp->ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, + &temp->param, temp->cookie ); + free( temp ); +} + +/************************************************************************ +* Function : ssdp_handle_ctrlpt_msg +* +* Parameters: +* IN http_message_t* hmsg: SSDP message from the device +* IN struct sockaddr_in* dest_addr: Address of the device +* IN xboolean timeout: timeout kept by the control point while +* sending search message +* IN void* cookie: Cookie stored by the control point application. +* This cookie will be returned to the control point +* in the callback +* +* Description: +* This function handles the ssdp messages from the devices. These +* messages includes the search replies, advertisement of device coming +* alive and bye byes. +* +* Returns: void +* +***************************************************************************/ +void +ssdp_handle_ctrlpt_msg( IN http_message_t * hmsg, + IN struct sockaddr_in *dest_addr, + IN xboolean timeout, // only in search reply + + IN void *cookie ) // only in search reply +{ + int handle; + struct Handle_Info *ctrlpt_info = NULL; + memptr hdr_value; + xboolean is_byebye; // byebye or alive + struct Upnp_Discovery param; + SsdpEvent event; + xboolean nt_found, + usn_found, + st_found; + char save_char; + Upnp_EventType event_type; + Upnp_FunPtr ctrlpt_callback; + void *ctrlpt_cookie; + ListNode *node = NULL; + SsdpSearchArg *searchArg = NULL; + int matched = 0; + ResultData *threadData; + ThreadPoolJob job; + + // we are assuming that there can be only one client supported at a time + + HandleLock( ); + + if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { + HandleUnlock( ); + return; + } + // copy + ctrlpt_callback = ctrlpt_info->Callback; + ctrlpt_cookie = ctrlpt_info->Cookie; + HandleUnlock( ); + + // search timeout + if( timeout ) { + ctrlpt_callback( UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie ); + return; + } + + param.ErrCode = UPNP_E_SUCCESS; + + // MAX-AGE + param.Expires = -1; // assume error + if( httpmsg_find_hdr( hmsg, HDR_CACHE_CONTROL, &hdr_value ) != NULL ) { + matchstr( hdr_value.buf, hdr_value.length, + "%imax-age = %d%0", ¶m.Expires ); + } + + // DATE + param.Date[0] = '\0'; + if( httpmsg_find_hdr( hmsg, HDR_DATE, &hdr_value ) != NULL ) { + linecopylen( param.Date, hdr_value.buf, hdr_value.length ); + } + + // dest addr + param.DestAddr = dest_addr; + + // EXT + param.Ext[0] = '\0'; + if( httpmsg_find_hdr( hmsg, HDR_EXT, &hdr_value ) != NULL ) { + linecopylen( param.Ext, hdr_value.buf, hdr_value.length ); + } + // LOCATION + param.Location[0] = '\0'; + if( httpmsg_find_hdr( hmsg, HDR_LOCATION, &hdr_value ) != NULL ) { + linecopylen( param.Location, hdr_value.buf, hdr_value.length ); + } + // SERVER / USER-AGENT + param.Os[0] = '\0'; + if( httpmsg_find_hdr( hmsg, HDR_SERVER, &hdr_value ) != NULL || + httpmsg_find_hdr( hmsg, HDR_USER_AGENT, &hdr_value ) != NULL ) { + linecopylen( param.Os, hdr_value.buf, hdr_value.length ); + } + // clear everything + param.DeviceId[0] = '\0'; + param.DeviceType[0] = '\0'; + param.ServiceType[0] = '\0'; + + param.ServiceVer[0] = '\0'; // not used; version is in ServiceType + + event.UDN[0] = '\0'; + event.DeviceType[0] = '\0'; + event.ServiceType[0] = '\0'; + + nt_found = FALSE; + + if( httpmsg_find_hdr( hmsg, HDR_NT, &hdr_value ) != NULL ) { + save_char = hdr_value.buf[hdr_value.length]; + hdr_value.buf[hdr_value.length] = '\0'; + + nt_found = ( ssdp_request_type( hdr_value.buf, &event ) == 0 ); + + hdr_value.buf[hdr_value.length] = save_char; + } + + usn_found = FALSE; + if( httpmsg_find_hdr( hmsg, HDR_USN, &hdr_value ) != NULL ) { + save_char = hdr_value.buf[hdr_value.length]; + hdr_value.buf[hdr_value.length] = '\0'; + + usn_found = ( unique_service_name( hdr_value.buf, &event ) == 0 ); + + hdr_value.buf[hdr_value.length] = save_char; + } + + if( nt_found || usn_found ) { + strcpy( param.DeviceId, event.UDN ); + strcpy( param.DeviceType, event.DeviceType ); + strcpy( param.ServiceType, event.ServiceType ); + } + + // ADVERT. OR BYEBYE + if( hmsg->is_request ) { + // use NTS hdr to determine advert., or byebye + // + if( httpmsg_find_hdr( hmsg, HDR_NTS, &hdr_value ) == NULL ) { + return; // error; NTS header not found + } + if( memptr_cmp( &hdr_value, "ssdp:alive" ) == 0 ) { + is_byebye = FALSE; + } else if( memptr_cmp( &hdr_value, "ssdp:byebye" ) == 0 ) { + is_byebye = TRUE; + } else { + return; // bad value + } + + if( is_byebye ) { + // check device byebye + if( !nt_found || !usn_found ) { + return; // bad byebye + } + event_type = UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE; + } else { + // check advertisement + // .Expires is valid if positive. This is for testing + // only. Expires should be greater than 1800 (30 mins) + if( !nt_found || + !usn_found || + strlen( param.Location ) == 0 || param.Expires <= 0 ) { + return; // bad advertisement + } + + event_type = UPNP_DISCOVERY_ADVERTISEMENT_ALIVE; + } + + // call callback + ctrlpt_callback( event_type, ¶m, ctrlpt_cookie ); + + } else // reply (to a SEARCH) + { + // only checking to see if there is a valid ST header + st_found = FALSE; + if( httpmsg_find_hdr( hmsg, HDR_ST, &hdr_value ) != NULL ) { + save_char = hdr_value.buf[hdr_value.length]; + hdr_value.buf[hdr_value.length] = '\0'; + st_found = ssdp_request_type( hdr_value.buf, &event ) == 0; + hdr_value.buf[hdr_value.length] = save_char; + } + if( hmsg->status_code != HTTP_OK || + param.Expires <= 0 || + strlen( param.Location ) == 0 || !usn_found || !st_found ) { + return; // bad reply + } + //check each current search + HandleLock( ); + if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { + HandleUnlock( ); + return; + } + node = ListHead( &ctrlpt_info->SsdpSearchList ); + + //temporary add null termination + //save_char = hdr_value.buf[ hdr_value.length ]; + //hdr_value.buf[ hdr_value.length ] = '\0'; + + while( node != NULL ) { + searchArg = node->item; + matched = 0; + //check for match of ST header and search target + switch ( searchArg->requestType ) { + case SSDP_ALL: + { + matched = 1; + break; + } + case SSDP_ROOTDEVICE: + { + matched = ( event.RequestType == SSDP_ROOTDEVICE ); + break; + } + case SSDP_DEVICEUDN: + { + matched = !( strncmp( searchArg->searchTarget, + hdr_value.buf, + hdr_value.length ) ); + break; + } + case SSDP_DEVICETYPE: + { + int m = min( hdr_value.length, + strlen( searchArg->searchTarget ) ); + + matched = !( strncmp( searchArg->searchTarget, + hdr_value.buf, m ) ); + break; + } + case SSDP_SERVICE: + { + int m = min( hdr_value.length, + strlen( searchArg->searchTarget ) ); + + matched = !( strncmp( searchArg->searchTarget, + hdr_value.buf, m ) ); + break; + } + default: + { + matched = 0; + break; + } + } + + if( matched ) { + //schedule call back + threadData = + ( ResultData * ) malloc( sizeof( ResultData ) ); + if( threadData != NULL ) { + threadData->param = param; + threadData->cookie = searchArg->cookie; + threadData->ctrlpt_callback = ctrlpt_callback; + TPJobInit( &job, ( start_routine ) send_search_result, + threadData ); + TPJobSetPriority( &job, MED_PRIORITY ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + ThreadPoolAdd( &gRecvThreadPool, &job, NULL ); + } + } + node = ListNext( &ctrlpt_info->SsdpSearchList, node ); + } + + HandleUnlock( ); + //ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, ¶m, cookie ); + } +} + +/************************************************************************ +* Function : process_reply +* +* Parameters: +* IN char* request_buf: the response came from the device +* IN int buf_len: The length of the response buffer +* IN struct sockaddr_in* dest_addr: The address of the device +* IN void *cookie : cookie passed by the control point application +* at the time of sending search message +* +* Description: +* This function processes reply recevied from a search +* +* Returns: void +* +***************************************************************************/ +static XINLINE void +process_reply( IN char *request_buf, + IN int buf_len, + IN struct sockaddr_in *dest_addr, + IN void *cookie ) +{ + http_parser_t parser; + + parser_response_init( &parser, HTTPMETHOD_MSEARCH ); + + // parse + if( parser_append( &parser, request_buf, buf_len ) != PARSE_SUCCESS ) { + httpmsg_destroy( &parser.msg ); + return; + } + // handle reply + ssdp_handle_ctrlpt_msg( &parser.msg, dest_addr, FALSE, cookie ); + + // done + httpmsg_destroy( &parser.msg ); +} + +/************************************************************************ +* Function : CreateClientRequestPacket +* +* Parameters: +* IN char * RqstBuf:Output string in HTTP format. +* IN char *SearchTarget:Search Target +* IN int Mx dest_addr: Number of seconds to wait to +* collect all the responses +* +* Description: +* This function creates a HTTP search request packet +* depending on the input parameter. +* +* Returns: void +* +***************************************************************************/ +static void +CreateClientRequestPacket( IN char *RqstBuf, + IN int Mx, + IN char *SearchTarget ) +{ + char TempBuf[COMMAND_LEN]; + + strcpy( RqstBuf, "M-SEARCH * HTTP/1.1\r\n" ); + + sprintf( TempBuf, "HOST: %s:%d\r\n", SSDP_IP, SSDP_PORT ); + strcat( RqstBuf, TempBuf ); + strcat( RqstBuf, "MAN: \"ssdp:discover\"\r\n" ); + + if( Mx > 0 ) { + sprintf( TempBuf, "MX: %d\r\n", Mx ); + strcat( RqstBuf, TempBuf ); + } + + if( SearchTarget != NULL ) { + sprintf( TempBuf, "ST: %s\r\n", SearchTarget ); + strcat( RqstBuf, TempBuf ); + } + strcat( RqstBuf, "\r\n" ); + +} + +/************************************************************************ +* Function : searchExpired +* +* Parameters: +* IN void * arg: +* +* Description: +* This function +* +* Returns: void +* +***************************************************************************/ +void +searchExpired( void *arg ) +{ + + int *id = ( int * )arg; + int handle = -1; + struct Handle_Info *ctrlpt_info = NULL; + + //remove search Target from list and call client back + ListNode *node = NULL; + SsdpSearchArg *item; + Upnp_FunPtr ctrlpt_callback; + void *cookie = NULL; + int found = 0; + + HandleLock( ); + + //remove search target from search list + + if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { + free( id ); + HandleUnlock( ); + return; + } + + ctrlpt_callback = ctrlpt_info->Callback; + + node = ListHead( &ctrlpt_info->SsdpSearchList ); + + while( node != NULL ) { + item = ( SsdpSearchArg * ) node->item; + if( item->timeoutEventId == ( *id ) ) { + free( item->searchTarget ); + cookie = item->cookie; + found = 1; + item->searchTarget = NULL; + free( item ); + ListDelNode( &ctrlpt_info->SsdpSearchList, node, 0 ); + break; + } + node = ListNext( &ctrlpt_info->SsdpSearchList, node ); + } + HandleUnlock( ); + + if( found ) { + ctrlpt_callback( UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie ); + } + + free( id ); +} + +/************************************************************************ +* Function : SearchByTarget +* +* Parameters: +* IN int Mx:Number of seconds to wait, to collect all the responses. +* IN char *St: Search target. +* IN void *Cookie: cookie provided by control point application. +* This cokie will be returned to application in the callback. +* +* Description: +* This function creates and send the search request for a specific URL. +* +* Returns: int +* 1 if successful else appropriate error +***************************************************************************/ +int +SearchByTarget( IN int Mx, + IN char *St, + IN void *Cookie ) +{ + int socklen = sizeof( struct sockaddr_in ); + int *id = NULL; + char *ReqBuf; + struct sockaddr_in destAddr; + fd_set wrSet; + SsdpSearchArg *newArg = NULL; + int timeTillRead = 0; + int handle; + struct Handle_Info *ctrlpt_info = NULL; + enum SsdpSearchType requestType; + unsigned long addr = inet_addr( LOCAL_HOST ); + + //ThreadData *ThData; + ThreadPoolJob job; + + requestType = ssdp_request_type1( St ); + if( requestType == SSDP_SERROR ) { + return UPNP_E_INVALID_PARAM; + } + + ReqBuf = ( char * )malloc( BUFSIZE ); + if( ReqBuf == NULL ) + return UPNP_E_OUTOF_MEMORY; + + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + ">>> SSDP SEND >>>\n%s\n", ReqBuf ); + ) + + timeTillRead = Mx; + + if( timeTillRead < MIN_SEARCH_TIME ) { + timeTillRead = MIN_SEARCH_TIME; + } else if( timeTillRead > MAX_SEARCH_TIME ) { + timeTillRead = MAX_SEARCH_TIME; + } + + CreateClientRequestPacket( ReqBuf, timeTillRead, St ); + memset( ( char * )&destAddr, 0, sizeof( struct sockaddr_in ) ); + + destAddr.sin_family = AF_INET; + destAddr.sin_addr.s_addr = inet_addr( SSDP_IP ); + destAddr.sin_port = htons( SSDP_PORT ); + + FD_ZERO( &wrSet ); + FD_SET( gSsdpReqSocket, &wrSet ); + + //add search criteria to list + HandleLock( ); + if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { + HandleUnlock( ); + free( ReqBuf ); + return UPNP_E_INTERNAL_ERROR; + } + + newArg = ( SsdpSearchArg * ) malloc( sizeof( SsdpSearchArg ) ); + newArg->searchTarget = strdup( St ); + newArg->cookie = Cookie; + newArg->requestType = requestType; + + id = ( int * )malloc( sizeof( int ) ); + TPJobInit( &job, ( start_routine ) searchExpired, id ); + TPJobSetPriority( &job, MED_PRIORITY ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + + //Schdule a timeout event to remove search Arg + TimerThreadSchedule( &gTimerThread, timeTillRead, + REL_SEC, &job, SHORT_TERM, id ); + newArg->timeoutEventId = ( *id ); + + ListAddTail( &ctrlpt_info->SsdpSearchList, newArg ); + HandleUnlock( ); + + setsockopt( gSsdpReqSocket, IPPROTO_IP, IP_MULTICAST_IF, + ( char * )&addr, sizeof( addr ) ); + + if( select( gSsdpReqSocket + 1, NULL, &wrSet, NULL, NULL ) + == UPNP_SOCKETERROR ) { + DBGONLY( if( errno == EBADF ) { + UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP_LIB :RequestHandler:An invalid file descriptor" + " was givenin one of the sets. \n" );} + else + if( errno == EINTR ) { + UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP_LIB :RequestHandler: A non blocked " + "signal was caught. \n" );} + else + if( errno == EINVAL ) { + UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP_LIB :RequestHandler: n is negative. \n" );} + else + if( errno == ENOMEM ) { + UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP_LIB : RequestHandler:select was unable to " + "allocate memory for internal tables.\n" );} + ) +shutdown( gSsdpReqSocket, SD_BOTH ); + UpnpCloseSocket( gSsdpReqSocket ); + free( ReqBuf ); + return UPNP_E_INTERNAL_ERROR; + } else if( FD_ISSET( gSsdpReqSocket, &wrSet ) ) { + int NumCopy = 0; + + while( NumCopy < NUM_SSDP_COPY ) { + sendto( gSsdpReqSocket, ReqBuf, strlen( ReqBuf ), 0, + ( struct sockaddr * )&destAddr, socklen ); + NumCopy++; + imillisleep( SSDP_PAUSE ); + } + } + + free( ReqBuf ); + return 1; +} + +#endif // EXCLUDE_SSDP +#endif // INCLUDE_CLIENT_APIS diff --git a/libupnp/upnp/src/ssdp/ssdp_device.c b/libupnp/upnp/src/ssdp/ssdp_device.c new file mode 100644 index 0000000..27c239c --- /dev/null +++ b/libupnp/upnp/src/ssdp/ssdp_device.c @@ -0,0 +1,806 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#ifdef INCLUDE_DEVICE_APIS +#if EXCLUDE_SSDP == 0 +#include +#include +#include "ssdplib.h" +#include "upnpapi.h" +#include "ThreadPool.h" +#include "httpparser.h" +#include "httpreadwrite.h" +#include "statcodes.h" +#include "unixutil.h" + +#define MSGTYPE_SHUTDOWN 0 +#define MSGTYPE_ADVERTISEMENT 1 +#define MSGTYPE_REPLY 2 + +/************************************************************************ +* Function : advertiseAndReplyThread +* +* Parameters: +* IN void *data: Structure containing the search request +* +* Description: +* This function is a wrapper function to reply the search request +* coming from the control point. +* +* Returns: void * +* always return NULL +***************************************************************************/ +void * +advertiseAndReplyThread( IN void *data ) +{ + SsdpSearchReply *arg = ( SsdpSearchReply * ) data; + + AdvertiseAndReply( 0, arg->handle, + arg->event.RequestType, + &arg->dest_addr, + arg->event.DeviceType, + arg->event.UDN, + arg->event.ServiceType, arg->MaxAge ); + free( arg ); + + return NULL; +} + +/************************************************************************ +* Function : ssdp_handle_device_request +* +* Parameters: +* IN http_message_t* hmsg: SSDP search request from the control point +* IN struct sockaddr_in* dest_addr: The address info of control point +* +* Description: +* This function handles the search request. It do the sanity checks of +* the request and then schedules a thread to send a random time reply ( +* random within maximum time given by the control point to reply). +* +* Returns: void * +* 1 if successful else appropriate error +***************************************************************************/ +void +ssdp_handle_device_request( IN http_message_t * hmsg, + IN struct sockaddr_in *dest_addr ) +{ +#define MX_FUDGE_FACTOR 10 + + int handle; + struct Handle_Info *dev_info = NULL; + memptr hdr_value; + int mx; + char save_char; + SsdpEvent event; + int ret_code; + SsdpSearchReply *threadArg = NULL; + ThreadPoolJob job; + int replyTime; + int maxAge; + + // check man hdr + if( httpmsg_find_hdr( hmsg, HDR_MAN, &hdr_value ) == NULL || + memptr_cmp( &hdr_value, "\"ssdp:discover\"" ) != 0 ) { + return; // bad or missing hdr + } + // MX header + if( httpmsg_find_hdr( hmsg, HDR_MX, &hdr_value ) == NULL || + ( mx = raw_to_int( &hdr_value, 10 ) ) < 0 ) { + return; + } + // ST header + if( httpmsg_find_hdr( hmsg, HDR_ST, &hdr_value ) == NULL ) { + return; + } + save_char = hdr_value.buf[hdr_value.length]; + hdr_value.buf[hdr_value.length] = '\0'; + ret_code = ssdp_request_type( hdr_value.buf, &event ); + hdr_value.buf[hdr_value.length] = save_char; // restore + if( ret_code == -1 ) { + return; // bad ST header + } + + HandleLock( ); + // device info + if( GetDeviceHandleInfo( &handle, &dev_info ) != HND_DEVICE ) { + HandleUnlock( ); + return; // no info found + } + maxAge = dev_info->MaxAge; + HandleUnlock( ); + + DBGONLY( UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, + "ssdp_handle_device_request with Cmd %d SEARCH\n", + event.Cmd ); + UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, + "MAX-AGE = %d\n", maxAge ); + UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, + "MX = %d\n", event.Mx ); + UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, + "DeviceType = %s\n", event.DeviceType ); + UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, + "DeviceUuid = %s\n", event.UDN ); + UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, + "ServiceType = %s\n", event.ServiceType ); ) + + threadArg = + ( SsdpSearchReply * ) malloc( sizeof( SsdpSearchReply ) ); + + if( threadArg == NULL ) { + return; + } + threadArg->handle = handle; + threadArg->dest_addr = ( *dest_addr ); + threadArg->event = event; + threadArg->MaxAge = maxAge; + + TPJobInit( &job, advertiseAndReplyThread, threadArg ); + TPJobSetFreeFunction( &job, ( free_routine ) free ); + + //Subtract a percentage from the mx + //to allow for network and processing delays + // (i.e. if search is for 30 seconds, + // respond withing 0 - 27 seconds) + + if( mx >= 2 ) { + mx -= MAXVAL( 1, mx / MX_FUDGE_FACTOR ); + } + + if( mx < 1 ) { + mx = 1; + } + + replyTime = rand( ) % mx; + + TimerThreadSchedule( &gTimerThread, replyTime, REL_SEC, &job, + SHORT_TERM, NULL ); +} + +/************************************************************************ +* Function : NewRequestHandler +* +* Parameters: +* IN struct sockaddr_in * DestAddr: Ip address, to send the reply. +* IN int NumPacket: Number of packet to be sent. +* IN char **RqPacket:Number of packet to be sent. +* +* Description: +* This function works as a request handler which passes the HTTP +* request string to multicast channel then +* +* Returns: void * +* 1 if successful else appropriate error +***************************************************************************/ +static int +NewRequestHandler( IN struct sockaddr_in *DestAddr, + IN int NumPacket, + IN char **RqPacket ) +{ + int ReplySock, + socklen = sizeof( struct sockaddr_in ); + int NumCopy, + Index; + unsigned long replyAddr = inet_addr( LOCAL_HOST ); + int ttl = 4; //a/c to UPNP Spec + + ReplySock = socket( AF_INET, SOCK_DGRAM, 0 ); + if( ReplySock == UPNP_INVALID_SOCKET ) { + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP_LIB: New Request Handler:" + "Error in socket operation !!!\n" ) ); + + return UPNP_E_OUTOF_SOCKET; + } + + setsockopt( ReplySock, IPPROTO_IP, IP_MULTICAST_IF, + ( char * )&replyAddr, sizeof( replyAddr ) ); + setsockopt( ReplySock, IPPROTO_IP, IP_MULTICAST_TTL, + ( char * )&ttl, sizeof( int ) ); + + for( Index = 0; Index < NumPacket; Index++ ) { + int rc; + + NumCopy = 0; + while( NumCopy < NUM_COPY ) { + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + ">>> SSDP SEND >>>\n%s\n", + *( RqPacket + Index ) ); + ) + rc = sendto( ReplySock, *( RqPacket + Index ), + strlen( *( RqPacket + Index ) ), + 0, ( struct sockaddr * )DestAddr, socklen ); + imillisleep( SSDP_PAUSE ); + ++NumCopy; + } + } + + shutdown( ReplySock, SD_BOTH ); + UpnpCloseSocket( ReplySock ); + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : CreateServiceRequestPacket +* +* Parameters: +* IN int msg_type : type of the message ( Search Reply, Advertisement +* or Shutdown ) +* IN char * nt : ssdp type +* IN char * usn : unique service name ( go in the HTTP Header) +* IN char * location :Location URL. +* IN int duration :Service duration in sec. +* OUT char** packet :Output buffer filled with HTTP statement. +* +* Description: +* This function creates a HTTP request packet. Depending +* on the input parameter it either creates a service advertisement +* request or service shutdown request etc. +* +* Returns: void +* +***************************************************************************/ +void +CreateServicePacket( IN int msg_type, + IN char *nt, + IN char *usn, + IN char *location, + IN int duration, + OUT char **packet ) +{ + int ret_code; + char *nts; + membuffer buf; + + //Notf=0 means service shutdown, + //Notf=1 means service advertisement, Notf =2 means reply + + membuffer_init( &buf ); + buf.size_inc = 30; + + *packet = NULL; + + if( msg_type == MSGTYPE_REPLY ) { +/* -- PATCH START - Sergey 'Jin' Bostandzhyan */ + ret_code = http_MakeMessage( &buf, 1, 1, + "R" "sdc" "D" "s" "ssc" "S" "Xc" "ssc" + "ssc" "c", HTTP_OK, + "CACHE-CONTROL: max-age=", duration, + "EXT:\r\n", "LOCATION: ", location, + X_USER_AGENT, + "ST: ", nt, "USN: ", usn ); +/* -- PATCH END - */ + + if( ret_code != 0 ) { + return; + } + } else if( msg_type == MSGTYPE_ADVERTISEMENT || + msg_type == MSGTYPE_SHUTDOWN ) { + if( msg_type == MSGTYPE_ADVERTISEMENT ) { + nts = "ssdp:alive"; + } else // shutdown + { + nts = "ssdp:byebye"; + } + + // NOTE: The CACHE-CONTROL and LOCATION headers are not present in + // a shutdown msg, but are present here for MS WinMe interop. + +/* -- PATCH START - Sergey 'Jin' Bostandzhyan */ + ret_code = http_MakeMessage( &buf, 1, 1, + "Q" "sssdc" "sdc" "ssc" "ssc" "ssc" + "S" "Xc" "ssc" "c", HTTPMETHOD_NOTIFY, "*", + 1, "HOST: ", SSDP_IP, ":", SSDP_PORT, + "CACHE-CONTROL: max-age=", duration, + "LOCATION: ", location, "NT: ", nt, + "NTS: ", nts, X_USER_AGENT, "USN: ", usn ); +/* -- PATCH END - */ + if( ret_code != 0 ) { + return; + } + + } else { + assert( 0 ); // unknown msg + } + + *packet = membuffer_detach( &buf ); // return msg + + membuffer_destroy( &buf ); + + return; +} + +/************************************************************************ +* Function : DeviceAdvertisement +* +* Parameters: +* IN char * DevType : type of the device +* IN int RootDev: flag to indicate if the device is root device +* IN char * nt : ssdp type +* IN char * usn : unique service name +* IN char * location :Location URL. +* IN int duration :Service duration in sec. +* +* Description: +* This function creates the device advertisement request based on +* the input parameter, and send it to the multicast channel. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +DeviceAdvertisement( IN char *DevType, + int RootDev, + char *Udn, + IN char *Location, + IN int Duration ) +{ + struct sockaddr_in DestAddr; + + //char Mil_Nt[LINE_SIZE] + char Mil_Usn[LINE_SIZE]; + char *msgs[3]; + int ret_code; + + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "In function SendDeviceAdvertisemenrt\n" ); + ) + + DestAddr.sin_family = AF_INET; + DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP ); + DestAddr.sin_port = htons( SSDP_PORT ); + + msgs[0] = NULL; + msgs[1] = NULL; + msgs[2] = NULL; + + //If deviceis a root device , here we need to + //send 3 advertisement or reply + if( RootDev ) { + sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn ); + CreateServicePacket( MSGTYPE_ADVERTISEMENT, "upnp:rootdevice", + Mil_Usn, Location, Duration, &msgs[0] ); + } + // both root and sub-devices need to send these two messages + // + + CreateServicePacket( MSGTYPE_ADVERTISEMENT, Udn, Udn, + Location, Duration, &msgs[1] ); + + sprintf( Mil_Usn, "%s::%s", Udn, DevType ); + CreateServicePacket( MSGTYPE_ADVERTISEMENT, DevType, Mil_Usn, + Location, Duration, &msgs[2] ); + + // check error + if( ( RootDev && msgs[0] == NULL ) || + msgs[1] == NULL || msgs[2] == NULL ) { + free( msgs[0] ); + free( msgs[1] ); + free( msgs[2] ); + return UPNP_E_OUTOF_MEMORY; + } + // send packets + if( RootDev ) { + // send 3 msg types + ret_code = NewRequestHandler( &DestAddr, 3, &msgs[0] ); + } else // sub-device + { + // send 2 msg types + ret_code = NewRequestHandler( &DestAddr, 2, &msgs[1] ); + } + + // free msgs + free( msgs[0] ); + free( msgs[1] ); + free( msgs[2] ); + + return ret_code; +} + +/************************************************************************ +* Function : SendReply +* +* Parameters: +* IN struct sockaddr_in * DestAddr:destination IP address. +* IN char *DevType: Device type +* IN int RootDev: 1 means root device 0 means embedded device. +* IN char * Udn: Device UDN +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. +* IN int ByType: +* +* Description: +* This function creates the reply packet based on the input parameter, +* and send it to the client addesss given in its input parameter DestAddr. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +SendReply( IN struct sockaddr_in *DestAddr, + IN char *DevType, + IN int RootDev, + IN char *Udn, + IN char *Location, + IN int Duration, + IN int ByType ) +{ + int ret_code; + char *msgs[2]; + int num_msgs; + char Mil_Usn[LINE_SIZE]; + int i; + + msgs[0] = NULL; + msgs[1] = NULL; + + if( RootDev ) { + // one msg for root device + num_msgs = 1; + + sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn ); + CreateServicePacket( MSGTYPE_REPLY, "upnp:rootdevice", + Mil_Usn, Location, Duration, &msgs[0] ); + } else { + // two msgs for embedded devices + num_msgs = 1; + + //NK: FIX for extra response when someone searches by udn + if( !ByType ) { + CreateServicePacket( MSGTYPE_REPLY, Udn, Udn, Location, + Duration, &msgs[0] ); + } else { + sprintf( Mil_Usn, "%s::%s", Udn, DevType ); + CreateServicePacket( MSGTYPE_REPLY, DevType, Mil_Usn, + Location, Duration, &msgs[0] ); + } + } + + // check error + for( i = 0; i < num_msgs; i++ ) { + if( msgs[i] == NULL ) { + free( msgs[0] ); + return UPNP_E_OUTOF_MEMORY; + } + } + + // send msgs + ret_code = NewRequestHandler( DestAddr, num_msgs, msgs ); + for( i = 0; i < num_msgs; i++ ) { + if( msgs[i] != NULL ) + free( msgs[i] ); + } + + return ret_code; +} + +/************************************************************************ +* Function : DeviceReply +* +* Parameters: +* IN struct sockaddr_in * DestAddr:destination IP address. +* IN char *DevType: Device type +* IN int RootDev: 1 means root device 0 means embedded device. +* IN char * Udn: Device UDN +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. + +* Description: +* This function creates the reply packet based on the input parameter, +* and send it to the client address given in its input parameter DestAddr. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +DeviceReply( IN struct sockaddr_in *DestAddr, + IN char *DevType, + IN int RootDev, + IN char *Udn, + IN char *Location, + IN int Duration ) +{ + char *szReq[3], + Mil_Nt[LINE_SIZE], + Mil_Usn[LINE_SIZE]; + int RetVal; + + szReq[0] = NULL; + szReq[1] = NULL; + szReq[2] = NULL; + + // create 2 or 3 msgs + + if( RootDev ) { + // 3 replies for root device + strcpy( Mil_Nt, "upnp:rootdevice" ); + sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn ); + CreateServicePacket( MSGTYPE_REPLY, Mil_Nt, Mil_Usn, + Location, Duration, &szReq[0] ); + } + + sprintf( Mil_Nt, "%s", Udn ); + sprintf( Mil_Usn, "%s", Udn ); + CreateServicePacket( MSGTYPE_REPLY, Mil_Nt, Mil_Usn, + Location, Duration, &szReq[1] ); + + sprintf( Mil_Nt, "%s", DevType ); + sprintf( Mil_Usn, "%s::%s", Udn, DevType ); + CreateServicePacket( MSGTYPE_REPLY, Mil_Nt, Mil_Usn, + Location, Duration, &szReq[2] ); + + // check error + + if( ( RootDev && szReq[0] == NULL ) || + szReq[1] == NULL || szReq[2] == NULL ) { + free( szReq[0] ); + free( szReq[1] ); + free( szReq[2] ); + return UPNP_E_OUTOF_MEMORY; + } + // send replies + if( RootDev ) { + RetVal = NewRequestHandler( DestAddr, 3, szReq ); + } else { + RetVal = NewRequestHandler( DestAddr, 2, &szReq[1] ); + } + + // free + free( szReq[0] ); + free( szReq[1] ); + free( szReq[2] ); + + return RetVal; +} + +/************************************************************************ +* Function : ServiceAdvertisement +* +* Parameters: +* IN char * Udn: Device UDN +* IN char *ServType: Service Type. +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. + +* Description: +* This function creates the advertisement packet based +* on the input parameter, and send it to the multicast channel. + +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +ServiceAdvertisement( IN char *Udn, + IN char *ServType, + IN char *Location, + IN int Duration ) +{ + char Mil_Usn[LINE_SIZE]; + char *szReq[1]; + struct sockaddr_in DestAddr; + int RetVal; + + DestAddr.sin_family = AF_INET; + DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP ); + DestAddr.sin_port = htons( SSDP_PORT ); + + sprintf( Mil_Usn, "%s::%s", Udn, ServType ); + + //CreateServiceRequestPacket(1,szReq[0],Mil_Nt,Mil_Usn, + //Server,Location,Duration); + CreateServicePacket( MSGTYPE_ADVERTISEMENT, ServType, Mil_Usn, + Location, Duration, &szReq[0] ); + if( szReq[0] == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + RetVal = NewRequestHandler( &DestAddr, 1, szReq ); + + free( szReq[0] ); + return RetVal; +} + +/************************************************************************ +* Function : ServiceReply +* +* Parameters: +* IN struct sockaddr_in *DestAddr: +* IN char * Udn: Device UDN +* IN char *ServType: Service Type. +* IN char * Location: Location of Device description document. +* IN int Duration :Life time of this device. + +* Description: +* This function creates the advertisement packet based +* on the input parameter, and send it to the multicast channel. + +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +ServiceReply( IN struct sockaddr_in *DestAddr, + IN char *ServType, + IN char *Udn, + IN char *Location, + IN int Duration ) +{ + char Mil_Usn[LINE_SIZE]; + char *szReq[1]; + int RetVal; + + szReq[0] = NULL; + + sprintf( Mil_Usn, "%s::%s", Udn, ServType ); + + CreateServicePacket( MSGTYPE_REPLY, ServType, Mil_Usn, + Location, Duration, &szReq[0] ); + if( szReq[0] == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + RetVal = NewRequestHandler( DestAddr, 1, szReq ); + + free( szReq[0] ); + return RetVal; +} + +/************************************************************************ +* Function : ServiceShutdown +* +* Parameters: +* IN char * Udn: Device UDN +* IN char *ServType: Service Type. +* IN char * Location: Location of Device description document. +* IN int Duration :Service duration in sec. + +* Description: +* This function creates a HTTP service shutdown request packet +* and sent it to the multicast channel through RequestHandler. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +ServiceShutdown( IN char *Udn, + IN char *ServType, + IN char *Location, + IN int Duration ) +{ + char Mil_Usn[LINE_SIZE]; + char *szReq[1]; + struct sockaddr_in DestAddr; + int RetVal; + + DestAddr.sin_family = AF_INET; + DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP ); + DestAddr.sin_port = htons( SSDP_PORT ); + + //sprintf(Mil_Nt,"%s",ServType); + sprintf( Mil_Usn, "%s::%s", Udn, ServType ); + //CreateServiceRequestPacket(0,szReq[0],Mil_Nt,Mil_Usn, + //Server,Location,Duration); + CreateServicePacket( MSGTYPE_SHUTDOWN, ServType, Mil_Usn, + Location, Duration, &szReq[0] ); + if( szReq[0] == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + RetVal = NewRequestHandler( &DestAddr, 1, szReq ); + + free( szReq[0] ); + return RetVal; +} + +/************************************************************************ +* Function : DeviceShutdown +* +* Parameters: +* IN char *DevType: Device Type. +* IN int RootDev:1 means root device. +* IN char * Udn: Device UDN +* IN char * Location: Location URL +* IN int Duration :Device duration in sec. +* +* Description: +* This function creates a HTTP device shutdown request packet +* and sent it to the multicast channel through RequestHandler. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ +int +DeviceShutdown( IN char *DevType, + IN int RootDev, + IN char *Udn, + IN char *_Server, + IN char *Location, + IN int Duration ) +{ + struct sockaddr_in DestAddr; + char *msgs[3]; + char Mil_Usn[LINE_SIZE]; + int ret_code; + + msgs[0] = NULL; + msgs[1] = NULL; + msgs[2] = NULL; + + DestAddr.sin_family = AF_INET; + DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP ); + DestAddr.sin_port = htons( SSDP_PORT ); + + // root device has one extra msg + if( RootDev ) { + sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn ); + CreateServicePacket( MSGTYPE_SHUTDOWN, "upnp:rootdevice", + Mil_Usn, Location, Duration, &msgs[0] ); + } + + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "In function DeviceShutdown\n" ); ) + // both root and sub-devices need to send these two messages + CreateServicePacket( MSGTYPE_SHUTDOWN, Udn, Udn, + Location, Duration, &msgs[1] ); + + sprintf( Mil_Usn, "%s::%s", Udn, DevType ); + CreateServicePacket( MSGTYPE_SHUTDOWN, DevType, Mil_Usn, + Location, Duration, &msgs[2] ); + + // check error + if( ( RootDev && msgs[0] == NULL ) || + msgs[1] == NULL || msgs[2] == NULL ) { + free( msgs[0] ); + free( msgs[1] ); + free( msgs[2] ); + return UPNP_E_OUTOF_MEMORY; + } + // send packets + if( RootDev ) { + // send 3 msg types + ret_code = NewRequestHandler( &DestAddr, 3, &msgs[0] ); + } else // sub-device + { + // send 2 msg types + ret_code = NewRequestHandler( &DestAddr, 2, &msgs[1] ); + } + + // free msgs + free( msgs[0] ); + free( msgs[1] ); + free( msgs[2] ); + + return ret_code; +} + +#endif // EXCLUDE_SSDP +#endif // INCLUDE_DEVICE_APIS diff --git a/libupnp/upnp/src/ssdp/ssdp_server.c b/libupnp/upnp/src/ssdp/ssdp_server.c new file mode 100644 index 0000000..4ad3e86 --- /dev/null +++ b/libupnp/upnp/src/ssdp/ssdp_server.c @@ -0,0 +1,1012 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#if EXCLUDE_SSDP == 0 + +#include "membuffer.h" +#include "ssdplib.h" +#include +#include "ThreadPool.h" +#include "miniserver.h" + +#include "upnpapi.h" +#include "httpparser.h" +#include "httpreadwrite.h" + +#define MAX_TIME_TOREAD 45 + +CLIENTONLY( SOCKET gSsdpReqSocket = 0; + ) + + void RequestHandler( ); + Event ErrotEvt; + + enum Listener { Idle, Stopping, Running }; + + unsigned short ssdpStopPort; + + struct SSDPSockArray { + int ssdpSock; //socket for incoming advertisments and search requests + CLIENTONLY( int ssdpReqSock; + ) //socket for sending search + //requests and receiving + // search replies + }; + +#ifdef INCLUDE_DEVICE_APIS +#if EXCLUDE_SSDP == 0 + +/************************************************************************ +* Function : AdvertiseAndReply +* +* Parameters: +* IN int AdFlag: -1 = Send shutdown, 0 = send reply, +* 1 = Send Advertisement +* IN UpnpDevice_Handle Hnd: Device handle +* IN enum SsdpSearchType SearchType:Search type for sending replies +* IN struct sockaddr_in *DestAddr:Destination address +* IN char *DeviceType:Device type +* IN char *DeviceUDN:Device UDN +* IN char *ServiceType:Service type +* IN int Exp:Advertisement age +* +* Description: +* This function sends SSDP advertisements, replies and shutdown messages. +* +* Returns: int +* UPNP_E_SUCCESS if successful else appropriate error +***************************************************************************/ + int AdvertiseAndReply( IN int AdFlag, + IN UpnpDevice_Handle Hnd, + IN enum SsdpSearchType SearchType, + IN struct sockaddr_in *DestAddr, + IN char *DeviceType, + IN char *DeviceUDN, + IN char *ServiceType, + int Exp ) +{ + int i, + j; + int defaultExp = DEFAULT_MAXAGE; + struct Handle_Info *SInfo = NULL; + char UDNstr[100], + devType[100], + servType[100]; + IXML_NodeList *nodeList = NULL; + IXML_NodeList *tmpNodeList = NULL; + IXML_Node *tmpNode = NULL; + IXML_Node *tmpNode2 = NULL; + IXML_Node *textNode = NULL; + DOMString tmpStr; + char SERVER[200]; + + DBGONLY( const DOMString dbgStr; + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Inside AdvertiseAndReply with AdFlag = %d\n", + AdFlag ); ) + + HandleLock( ); + if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) { + HandleUnlock( ); + return UPNP_E_INVALID_HANDLE; + } + defaultExp = SInfo->MaxAge; + + //Modifed to prevent more than one thread from accessing the + //UpnpDocument stored with the handle at the same time + // HandleUnlock(); + nodeList = NULL; + + //get server info + + get_sdk_info( SERVER ); + + // parse the device list and send advertisements/replies + for( i = 0;; i++ ) { + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Entering new device list with i = %d\n\n", + i ); + ) + + tmpNode = ixmlNodeList_item( SInfo->DeviceList, i ); + if( tmpNode == NULL ) { + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting new device list with i = %d\n\n", + i ); + ) + break; + } + + DBGONLY( dbgStr = ixmlNode_getNodeName( tmpNode ); + UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Extracting device type once for %s\n", + dbgStr ); ) + // extract device type + ixmlNodeList_free( nodeList ); + nodeList = NULL; + nodeList = + ixmlElement_getElementsByTagName( ( IXML_Element * ) tmpNode, + "deviceType" ); + if( nodeList == NULL ) { + continue; + } + + DBGONLY( dbgStr = ixmlNode_getNodeName( tmpNode ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Extracting UDN for %s\n", dbgStr ); ) + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Extracting device type\n" ); + ) + + tmpNode2 = ixmlNodeList_item( nodeList, 0 ); + if( tmpNode2 == NULL ) { + continue; + } + textNode = ixmlNode_getFirstChild( tmpNode2 ); + if( textNode == NULL ) { + continue; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Extracting device type \n" ); + ) + + tmpStr = ixmlNode_getNodeValue( textNode ); + if( tmpStr == NULL ) { + continue; + } + + strcpy( devType, tmpStr ); + if( devType == NULL ) { + continue; + } + + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Extracting device type = %s\n", devType ); + if( tmpNode == NULL ) { + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "TempNode is NULL\n" );} + dbgStr = ixmlNode_getNodeName( tmpNode ); + UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Extracting UDN for %s\n", dbgStr ); ) + // extract UDN + ixmlNodeList_free( nodeList ); + nodeList = NULL; + nodeList = ixmlElement_getElementsByTagName( ( IXML_Element * ) + tmpNode, "UDN" ); + if( nodeList == NULL ) { + + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, + __LINE__, "UDN not found!!!\n" ); + ) + continue; + } + tmpNode2 = ixmlNodeList_item( nodeList, 0 ); + if( tmpNode2 == NULL ) { + + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, + __LINE__, "UDN not found!!!\n" ); + ) + continue; + } + textNode = ixmlNode_getFirstChild( tmpNode2 ); + if( textNode == NULL ) { + + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, + __LINE__, "UDN not found!!!\n" ); + ) + continue; + } + tmpStr = ixmlNode_getNodeValue( textNode ); + if( tmpStr == NULL ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__, + "UDN not found!!!!\n" ); + ) + continue; + } + strcpy( UDNstr, tmpStr ); + if( UDNstr == NULL ) { + continue; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Sending UDNStr = %s \n", UDNstr ); + ) + if( AdFlag ) { + // send the device advertisement + if( AdFlag == 1 ) { + DeviceAdvertisement( devType, i == 0, + UDNstr, SInfo->DescURL, Exp ); + } else // AdFlag == -1 + { + DeviceShutdown( devType, i == 0, UDNstr, + SERVER, SInfo->DescURL, Exp ); + } + } else { + switch ( SearchType ) { + + case SSDP_ALL: + DeviceReply( DestAddr, + devType, i == 0, + UDNstr, SInfo->DescURL, defaultExp ); + break; + + case SSDP_ROOTDEVICE: + if( i == 0 ) { + SendReply( DestAddr, devType, 1, + UDNstr, SInfo->DescURL, defaultExp, 0 ); + } + break; + case SSDP_DEVICEUDN: + { + if( DeviceUDN != NULL && strlen( DeviceUDN ) != 0 ) { + if( strcasecmp( DeviceUDN, UDNstr ) ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, API, __FILE__, + __LINE__, + "DeviceUDN=%s and search " + "UDN=%s did not match\n", + UDNstr, DeviceUDN ); + ) + break; + } else { + DBGONLY( UpnpPrintf + ( UPNP_INFO, API, __FILE__, + __LINE__, + "DeviceUDN=%s and search " + "UDN=%s MATCH\n", UDNstr, + DeviceUDN ); + ) + SendReply( DestAddr, devType, 0, + UDNstr, SInfo->DescURL, + defaultExp, 0 ); + break; + } + } + } + case SSDP_DEVICETYPE: + { + if( !strncasecmp + ( DeviceType, devType, + strlen( DeviceType ) ) ) { + DBGONLY( UpnpPrintf + ( UPNP_INFO, API, __FILE__, __LINE__, + "DeviceType=%s and search devType=%s MATCH\n", + devType, DeviceType ); + ) + SendReply( DestAddr, devType, 0, UDNstr, + SInfo->DescURL, defaultExp, 1 ); + } + + DBGONLY( + else + UpnpPrintf( UPNP_INFO, API, __FILE__, + __LINE__, + "DeviceType=%s and search devType=%s" + " DID NOT MATCH\n", + devType, DeviceType ); + ) + + break; + } + default: + break; + } + } + // send service advertisements for services corresponding + // to the same device + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Sending service Advertisement\n" ); + ) + + tmpNode = ixmlNodeList_item( SInfo->ServiceList, i ); + if( tmpNode == NULL ) { + continue; + } + ixmlNodeList_free( nodeList ); + nodeList = NULL; + nodeList = ixmlElement_getElementsByTagName( ( IXML_Element * ) + tmpNode, "service" ); + if( nodeList == NULL ) { + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "Service not found 3\n" ); + ) + continue; + } + for( j = 0;; j++ ) { + tmpNode = ixmlNodeList_item( nodeList, j ); + if( tmpNode == NULL ) + break; + + ixmlNodeList_free( tmpNodeList ); + tmpNodeList = NULL; + tmpNodeList = ixmlElement_getElementsByTagName( ( IXML_Element + * ) tmpNode, + "serviceType" ); + + if( tmpNodeList == NULL ) { + DBGONLY( UpnpPrintf + ( UPNP_CRITICAL, API, __FILE__, __LINE__, + "ServiceType not found \n" ); + ) + continue; + } + tmpNode2 = ixmlNodeList_item( tmpNodeList, 0 ); + if( tmpNode2 == NULL ) { + continue; + } + textNode = ixmlNode_getFirstChild( tmpNode2 ); + if( textNode == NULL ) { + continue; + } + // servType is of format Servicetype:ServiceVersion + tmpStr = ixmlNode_getNodeValue( textNode ); + if( tmpStr == NULL ) { + continue; + } + strcpy( servType, tmpStr ); + if( servType == NULL ) { + continue; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "ServiceType = %s\n", servType ); + ) + if( AdFlag ) { + if( AdFlag == 1 ) { + ServiceAdvertisement( UDNstr, servType, + SInfo->DescURL, Exp ); + } else // AdFlag == -1 + { + ServiceShutdown( UDNstr, servType, + SInfo->DescURL, Exp ); + } + + } else { + switch ( SearchType ) { + case SSDP_ALL: + { + ServiceReply( DestAddr, servType, + UDNstr, SInfo->DescURL, + defaultExp ); + break; + } + case SSDP_SERVICE: + { + if( ServiceType != NULL ) { + if( !strncasecmp( ServiceType, + servType, + strlen( ServiceType ) ) ) + { + ServiceReply( DestAddr, servType, + UDNstr, SInfo->DescURL, + defaultExp ); + } + } + break; + } + default: + break; + } // switch(SearchType) + + } + } + ixmlNodeList_free( tmpNodeList ); + tmpNodeList = NULL; + ixmlNodeList_free( nodeList ); + nodeList = NULL; + } + DBGONLY( UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, + "Exiting AdvertiseAndReply : \n" ); + ) + + HandleUnlock( ); + + return UPNP_E_SUCCESS; + +} /****************** End of AdvertiseAndReply *********************/ + +#endif +#endif + +/************************************************************************ +* Function : Make_Socket_NoBlocking +* +* Parameters: +* IN int sock: socket +* +* Description: +* This function makes socket non-blocking. +* +* Returns: int +* 0 if successful else -1 +***************************************************************************/ +int +Make_Socket_NoBlocking( int sock ) +{ + + int val; + + val = fcntl( sock, F_GETFL, 0 ); + if( fcntl( sock, F_SETFL, val | O_NONBLOCK ) == -1 ) { + return -1; + } + + return 0; +} + +/************************************************************************ +* Function : unique_service_name +* +* Parameters: +* IN char *cmd: Service Name string +* OUT SsdpEvent *Evt: The SSDP event structure partially filled +* by all the function. +* +* Description: +* This function fills the fields of the event structure like DeviceType, +* Device UDN and Service Type +* +* Returns: int +* 0 if successful else -1 +***************************************************************************/ +int +unique_service_name( IN char *cmd, + IN SsdpEvent * Evt ) +{ + char *TempPtr, + TempBuf[COMMAND_LEN], + *Ptr, + *ptr1, + *ptr2, + *ptr3; + int CommandFound = 0; + + if( ( TempPtr = strstr( cmd, "uuid:schemas" ) ) != NULL ) { + + ptr1 = strstr( cmd, ":device" ); + if( ptr1 != NULL ) { + ptr2 = strstr( ptr1 + 1, ":" ); + } else { + return -1; + } + + if( ptr2 != NULL ) { + ptr3 = strstr( ptr2 + 1, ":" ); + } else { + return -1; + } + + if( ptr3 != NULL ) { + sprintf( Evt->UDN, "uuid:%s", ptr3 + 1 ); + } else { + return -1; + } + + ptr1 = strstr( cmd, ":" ); + if( ptr1 != NULL ) { + strncpy( TempBuf, ptr1, ptr3 - ptr1 ); + TempBuf[ptr3 - ptr1] = '\0'; + sprintf( Evt->DeviceType, "urn%s", TempBuf ); + } else { + return -1; + } + return 0; + } + + if( ( TempPtr = strstr( cmd, "uuid" ) ) != NULL ) { + //printf("cmd = %s\n",cmd); + if( ( Ptr = strstr( cmd, "::" ) ) != NULL ) { + strncpy( Evt->UDN, TempPtr, Ptr - TempPtr ); + Evt->UDN[Ptr - TempPtr] = '\0'; + } else { + strcpy( Evt->UDN, TempPtr ); + } + CommandFound = 1; + } + + if( strstr( cmd, "urn:" ) != NULL + && strstr( cmd, ":service:" ) != NULL ) { + + if( ( TempPtr = strstr( cmd, "urn" ) ) != NULL ) { + strcpy( Evt->ServiceType, TempPtr ); + CommandFound = 1; + } + } + + if( strstr( cmd, "urn:" ) != NULL + && strstr( cmd, ":device:" ) != NULL ) { + if( ( TempPtr = strstr( cmd, "urn" ) ) != NULL ) { + strcpy( Evt->DeviceType, TempPtr ); + CommandFound = 1; + } + } + + if( CommandFound == 0 ) { + + return -1; + } + + return 0; +} + +/************************************************************************ +* Function : ssdp_request_type1 +* +* Parameters: +* IN char *cmd: command came in the ssdp request +* +* Description: +* This function figures out the type of the SSDP search in the +* in the request. +* +* Returns: enum SsdpSearchType +* return appropriate search type else returns SSDP_ERROR +***************************************************************************/ +enum SsdpSearchType +ssdp_request_type1( IN char *cmd ) +{ + if( strstr( cmd, ":all" ) != NULL ) { + return SSDP_ALL; + } + if( strstr( cmd, ":rootdevice" ) != NULL ) { + return SSDP_ROOTDEVICE; + } + if( strstr( cmd, "uuid:" ) != NULL ) { + return SSDP_DEVICEUDN; + } + if( ( strstr( cmd, "urn:" ) != NULL ) + && ( strstr( cmd, ":device:" ) != NULL ) ) { + return SSDP_DEVICETYPE; + } + if( ( strstr( cmd, "urn:" ) != NULL ) + && ( strstr( cmd, ":service:" ) != NULL ) ) { + return SSDP_SERVICE; + } + return SSDP_SERROR; +} + +/************************************************************************ +* Function : ssdp_request_type +* +* Parameters: +* IN char *cmd: command came in the ssdp request +* OUT SsdpEvent *Evt: The event structure partially filled by +* this function. +* +* Description: +* This function starts filling the SSDP event structure based upon the +* request received. +* +* Returns: int +* 0 on success; -1 on error +***************************************************************************/ +int +ssdp_request_type( IN char *cmd, + OUT SsdpEvent * Evt ) +{ + // clear event + memset( Evt, 0, sizeof( SsdpEvent ) ); + unique_service_name( cmd, Evt ); + Evt->ErrCode = NO_ERROR_FOUND; + + if( ( Evt->RequestType = ssdp_request_type1( cmd ) ) == SSDP_SERROR ) { + Evt->ErrCode = E_HTTP_SYNTEX; + return -1; + } + return 0; +} + +/************************************************************************ +* Function : free_ssdp_event_handler_data +* +* Parameters: +* IN void *the_data: ssdp_thread_data structure. This structure contains +* SSDP request message. +* +* Description: +* This function frees the ssdp request +* +* Returns: VOID +* +***************************************************************************/ +static void +free_ssdp_event_handler_data( void *the_data ) +{ + ssdp_thread_data *data = ( ssdp_thread_data * ) the_data; + + if( data != NULL ) { + http_message_t *hmsg = &data->parser.msg; + + // free data + httpmsg_destroy( hmsg ); + free( data ); + } +} + +/************************************************************************ +* Function : valid_ssdp_msg +* +* Parameters: +* IN void *the_data: ssdp_thread_data structure. This structure contains +* SSDP request message. +* +* Description: +* This function do some quick checking of the ssdp msg +* +* Returns: xboolean +* returns TRUE if msg is valid else FALSE +***************************************************************************/ +static XINLINE xboolean +valid_ssdp_msg( IN http_message_t * hmsg ) +{ + memptr hdr_value; + + // check for valid methods - NOTIFY or M-SEARCH + if( hmsg->method != HTTPMETHOD_NOTIFY && + hmsg->method != HTTPMETHOD_MSEARCH + && hmsg->request_method != HTTPMETHOD_MSEARCH ) { + return FALSE; + } + if( hmsg->request_method != HTTPMETHOD_MSEARCH ) { + // check PATH == * + if( hmsg->uri.type != RELATIVE || + strncmp( "*", hmsg->uri.pathquery.buff, + hmsg->uri.pathquery.size ) != 0 ) { + return FALSE; + } + // check HOST header + if( ( httpmsg_find_hdr( hmsg, HDR_HOST, &hdr_value ) == NULL ) || + ( memptr_cmp( &hdr_value, "239.255.255.250:1900" ) != 0 ) + ) { + return FALSE; + } + } + return TRUE; // passed quick check +} + +/************************************************************************ +* Function : start_event_handler +* +* Parameters: +* IN void *the_data: ssdp_thread_data structure. This structure contains +* SSDP request message. +* +* Description: +* This function parses the message and dispatches it to a handler +* which handles the ssdp request msg +* +* Returns: int +* 0 if successful -1 if error +***************************************************************************/ +static XINLINE int +start_event_handler( void *Data ) +{ + + http_parser_t *parser = NULL; + parse_status_t status; + ssdp_thread_data *data = ( ssdp_thread_data * ) Data; + + parser = &data->parser; + + status = parser_parse( parser ); + if( status == PARSE_FAILURE ) { + if( parser->msg.method != HTTPMETHOD_NOTIFY || + !parser->valid_ssdp_notify_hack ) { + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP recvd bad msg code = %d\n", + status ); + ) + // ignore bad msg, or not enuf mem + goto error_handler; + } + // valid notify msg + } else if( status != PARSE_SUCCESS ) { + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, + "SSDP recvd bad msg code = %d\n", status ); + ) + + goto error_handler; + } + // check msg + if( !valid_ssdp_msg( &parser->msg ) ) { + goto error_handler; + } + return 0; //////// done; thread will free 'data' + + error_handler: + free_ssdp_event_handler_data( data ); + return -1; +} + +/************************************************************************ +* Function : ssdp_event_handler_thread +* +* Parameters: +* IN void *the_data: ssdp_thread_data structure. This structure contains +* SSDP request message. +* +* Description: +* This function is a thread that handles SSDP requests. +* +* Returns: void +* +***************************************************************************/ +static void +ssdp_event_handler_thread( void *the_data ) +{ + ssdp_thread_data *data = ( ssdp_thread_data * ) the_data; + http_message_t *hmsg = &data->parser.msg; + + if( start_event_handler( the_data ) != 0 ) { + return; + } + // send msg to device or ctrlpt + if( ( hmsg->method == HTTPMETHOD_NOTIFY ) || + ( hmsg->request_method == HTTPMETHOD_MSEARCH ) ) { + + CLIENTONLY( ssdp_handle_ctrlpt_msg( hmsg, &data->dest_addr, + FALSE, NULL ); + ); + } else { + + DEVICEONLY( ssdp_handle_device_request( hmsg, &data->dest_addr ); + ); + } + + // free data + free_ssdp_event_handler_data( data ); +} + +/************************************************************************ +* Function : readFromSSDPSocket +* +* Parameters: +* IN SOCKET socket: SSDP socket +* +* Description: +* This function reads the data from the ssdp socket. +* +* Returns: void +* +***************************************************************************/ +void +readFromSSDPSocket( SOCKET socket ) +{ + char *requestBuf = NULL; + char staticBuf[BUFSIZE]; + struct sockaddr_in clientAddr; + ThreadPoolJob job; + ssdp_thread_data *data = NULL; + socklen_t socklen = 0; + int byteReceived = 0; + + requestBuf = staticBuf; + + //in case memory + //can't be allocated, still drain the + //socket using a static buffer + + socklen = sizeof( struct sockaddr_in ); + + data = ( ssdp_thread_data * ) + malloc( sizeof( ssdp_thread_data ) ); + + if( data != NULL ) { + //initialize parser + +#ifdef INCLUDE_CLIENT_APIS + if( socket == gSsdpReqSocket ) { + parser_response_init( &data->parser, HTTPMETHOD_MSEARCH ); + } else { + parser_request_init( &data->parser ); + } +#else + parser_request_init( &data->parser ); +#endif + + //set size of parser buffer + + if( membuffer_set_size( &data->parser.msg.msg, BUFSIZE ) == 0 ) { + //use this as the buffer for recv + requestBuf = data->parser.msg.msg.buf; + + } else { + free( data ); + data = NULL; + } + } + byteReceived = recvfrom( socket, requestBuf, + BUFSIZE - 1, 0, + ( struct sockaddr * )&clientAddr, &socklen ); + + if( byteReceived > 0 ) { + + requestBuf[byteReceived] = '\0'; + DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, + __FILE__, __LINE__, + "Received response !!! " + "%s From host %s \n", + requestBuf, + inet_ntoa( clientAddr.sin_addr ) ); + ) + + DBGONLY( UpnpPrintf( UPNP_PACKET, SSDP, + __FILE__, __LINE__, + "Received multicast packet:" + "\n %s\n", requestBuf ); + ) + //add thread pool job to handle request + if( data != NULL ) { + data->parser.msg.msg.length += byteReceived; + // null-terminate + data->parser.msg.msg.buf[byteReceived] = 0; + data->dest_addr = clientAddr; + TPJobInit( &job, ( start_routine ) + ssdp_event_handler_thread, data ); + TPJobSetFreeFunction( &job, free_ssdp_event_handler_data ); + TPJobSetPriority( &job, MED_PRIORITY ); + + if( ThreadPoolAdd( &gRecvThreadPool, &job, NULL ) != 0 ) { + free_ssdp_event_handler_data( data ); + } + } + + } else { + free_ssdp_event_handler_data( data ); + } +} + +/************************************************************************ +* Function : get_ssdp_sockets +* +* Parameters: +* OUT MiniServerSockArray *out: Arrays of SSDP sockets +* +* Description: +* This function creates the ssdp sockets. It set their option to listen +* for multicast traffic. +* +* Returns: int +* return UPNP_E_SUCCESS if successful else returns appropriate error +***************************************************************************/ +int +get_ssdp_sockets( MiniServerSockArray * out ) +{ + SOCKET ssdpSock; + + CLIENTONLY( SOCKET ssdpReqSock; + ) + int onOff = 1; + u_char ttl = 4; + struct ip_mreq ssdpMcastAddr; + struct sockaddr_in ssdpAddr; + int option = 1; + + CLIENTONLY( if( ( ssdpReqSock = socket( AF_INET, SOCK_DGRAM, 0 ) ) + == UPNP_INVALID_SOCKET ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, + SSDP, __FILE__, __LINE__, + "Error in socket operation !!!\n" ); ) + return UPNP_E_OUTOF_SOCKET;} + setsockopt( ssdpReqSock, + IPPROTO_IP, + IP_MULTICAST_TTL, &ttl, sizeof( ttl ) ); + // just do it, regardless if fails or not. + Make_Socket_NoBlocking( ssdpReqSock ); gSsdpReqSocket = ssdpReqSock; ) //CLIENTONLY + + if( ( ssdpSock = socket( AF_INET, SOCK_DGRAM, 0 ) ) + == UPNP_INVALID_SOCKET ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, + SSDP, __FILE__, __LINE__, + "Error in socket operation !!!\n" ); + ) + CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ) ); + CLIENTONLY( UpnpCloseSocket( ssdpReqSock ) ); + return UPNP_E_OUTOF_SOCKET; + } + + onOff = 1; + if( setsockopt( ssdpSock, SOL_SOCKET, SO_REUSEADDR, + ( char * )&onOff, sizeof( onOff ) ) != 0 ) { + + DBGONLY( UpnpPrintf( UPNP_CRITICAL, + SSDP, __FILE__, __LINE__, + "Error in set reuse addr !!!\n" ); + ) + CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ) ); + CLIENTONLY( UpnpCloseSocket( ssdpReqSock ) ); + shutdown( ssdpSock, SD_BOTH ); + UpnpCloseSocket( ssdpSock ); + return UPNP_E_SOCKET_ERROR; + } + + memset( ( void * )&ssdpAddr, 0, sizeof( struct sockaddr_in ) ); + ssdpAddr.sin_family = AF_INET; + // ssdpAddr.sin_addr.s_addr = inet_addr(LOCAL_HOST); + ssdpAddr.sin_addr.s_addr = htonl( INADDR_ANY ); + ssdpAddr.sin_port = htons( SSDP_PORT ); + if( bind + ( ssdpSock, ( struct sockaddr * )&ssdpAddr, + sizeof( ssdpAddr ) ) != 0 ) { + DBGONLY( UpnpPrintf + ( UPNP_CRITICAL, SSDP, __FILE__, __LINE__, + "Error in binding !!!\n" ); + ) + shutdown( ssdpSock, SD_BOTH ); + UpnpCloseSocket( ssdpSock ); + CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ) ); + CLIENTONLY( UpnpCloseSocket( ssdpReqSock ) ); + return UPNP_E_SOCKET_BIND; + } + + memset( ( void * )&ssdpMcastAddr, 0, sizeof( struct ip_mreq ) ); + ssdpMcastAddr.imr_interface.s_addr = htonl( INADDR_ANY ); + ssdpMcastAddr.imr_multiaddr.s_addr = inet_addr( SSDP_IP ); + if( setsockopt( ssdpSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + ( char * )&ssdpMcastAddr, + sizeof( struct ip_mreq ) ) != 0 ) { + DBGONLY( UpnpPrintf + ( UPNP_CRITICAL, SSDP, __FILE__, __LINE__, + "Error in joining" " multicast group !!!\n" ); + ) + shutdown( ssdpSock, SD_BOTH ); + CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ) ); + UpnpCloseSocket( ssdpSock ); + CLIENTONLY( UpnpCloseSocket( ssdpReqSock ) ); + return UPNP_E_SOCKET_ERROR; + } + // result is not checked becuase it will fail in WinMe and Win9x. + setsockopt( ssdpSock, IPPROTO_IP, + IP_MULTICAST_TTL, &ttl, sizeof( ttl ) ); + if( setsockopt( ssdpSock, SOL_SOCKET, SO_BROADCAST, + ( char * )&option, sizeof( option ) ) != 0 ) { + DBGONLY( UpnpPrintf( UPNP_CRITICAL, + SSDP, __FILE__, __LINE__, + "Error in setting broadcast !!!\n" ); + ) + shutdown( ssdpSock, SD_BOTH ); + CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ) ); + UpnpCloseSocket( ssdpSock ); + CLIENTONLY( UpnpCloseSocket( ssdpReqSock ) ); + return UPNP_E_NETWORK_ERROR; + } + + CLIENTONLY( out->ssdpReqSock = ssdpReqSock; + ); + out->ssdpSock = ssdpSock; + return UPNP_E_SUCCESS; +} + +#endif // EXCLUDE_SSDP diff --git a/libupnp/upnp/src/urlconfig/urlconfig.c b/libupnp/upnp/src/urlconfig/urlconfig.c new file mode 100644 index 0000000..fb0942e --- /dev/null +++ b/libupnp/upnp/src/urlconfig/urlconfig.c @@ -0,0 +1,425 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2000-2003 Intel Corporation +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +#include "config.h" +#include +#include +#include +#include +#include "upnp.h" +#include "util.h" +#include "webserver.h" +#include "uri.h" +#include "membuffer.h" +#include "urlconfig.h" + +/************************************************************************ +* Function : addrToString +* +* Parameters : +* IN const struct sockaddr_in* addr ; socket address object with +* the IP Address and port information +* OUT char ipaddr_port[] ; character array which will hold the +* IP Address in a string format. +* +* Description : Converts an Internet address to a string and stores it +* a buffer. +* +* Return : void ; +* +* Note : +************************************************************************/ +static XINLINE void +addrToString( IN const struct sockaddr_in *addr, + OUT char ipaddr_port[] ) +{ + sprintf( ipaddr_port, "%s:%d", inet_ntoa( addr->sin_addr ), + ntohs( addr->sin_port ) ); +} + +/************************************************************************ +* Function : calc_alias +* +* Parameters : +* IN const char* alias ; String containing the alias +* IN const char* rootPath ; String containing the root path +* OUT char** newAlias ; String pointer to hold the modified new +* alias +* +* Description : Determine alias based urlbase's root path. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success. +* UPNP_E_OUTOF_MEMORY - On Failure to allocate memory for new alias +* +* Note : 'newAlias' should be freed using free() +************************************************************************/ +static XINLINE int +calc_alias( IN const char *alias, + IN const char *rootPath, + OUT char **newAlias ) +{ + const char *aliasPtr; + size_t root_len; + char *temp_str; + size_t new_alias_len; + char *alias_temp; + + assert( rootPath ); + assert( alias ); + + // add / suffix, if missing + root_len = strlen( rootPath ); + if( root_len == 0 || rootPath[root_len - 1] != '/' ) { + temp_str = "/"; + } else { + temp_str = ""; // suffix already present + } + + // discard / prefix, if present + if( alias[0] == '/' ) { + aliasPtr = alias + 1; + } else { + aliasPtr = alias; + } + + new_alias_len = root_len + strlen( temp_str ) + strlen( aliasPtr ); + alias_temp = ( char * )malloc( new_alias_len + 1 ); + if( alias_temp == NULL ) { + return UPNP_E_OUTOF_MEMORY; + } + + strcpy( alias_temp, rootPath ); + strcat( alias_temp, temp_str ); + strcat( alias_temp, aliasPtr ); + + *newAlias = alias_temp; + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : calc_descURL +* +* Parameters : +* IN const char* ipPortStr ; String containing the port number +* IN const char* alias ; String containing the alias +* OUT char descURL[LINE_SIZE] ; buffer to hold the calculated +* description URL +* +* Description : Determines the description URL +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_URL_TOO_BIG - length of the URL is determined to be to +* exceeding the limit. +* +* Note : +************************************************************************/ +static XINLINE int +calc_descURL( IN const char *ipPortStr, + IN const char *alias, + OUT char descURL[LINE_SIZE] ) +{ + size_t len; + const char *http_scheme = "http://"; + + assert( ipPortStr != NULL && strlen( ipPortStr ) > 0 ); + assert( alias != NULL && strlen( alias ) > 0 ); + + len = strlen( http_scheme ) + strlen( ipPortStr ) + strlen( alias ); + + if( len > ( LINE_SIZE - 1 ) ) { + return UPNP_E_URL_TOO_BIG; + } + strcpy( descURL, http_scheme ); + strcat( descURL, ipPortStr ); + strcat( descURL, alias ); + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "desc url: %s\n", descURL ); + ) + + return UPNP_E_SUCCESS; +} + +/************************************************************************ +* Function : config_description_doc +* +* Parameters : +* INOUT IXML_Document *doc ;IMXL description document to be +* configured +* IN const char* ip_str ; string containing the IP port number +* OUT char** root_path_str ; buffer to hold the root path +* of the configured description document +* INOUT IXML_Document *doc : Description document +* IN const char* ip_str : ipaddress string +* OUT char** root_path_str : root path string +* +* Description : Configure the description document. Add the standard +* format and then add information from the root device and any +* child nodes. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - Default Error +* UPNP_E_INVALID_DESC - Invalid child node +* UPNP_E_INVALID_URL - Invalid node information +* +* Note : +************************************************************************/ +static int +config_description_doc( INOUT IXML_Document * doc, + IN const char *ip_str, + OUT char **root_path_str ) +{ + xboolean addNew = FALSE; + IXML_NodeList *baseList; + IXML_Element *element = NULL; + IXML_Element *newElement = NULL; + IXML_Node *textNode = NULL; + IXML_Node *rootNode = NULL; + IXML_Node *urlbase_node = NULL; + char *urlBaseStr = "URLBase"; + DOMString domStr = NULL; + uri_type uri; + int err_code; + int len; + membuffer url_str; + membuffer root_path; + + membuffer_init( &url_str ); + membuffer_init( &root_path ); + + err_code = UPNP_E_OUTOF_MEMORY; // default error + + baseList = ixmlDocument_getElementsByTagName( doc, urlBaseStr ); + if( baseList == NULL ) { + // urlbase not found -- create new one + addNew = TRUE; + element = ixmlDocument_createElement( doc, urlBaseStr ); + if( element == NULL ) { + goto error_handler; + } + + if( membuffer_append_str( &url_str, "http://" ) != 0 || + membuffer_append_str( &url_str, ip_str ) != 0 || + membuffer_append_str( &url_str, "/" ) != 0 || + membuffer_append_str( &root_path, "/" ) != 0 ) { + goto error_handler; + } + + rootNode = ixmlNode_getFirstChild( ( IXML_Node * ) doc ); + if( rootNode == NULL ) { + err_code = UPNP_E_INVALID_DESC; + goto error_handler; + } + + err_code = + ixmlNode_appendChild( rootNode, ( IXML_Node * ) element ); + if( err_code != IXML_SUCCESS ) { + goto error_handler; + } + + textNode = + ixmlDocument_createTextNode( doc, ( char * )url_str.buf ); + if( textNode == NULL ) { + goto error_handler; + } + + err_code = + ixmlNode_appendChild( ( IXML_Node * ) element, textNode ); + if( err_code != IXML_SUCCESS ) { + goto error_handler; + } + + } else { + // urlbase found + urlbase_node = ixmlNodeList_item( baseList, 0 ); + assert( urlbase_node != NULL ); + + textNode = ixmlNode_getFirstChild( urlbase_node ); + if( textNode == NULL ) { + err_code = UPNP_E_INVALID_DESC; + goto error_handler; + } + + domStr = ixmlNode_getNodeValue( textNode ); + if( domStr == NULL ) { + err_code = UPNP_E_INVALID_URL; + goto error_handler; + } + + len = parse_uri( domStr, strlen( domStr ), &uri ); + if( len < 0 || uri.type != ABSOLUTE ) { + err_code = UPNP_E_INVALID_URL; + goto error_handler; + } + + if( membuffer_assign( &url_str, uri.scheme.buff, + uri.scheme.size ) != 0 || + membuffer_append_str( &url_str, "://" ) != 0 || + membuffer_append_str( &url_str, ip_str ) != 0 ) { + goto error_handler; + } + // add leading '/' if missing from relative path + if( ( uri.pathquery.size > 0 && uri.pathquery.buff[0] != '/' ) || + ( uri.pathquery.size == 0 ) + ) { + if( membuffer_append_str( &url_str, "/" ) != 0 || + membuffer_append_str( &root_path, "/" ) != 0 ) { + goto error_handler; + } + } + + if( membuffer_append( &url_str, uri.pathquery.buff, + uri.pathquery.size ) != 0 || + membuffer_append( &root_path, uri.pathquery.buff, + uri.pathquery.size ) != 0 ) { + goto error_handler; + } + // add trailing '/' if missing + if( url_str.buf[url_str.length - 1] != '/' ) { + if( membuffer_append( &url_str, "/", 1 ) != 0 ) { + goto error_handler; + } + } + + err_code = ixmlNode_setNodeValue( textNode, url_str.buf ); + if( err_code != IXML_SUCCESS ) { + goto error_handler; + } + } + + *root_path_str = membuffer_detach( &root_path ); // return path + err_code = UPNP_E_SUCCESS; + + error_handler: + if( err_code != UPNP_E_SUCCESS ) { + ixmlElement_free( newElement ); + } + + ixmlNodeList_free( baseList ); + + membuffer_destroy( &root_path ); + membuffer_destroy( &url_str ); + + return err_code; +} + +/************************************************************************ +* Function : configure_urlbase +* +* Parameters : +* INOUT IXML_Document *doc ; IXML Description document +* IN const struct sockaddr_in* serverAddr ; socket address object +* providing the IP address and port information +* IN const char* alias ; string containing the alias +* IN time_t last_modified ; time when the XML document was +* downloaded +* OUT char docURL[LINE_SIZE] ; buffer to hold the URL of the +* document. +* INOUT IXML_Document *doc:dom document whose urlbase is to be modified +* IN const struct sockaddr_in* serverAddr : ip address and port of +* the miniserver +* IN const char* alias : a name to be used for the temp; e.g.:"foo.xml" +* IN time_t last_modified : time +* OUT char docURL[LINE_SIZE] : document URL +* +* Description : Configure the full URL for the description document. +* Create the URL document and add alias, description information. +* The doc is added to the web server to be served using the given +* alias. +* +* Return : int ; +* UPNP_E_SUCCESS - On Success +* UPNP_E_OUTOF_MEMORY - Default Error +* +* Note : +************************************************************************/ +int +configure_urlbase( INOUT IXML_Document * doc, + IN const struct sockaddr_in *serverAddr, + IN const char *alias, + IN time_t last_modified, + OUT char docURL[LINE_SIZE] ) +{ + char *root_path = NULL; + char *new_alias = NULL; + char *xml_str = NULL; + int err_code; + char ipaddr_port[LINE_SIZE]; + + err_code = UPNP_E_OUTOF_MEMORY; // default error + + // get IP address and port + addrToString( serverAddr, ipaddr_port ); + + // config url-base in 'doc' + err_code = config_description_doc( doc, ipaddr_port, &root_path ); + if( err_code != UPNP_E_SUCCESS ) { + goto error_handler; + } + // calc alias + err_code = calc_alias( alias, root_path, &new_alias ); + if( err_code != UPNP_E_SUCCESS ) { + goto error_handler; + } + // calc full url for desc doc + err_code = calc_descURL( ipaddr_port, new_alias, docURL ); + if( err_code != UPNP_E_SUCCESS ) { + goto error_handler; + } + // xml doc to str + xml_str = ixmlPrintDocument( doc ); + if( xml_str == NULL ) { + goto error_handler; + } + + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "desc url: %s\n", docURL ); + ) + DBGONLY( UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__, + "doc = %s\n", xml_str ); + ) + // store in web server + err_code = + web_server_set_alias( new_alias, xml_str, strlen( xml_str ), + last_modified ); + + error_handler: + free( root_path ); + free( new_alias ); + + if( err_code != UPNP_E_SUCCESS ) { + ixmlFreeDOMString( xml_str ); + } + return err_code; +} diff --git a/libupnp/upnp/src/uuid/md5.c b/libupnp/upnp/src/uuid/md5.c new file mode 100644 index 0000000..e60016a --- /dev/null +++ b/libupnp/upnp/src/uuid/md5.c @@ -0,0 +1,432 @@ +/* + MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + + */ + +#include "config.h" +#include "global.h" +#include "md5.h" + +/* + Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST( ( UINT4[4], + unsigned char[64] ) ); +static void Encode PROTO_LIST( ( unsigned char *, + UINT4 *, + unsigned int ) ); + +static void Decode PROTO_LIST( ( UINT4 *, + unsigned char *, + unsigned int ) ); + +static void MD5_memcpy PROTO_LIST( ( POINTER, + POINTER, + unsigned int ) ); +static void MD5_memset PROTO_LIST( ( POINTER, + int, + unsigned int ) ); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* + ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) <<(n)) | ((x) >> (32-(n)))) + +/* + FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* + MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init( context ) + MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* + Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* + MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ + +void +MD5Update( context, + input, + inputLen ) + MD5_CTX *context; /* context */ + unsigned char *input; /* input block */ + unsigned int inputLen; /* length of input block */ +{ + unsigned int i, + index, + partLen; + + /* + Compute number of bytes mod 64 + */ + index = ( unsigned int )( ( context->count[0] >> 3 ) & 0x3F ); + + /* + Update number of bits + */ + if( ( context->count[0] += + ( ( UINT4 ) inputLen << 3 ) ) < ( ( UINT4 ) inputLen << 3 ) ) + context->count[1]++; + + context->count[1] += ( ( UINT4 ) inputLen >> 29 ); + + partLen = 64 - index; + + /* + Transform as many times as possible. + */ + if( inputLen >= partLen ) { + + MD5_memcpy + ( ( POINTER ) & context->buffer[index], ( POINTER ) input, + partLen ); + MD5Transform( context->state, context->buffer ); + + for( i = partLen; i + 63 < inputLen; i += 64 ) + MD5Transform( context->state, &input[i] ); + + index = 0; + } else + i = 0; + + /* + Buffer remaining input + */ + MD5_memcpy + ( ( POINTER ) & context->buffer[index], ( POINTER ) & input[i], + inputLen - i ); + +} + +/* + MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ + +void +MD5Final( digest, + context ) + unsigned char digest[16]; /* message digest */ + MD5_CTX *context; /* context */ +{ + + unsigned char bits[8]; + unsigned int index, + padLen; + + /* + Save number of bits + */ + Encode( bits, context->count, 8 ); + + /* + Pad out to 56 mod 64. + */ + index = ( unsigned int )( ( context->count[0] >> 3 ) & 0x3f ); + + padLen = ( index < 56 ) ? ( 56 - index ) : ( 120 - index ); + + MD5Update( context, PADDING, padLen ); + + /* + Append length (before padding) + */ + MD5Update( context, bits, 8 ); + + /* + Store state in digest + */ + Encode( digest, context->state, 16 ); + + /* + Zeroize sensitive information. + */ + MD5_memset( ( POINTER ) context, 0, sizeof( *context ) ); + +} + +/* + MD5 basic transformation. Transforms state based on block. + */ +static void +MD5Transform( state, + block ) + UINT4 state[4]; + unsigned char block[64]; +{ + UINT4 a = state[0], + b = state[1], + c = state[2], + d = state[3], + x[16]; + + Decode( x, block, 64 ); + + /* + Round 1 + */ + FF( a, b, c, d, x[0], S11, 0xd76aa478 ); /* 1 */ + FF( d, a, b, c, x[1], S12, 0xe8c7b756 ); /* 2 */ + FF( c, d, a, b, x[2], S13, 0x242070db ); /* 3 */ + FF( b, c, d, a, x[3], S14, 0xc1bdceee ); /* 4 */ + FF( a, b, c, d, x[4], S11, 0xf57c0faf ); /* 5 */ + FF( d, a, b, c, x[5], S12, 0x4787c62a ); /* 6 */ + FF( c, d, a, b, x[6], S13, 0xa8304613 ); /* 7 */ + FF( b, c, d, a, x[7], S14, 0xfd469501 ); /* 8 */ + FF( a, b, c, d, x[8], S11, 0x698098d8 ); /* 9 */ + FF( d, a, b, c, x[9], S12, 0x8b44f7af ); /* 10 */ + FF( c, d, a, b, x[10], S13, 0xffff5bb1 ); /* 11 */ + FF( b, c, d, a, x[11], S14, 0x895cd7be ); /* 12 */ + FF( a, b, c, d, x[12], S11, 0x6b901122 ); /* 13 */ + FF( d, a, b, c, x[13], S12, 0xfd987193 ); /* 14 */ + FF( c, d, a, b, x[14], S13, 0xa679438e ); /* 15 */ + FF( b, c, d, a, x[15], S14, 0x49b40821 ); /* 16 */ + + /* + Round 2 + */ + GG( a, b, c, d, x[1], S21, 0xf61e2562 ); /* 17 */ + GG( d, a, b, c, x[6], S22, 0xc040b340 ); /* 18 */ + GG( c, d, a, b, x[11], S23, 0x265e5a51 ); /* 19 */ + GG( b, c, d, a, x[0], S24, 0xe9b6c7aa ); /* 20 */ + GG( a, b, c, d, x[5], S21, 0xd62f105d ); /* 21 */ + GG( d, a, b, c, x[10], S22, 0x2441453 ); /* 22 */ + GG( c, d, a, b, x[15], S23, 0xd8a1e681 ); /* 23 */ + GG( b, c, d, a, x[4], S24, 0xe7d3fbc8 ); /* 24 */ + GG( a, b, c, d, x[9], S21, 0x21e1cde6 ); /* 25 */ + GG( d, a, b, c, x[14], S22, 0xc33707d6 ); /* 26 */ + GG( c, d, a, b, x[3], S23, 0xf4d50d87 ); /* 27 */ + GG( b, c, d, a, x[8], S24, 0x455a14ed ); /* 28 */ + GG( a, b, c, d, x[13], S21, 0xa9e3e905 ); /* 29 */ + GG( d, a, b, c, x[2], S22, 0xfcefa3f8 ); /* 30 */ + GG( c, d, a, b, x[7], S23, 0x676f02d9 ); /* 31 */ + GG( b, c, d, a, x[12], S24, 0x8d2a4c8a ); /* 32 */ + + /* + Round 3 + */ + HH( a, b, c, d, x[5], S31, 0xfffa3942 ); /* 33 */ + HH( d, a, b, c, x[8], S32, 0x8771f681 ); /* 34 */ + HH( c, d, a, b, x[11], S33, 0x6d9d6122 ); /* 35 */ + HH( b, c, d, a, x[14], S34, 0xfde5380c ); /* 36 */ + HH( a, b, c, d, x[1], S31, 0xa4beea44 ); /* 37 */ + HH( d, a, b, c, x[4], S32, 0x4bdecfa9 ); /* 38 */ + HH( c, d, a, b, x[7], S33, 0xf6bb4b60 ); /* 39 */ + HH( b, c, d, a, x[10], S34, 0xbebfbc70 ); /* 40 */ + HH( a, b, c, d, x[13], S31, 0x289b7ec6 ); /* 41 */ + HH( d, a, b, c, x[0], S32, 0xeaa127fa ); /* 42 */ + HH( c, d, a, b, x[3], S33, 0xd4ef3085 ); /* 43 */ + HH( b, c, d, a, x[6], S34, 0x4881d05 ); /* 44 */ + HH( a, b, c, d, x[9], S31, 0xd9d4d039 ); /* 45 */ + HH( d, a, b, c, x[12], S32, 0xe6db99e5 ); /* 46 */ + HH( c, d, a, b, x[15], S33, 0x1fa27cf8 ); /* 47 */ + HH( b, c, d, a, x[2], S34, 0xc4ac5665 ); /* 48 */ + + /* + Round 4 + */ + II( a, b, c, d, x[0], S41, 0xf4292244 ); /* 49 */ + II( d, a, b, c, x[7], S42, 0x432aff97 ); /* 50 */ + II( c, d, a, b, x[14], S43, 0xab9423a7 ); /* 51 */ + II( b, c, d, a, x[5], S44, 0xfc93a039 ); /* 52 */ + II( a, b, c, d, x[12], S41, 0x655b59c3 ); /* 53 */ + II( d, a, b, c, x[3], S42, 0x8f0ccc92 ); /* 54 */ + II( c, d, a, b, x[10], S43, 0xffeff47d ); /* 55 */ + II( b, c, d, a, x[1], S44, 0x85845dd1 ); /* 56 */ + II( a, b, c, d, x[8], S41, 0x6fa87e4f ); /* 57 */ + II( d, a, b, c, x[15], S42, 0xfe2ce6e0 ); /* 58 */ + II( c, d, a, b, x[6], S43, 0xa3014314 ); /* 59 */ + II( b, c, d, a, x[13], S44, 0x4e0811a1 ); /* 60 */ + II( a, b, c, d, x[4], S41, 0xf7537e82 ); /* 61 */ + II( d, a, b, c, x[11], S42, 0xbd3af235 ); /* 62 */ + II( c, d, a, b, x[2], S43, 0x2ad7d2bb ); /* 63 */ + II( b, c, d, a, x[9], S44, 0xeb86d391 ); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* + Zeroize sensitive information. + */ + MD5_memset( ( POINTER ) x, 0, sizeof( x ) ); + +} + +/* + Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode( output, + input, + len ) + unsigned char *output; + UINT4 *input; + unsigned int len; +{ + unsigned int i, + j; + + for( i = 0, j = 0; j < len; i++, j += 4 ) { + output[j] = ( unsigned char )( input[i] & 0xff ); + output[j + 1] = ( unsigned char )( ( input[i] >> 8 ) & 0xff ); + output[j + 2] = ( unsigned char )( ( input[i] >> 16 ) & 0xff ); + output[j + 3] = ( unsigned char )( ( input[i] >> 24 ) & 0xff ); + } + +} + +/* + Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ + +static void +Decode( output, + input, + len ) + UINT4 *output; + unsigned char *input; + unsigned int len; +{ + unsigned int i, + j; + + for( i = 0, j = 0; j < len; i++, j += 4 ) + output[i] = + ( ( UINT4 ) input[j] ) | ( ( ( UINT4 ) input[j + 1] ) << 8 ) | + ( ( ( UINT4 ) input[j + 2] ) << 16 ) | + ( ( ( UINT4 ) input[j + 3] ) << 24 ); +} + +/* + Note: Replace "for loop" with standard memcpy if possible. + */ +static void +MD5_memcpy( output, + input, + len ) + POINTER output; + POINTER input; + unsigned int len; +{ + unsigned int i; + + for( i = 0; i < len; i++ ) + output[i] = input[i]; + +} + +/* + Note: Replace "for loop" with standard memset if possible. + */ +static void +MD5_memset( output, + value, + len ) + POINTER output; + int value; + unsigned int len; +{ + unsigned int i; + + for( i = 0; i < len; i++ ) + ( ( char * )output )[i] = ( char )value; + +} diff --git a/libupnp/upnp/src/uuid/sysdep.c b/libupnp/upnp/src/uuid/sysdep.c new file mode 100644 index 0000000..e43a173 --- /dev/null +++ b/libupnp/upnp/src/uuid/sysdep.c @@ -0,0 +1,162 @@ +/* + ** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. + ** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & + ** Digital Equipment Corporation, Maynard, Mass. + ** Copyright (c) 1998 Microsoft. + ** To anyone who acknowledges that this file is provided "AS IS" + ** without any express or implied warranty: permission to use, copy, + ** modify, and distribute this file for any purpose is hereby + ** granted without fee, provided that the above copyright notices and + ** this notice appears in all source code copies, and that none of + ** the names of Open Software Foundation, Inc., Hewlett-Packard + ** Company, or Digital Equipment Corporation be used in advertising + ** or publicity pertaining to distribution of the software without + ** specific, written prior permission. Neither Open Software + ** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment + ** Corporation makes any representations about the suitability of + ** this software for any purpose. + */ + +#include "config.h" +#include +#include +#include +#include "sysdep.h" + +/*-----------------------------------------------------------------------------*/ +/* + system dependent call to get IEEE node ID. + This sample implementation generates a random node ID + */ +void +get_ieee_node_identifier( uuid_node_t * node ) +{ + char seed[16]; + static int inited = 0; + static uuid_node_t saved_node; + + if( !inited ) { + get_random_info( seed ); + seed[0] |= 0x80; + memcpy( &saved_node, seed, sizeof( uuid_node_t ) ); + + inited = 1; + }; + + *node = saved_node; +}; + +/*-----------------------------------------------------------------------------*/ +/* + system dependent call to get the current system time. + Returned as 100ns ticks since Oct 15, 1582, but resolution may be + less than 100ns. + */ + +#ifdef _WINDOWS_ + +void +get_system_time( uuid_time_t * uuid_time ) +{ + ULARGE_INTEGER time; + + GetSystemTimeAsFileTime( ( FILETIME * ) & time ); + + /* + NT keeps time in FILETIME format which is 100ns ticks since + Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. + The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) + + 18 years and 5 leap days. + */ + + time.QuadPart += ( unsigned __int64 )( 1000 * 1000 * 10 ) // seconds + * ( unsigned __int64 )( 60 * 60 * 24 ) // days + * ( unsigned __int64 )( 17 + 30 + 31 + 365 * 18 + 5 ); // # of days + + *uuid_time = time.QuadPart; + +}; + +/*-----------------------------------------------------------------------------*/ +void +get_random_info( char seed[16] ) +{ + MD5_CTX c; + typedef struct { + MEMORYSTATUS m; + SYSTEM_INFO s; + FILETIME t; + LARGE_INTEGER pc; + DWORD tc; + DWORD l; + char hostname[MAX_COMPUTERNAME_LENGTH + 1]; + } randomness; + randomness r; + + MD5Init( &c ); + /* + memory usage stats + */ + GlobalMemoryStatus( &r.m ); + /* + random system stats + */ + GetSystemInfo( &r.s ); + /* + 100ns resolution (nominally) time of day + */ + GetSystemTimeAsFileTime( &r.t ); + /* + high resolution performance counter + */ + QueryPerformanceCounter( &r.pc ); + /* + milliseconds since last boot + */ + r.tc = GetTickCount( ); + r.l = MAX_COMPUTERNAME_LENGTH + 1; + + GetComputerName( r.hostname, &r.l ); + MD5Update( &c, &r, sizeof( randomness ) ); + MD5Final( seed, &c ); +}; +#else + +/*-----------------------------------------------------------------------------*/ +void +get_system_time( uuid_time_t * uuid_time ) +{ + struct timeval tp; + + gettimeofday( &tp, ( struct timezone * )0 ); + + /* + Offset between UUID formatted times and Unix formatted times. + UUID UTC base time is October 15, 1582. + Unix base time is January 1, 1970. + */ + *uuid_time = ( tp.tv_sec * 10000000 ) + ( tp.tv_usec * 10 ) + + I64( 0x01B21DD213814000 ); +}; + +/*-----------------------------------------------------------------------------*/ +void +get_random_info( char seed[16] ) +{ + MD5_CTX c; + typedef struct { +// struct sysinfo s; + struct timeval t; + char hostname[257]; + } randomness; + randomness r; + + MD5Init( &c ); + + gettimeofday( &r.t, ( struct timezone * )0 ); + gethostname( r.hostname, 256 ); + MD5Update( &c, &r, sizeof( randomness ) ); + MD5Final( seed, &c ); +}; + +#endif diff --git a/libupnp/upnp/src/uuid/uuid.c b/libupnp/upnp/src/uuid/uuid.c new file mode 100644 index 0000000..af5fe78 --- /dev/null +++ b/libupnp/upnp/src/uuid/uuid.c @@ -0,0 +1,383 @@ + /* + ** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. + ** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & + ** Digital Equipment Corporation, Maynard, Mass. + ** Copyright (c) 1998 Microsoft. + ** To anyone who acknowledges that this file is provided "AS IS" + ** without any express or implied warranty: permission to use, copy, + ** modify, and distribute this file for any purpose is hereby + ** granted without fee, provided that the above copyright notices and + ** this notice appears in all source code copies, and that none of + ** the names of Open Software Foundation, Inc., Hewlett-Packard + ** Company, or Digital Equipment Corporation be used in advertising + ** or publicity pertaining to distribution of the software without + ** specific, written prior permission. Neither Open Software + ** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment + ** Corporation makes any representations about the suitability of + ** this software for any purpose. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include "sysdep.h" +#include "uuid.h" + +/* + various forward declarations + */ +static int read_state( unsigned16 * clockseq, + uuid_time_t * timestamp, + uuid_node_t * node ); +static void write_state( unsigned16 clockseq, + uuid_time_t timestamp, + uuid_node_t node ); +static void format_uuid_v1( uuid_upnp * uid, + unsigned16 clockseq, + uuid_time_t timestamp, + uuid_node_t node ); +static void format_uuid_v3( uuid_upnp * uid, + unsigned char hash[16] ); +static void get_current_time( uuid_time_t * timestamp ); +static unsigned16 true_random( void ); + +/*-----------------------------------------------------------------------------*/ +/* + uuid_create -- generator a UUID + */ +int +uuid_create( uuid_upnp * uid ) +{ + uuid_time_t timestamp, + last_time; + unsigned16 clockseq; + uuid_node_t node; + uuid_node_t last_node; + int f; + + /* + acquire system wide lock so we're alone + */ + UUIDLock( ); + + /* + get current time + */ + get_current_time( ×tamp ); + + /* + get node ID + */ + get_ieee_node_identifier( &node ); + + /* + get saved state from NV storage + */ + f = read_state( &clockseq, &last_time, &last_node ); + + /* + if no NV state, or if clock went backwards, or node ID changed + (e.g., net card swap) change clockseq + */ + if( !f || memcmp( &node, &last_node, sizeof( uuid_node_t ) ) ) + clockseq = true_random( ); + else if( timestamp < last_time ) + clockseq++; + + /* + stuff fields into the UUID + */ + format_uuid_v1( uid, clockseq, timestamp, node ); + + /* + save the state for next time + */ + write_state( clockseq, timestamp, node ); + + UUIDUnlock( ); + return ( 1 ); +}; + +/*-----------------------------------------------------------------------------*/ +void +uuid_unpack( uuid_upnp * u, + char *out ) +{ + + sprintf( out, + "%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + ( unsigned int )u->time_low, u->time_mid, + u->time_hi_and_version, u->clock_seq_hi_and_reserved, + u->clock_seq_low, u->node[0], u->node[1], u->node[2], + u->node[3], u->node[4], u->node[5] ); + + *( out + 36 ) = '\0'; + +}; + +/*-----------------------------------------------------------------------------*/ +/* + format_uuid_v1 -- make a UUID from the timestamp, clockseq, + and node ID + */ +void +format_uuid_v1( uuid_upnp * uid, + unsigned16 clock_seq, + uuid_time_t timestamp, + uuid_node_t node ) +{ + /* + Construct a version 1 uuid with the information we've gathered + * plus a few constants. + */ + uid->time_low = ( unsigned long )( timestamp & 0xFFFFFFFF ); + uid->time_mid = ( unsigned short )( ( timestamp >> 32 ) & 0xFFFF ); + uid->time_hi_and_version = ( unsigned short )( ( timestamp >> 48 ) & + 0x0FFF ); + uid->time_hi_and_version |= ( 1 << 12 ); + uid->clock_seq_low = clock_seq & 0xFF; + uid->clock_seq_hi_and_reserved = ( clock_seq & 0x3F00 ) >> 8; + uid->clock_seq_hi_and_reserved |= 0x80; + memcpy( &uid->node, &node, sizeof uid->node ); +}; + +/*-----------------------------------------------------------------------------*/ +/* + data type for UUID generator persistent state + */ +typedef struct { + uuid_time_t ts; /* saved timestamp */ + uuid_node_t node; /* saved node ID */ + unsigned16 cs; /* saved clock sequence */ +} uuid_state; + +static uuid_state st; +static int stateInited = 0; + +/*-----------------------------------------------------------------------------*/ +/* + read_state -- read UUID generator state from non-volatile store + */ +int +read_state( unsigned16 * clockseq, + uuid_time_t * timestamp, + uuid_node_t * node ) +{ + + if( !stateInited ) { + return 0; + }; + + *clockseq = st.cs; + *timestamp = st.ts; + *node = st.node; + return 1; +}; + +/*-----------------------------------------------------------------------------*/ +/* + write_state -- save UUID generator state back to non-volatile + storage + */ +void +write_state( unsigned16 clockseq, + uuid_time_t timestamp, + uuid_node_t node ) +{ + static uuid_time_t next_save; + + if( !stateInited ) { + next_save = timestamp; + stateInited = 1; + }; + + /* + always save state to volatile shared state + */ + st.cs = clockseq; + st.ts = timestamp; + st.node = node; + if( timestamp >= next_save ) { + /* + schedule next save for 10 seconds from now + */ + next_save = timestamp + ( 10 * 10 * 1000 * 1000 ); + }; +}; + +/*-----------------------------------------------------------------------------*/ +/* + get-current_time -- get time as 60 bit 100ns ticks since whenever. + Compensate for the fact that real clock resolution is + less than 100ns. + */ +void +get_current_time( uuid_time_t * timestamp ) +{ + uuid_time_t time_now; + static uuid_time_t time_last; + static unsigned16 uuids_this_tick; + static int inited = 0; + + if( !inited ) { + get_system_time( &time_now ); + uuids_this_tick = UUIDS_PER_TICK; + inited = 1; + }; + + while( 1 ) { + get_system_time( &time_now ); + + /* + if clock reading changed since last UUID generated... + */ + if( time_last != time_now ) { + /* + reset count of uuids gen'd with this clock reading + */ + uuids_this_tick = 0; + break; + }; + + if( uuids_this_tick < UUIDS_PER_TICK ) { + uuids_this_tick++; + break; + }; + /* + going too fast for our clock; spin + */ + }; + + /* + add the count of uuids to low order bits of the clock reading + */ + *timestamp = time_now + uuids_this_tick; + time_last = *timestamp; +}; + +/*-----------------------------------------------------------------------------*/ +/* + true_random -- generate a crypto-quality random number. + This sample doesn't do that. + */ +static unsigned16 +true_random( void ) +{ + static int inited = 0; + uuid_time_t time_now; + + if( !inited ) { + get_system_time( &time_now ); + time_now = time_now / UUIDS_PER_TICK; + srand( ( unsigned int )( ( ( time_now >> 32 ) ^ time_now ) & + 0xffffffff ) ); + inited = 1; + }; + + return ( rand( ) ); +} + +/*-----------------------------------------------------------------------------*/ +/* + uuid_create_from_name -- create a UUID using a "name" from a "name space" + */ +void +uuid_create_from_name( uuid_upnp * uid, /* resulting UUID */ + + uuid_upnp nsid, /* UUID to serve as context, so identical + names from different name spaces generate + different UUIDs */ + + void *name, /* the name from which to generate a UUID */ + + int namelen /* the length of the name */ + ) +{ + MD5_CTX c; + unsigned char hash[16]; + uuid_upnp net_nsid; /* context UUID in network byte order */ + + /* + put name space ID in network byte order so it hashes the same + no matter what endian machine we're on + */ + net_nsid = nsid; + net_nsid.time_low=htonl( net_nsid.time_low ); + net_nsid.time_mid=htons( net_nsid.time_mid ); + net_nsid.time_hi_and_version=htons( net_nsid.time_hi_and_version ); + + MD5Init( &c ); + MD5Update( &c, &net_nsid, sizeof( uuid_upnp ) ); + MD5Update( &c, name, namelen ); + MD5Final( hash, &c ); + + /* + the hash is in network byte order at this point + */ + format_uuid_v3( uid, hash ); +}; + +/*-----------------------------------------------------------------------------*/ +/* + format_uuid_v3 -- make a UUID from a (pseudo)random 128 bit number + */ +void +format_uuid_v3( uuid_upnp * uid, + unsigned char hash[16] ) +{ + /* + Construct a version 3 uuid with the (pseudo-)random number + * plus a few constants. + */ + + memcpy( uid, hash, sizeof( uuid_upnp ) ); + + /* + convert UUID to local byte order + */ + uid->time_low=ntohl( uid->time_low ); + uid->time_mid=ntohs( uid->time_mid ); + uid->time_hi_and_version=ntohs( uid->time_hi_and_version ); + + /* + put in the variant and version bits + */ + uid->time_hi_and_version &= 0x0FFF; + uid->time_hi_and_version |= ( 3 << 12 ); + uid->clock_seq_hi_and_reserved &= 0x3F; + uid->clock_seq_hi_and_reserved |= 0x80; +}; + +/*-----------------------------------------------------------------------------*/ +/* + uuid_compare -- Compare two UUID's "lexically" and return + -1 u1 is lexically before u2 + 0 u1 is equal to u2 + 1 u1 is lexically after u2 + + Note: lexical ordering is not temporal ordering! + */ +int +uuid_compare( uuid_upnp * u1, + uuid_upnp * u2 ) +{ + int i; + +#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1; + CHECK( u1->time_low, u2->time_low ); + CHECK( u1->time_mid, u2->time_mid ); + CHECK( u1->time_hi_and_version, u2->time_hi_and_version ); + CHECK( u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved ); + CHECK( u1->clock_seq_low, u2->clock_seq_low ) + for( i = 0; i < 6; i++ ) { + if( u1->node[i] < u2->node[i] ) + return -1; + if( u1->node[i] > u2->node[i] ) + return 1; + } + + return 0; +}; diff --git a/libupnp/upnp/test/test_init.c b/libupnp/upnp/test/test_init.c new file mode 100644 index 0000000..98f1360 --- /dev/null +++ b/libupnp/upnp/test/test_init.c @@ -0,0 +1,133 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2006 Rémi Turboult +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + + +#include "upnp.h" +#include +#include +#if UPNP_HAVE_TOOLS +# include "upnptools.h" +#endif + + +int +main (int argc, char* argv[]) +{ + int rc; + int a, b, c; + + /* + * Check library version (and formats) + */ + printf ("\n"); + + printf ("UPNP_VERSION_STRING = \"%s\"\n", UPNP_VERSION_STRING); + printf ("UPNP_VERSION_MAJOR = %d\n", UPNP_VERSION_MAJOR); + printf ("UPNP_VERSION_MINOR = %d\n", UPNP_VERSION_MINOR); + printf ("UPNP_VERSION_PATCH = %d\n", UPNP_VERSION_PATCH); + printf ("UPNP_VERSION = %d\n", UPNP_VERSION); + + if ( sscanf (UPNP_VERSION_STRING, "%d.%d.%d", &a, &b, &c) != 3 || + a != UPNP_VERSION_MAJOR || + b != UPNP_VERSION_MINOR || + c != UPNP_VERSION_PATCH ) { + printf ("** ERROR malformed UPNP_VERSION_STRING\n"); + exit (EXIT_FAILURE); // ----------> + } + + + /* + * Check library optional features + */ + printf ("\n"); + +#if UPNP_HAVE_DEBUG + printf ("UPNP_HAVE_DEBUG \t= yes\n"); +#else + printf ("UPNP_HAVE_DEBUG \t= no\n"); +#endif + +#if UPNP_HAVE_CLIENT + printf ("UPNP_HAVE_CLIENT\t= yes\n"); +#else + printf ("UPNP_HAVE_CLIENT\t= no\n"); +#endif + +#if UPNP_HAVE_DEVICE + printf ("UPNP_HAVE_DEVICE\t= yes\n"); +#else + printf ("UPNP_HAVE_DEVICE\t= no\n"); +#endif + +#if UPNP_HAVE_WEBSERVER + printf ("UPNP_HAVE_WEBSERVER\t= yes\n"); +#else + printf ("UPNP_HAVE_WEBSERVER\t= no\n"); +#endif + +#if UPNP_HAVE_TOOLS + printf ("UPNP_HAVE_TOOLS \t= yes\n"); +#else + printf ("UPNP_HAVE_TOOLS \t= no\n"); +#endif + + + /* + * Test library initialisation + */ + printf ("\n"); + printf ("Intializing UPnP ... \n"); + rc = UpnpInit (NULL, 0); + if ( UPNP_E_SUCCESS == rc ) { + const char* ip_address = UpnpGetServerIpAddress(); + unsigned short port = UpnpGetServerPort(); + + printf ("UPnP Initialized OK ip=%s, port=%d\n", + (ip_address ? ip_address : "UNKNOWN"), port); + } else { + printf ("** ERROR UpnpInit(): %d", rc); +#if UPNP_HAVE_TOOLS + printf (" %s", UpnpGetErrorMessage (rc)); +#endif + printf ("\n"); + exit (EXIT_FAILURE); // ----------> + } + + (void) UpnpFinish(); + printf ("\n"); + + exit (EXIT_SUCCESS); +} + + + +