vino r1026 - in trunk: . docs server server/miniupnp



Author: jwendell
Date: Wed Dec 17 17:01:10 2008
New Revision: 1026
URL: http://svn.gnome.org/viewvc/vino?rev=1026&view=rev

Log:
2008-12-17  Jonh Wendell  <jwendell gnome org>

	Added UPnP support. Created a new boolean gconf key called use_upnp.
	For now, no GUI changes. Closes #564853.

	* server/miniupnp/*: miniUPnP files
	* server/vino-upnp.[ch]: New class
	* server/vino-server.[ch]: Use new VinoUpnp class
	* server/vino-prefs.c: New gconf key: use_upnp
	* server/vino-utils.[ch],
	* docs/debugging.txt: Added UPnP debug stuff



Added:
   trunk/server/miniupnp/   (props changed)
   trunk/server/miniupnp/Changelog.txt
   trunk/server/miniupnp/LICENCE
   trunk/server/miniupnp/Makefile.am
   trunk/server/miniupnp/README
   trunk/server/miniupnp/bsdqueue.h
   trunk/server/miniupnp/codelength.h
   trunk/server/miniupnp/declspec.h
   trunk/server/miniupnp/igd_desc_parse.c
   trunk/server/miniupnp/igd_desc_parse.h
   trunk/server/miniupnp/minisoap.c
   trunk/server/miniupnp/minisoap.h
   trunk/server/miniupnp/minissdpc.c
   trunk/server/miniupnp/minissdpc.h
   trunk/server/miniupnp/miniupnpc.c
   trunk/server/miniupnp/miniupnpc.h
   trunk/server/miniupnp/miniwget.c
   trunk/server/miniupnp/miniwget.h
   trunk/server/miniupnp/minixml.c
   trunk/server/miniupnp/minixml.h
   trunk/server/miniupnp/upnpcommands.c
   trunk/server/miniupnp/upnpcommands.h
   trunk/server/miniupnp/upnperrors.c
   trunk/server/miniupnp/upnperrors.h
   trunk/server/miniupnp/upnpreplyparse.c
   trunk/server/miniupnp/upnpreplyparse.h
   trunk/server/vino-upnp.c
   trunk/server/vino-upnp.h
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/docs/debugging.txt
   trunk/server/Makefile.am
   trunk/server/vino-prefs.c
   trunk/server/vino-server.c
   trunk/server/vino-server.h
   trunk/server/vino-server.schemas.in
   trunk/server/vino-util.c
   trunk/server/vino-util.h

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Wed Dec 17 17:01:10 2008
@@ -344,6 +344,7 @@
 server/Makefile
 server/libvncserver/Makefile
 server/libvncserver/rfb/Makefile
+server/miniupnp/Makefile
 capplet/Makefile
 tools/Makefile
 docs/Makefile

Modified: trunk/docs/debugging.txt
==============================================================================
--- trunk/docs/debugging.txt	(original)
+++ trunk/docs/debugging.txt	Wed Dec 17 17:01:10 2008
@@ -38,7 +38,8 @@
   + "prefs"   - spews information about the state of GConf preferences
   + "prompt"  - information about the prompt dialog displayed when a
                 client connects
-  + "dbus"    - information about d-bus stuff
+  + "dbus"    - information about D-BUS stuff
+  + "upnp"    - information about UPNP stuff
 
 
   When tracking down performance problems, I've found strace very

Modified: trunk/server/Makefile.am
==============================================================================
--- trunk/server/Makefile.am	(original)
+++ trunk/server/Makefile.am	Wed Dec 17 17:01:10 2008
@@ -1,9 +1,10 @@
 NULL =
 
-SUBDIRS = libvncserver
+SUBDIRS = libvncserver miniupnp
 
 INCLUDES = \
 	-I$(top_srcdir)/server/libvncserver \
+	-I$(top_srcdir)/server/miniupnp \
 	$(VINO_SERVER_CFLAGS) \
 	$(VINO_DEBUG_CFLAGS) \
 	$(VINO_LIBNOTIFY_CFLAGS) \
@@ -30,6 +31,7 @@
 	$(LIBGCRYPT_LIBS) \
 	$(X_LIBS) $(XTEST_LIBS) $(XSHM_LIBS) $(XDAMAGE_LIBS) \
 	$(top_builddir)/server/libvncserver/libvncserver.la \
+	$(top_builddir)/server/miniupnp/libminiupnp.la \
 	$(NULL)
 
 if HTTP_SERVER
@@ -67,6 +69,8 @@
 	vino-dbus-listener.h \
 	vino-background.c \
 	vino-background.h \
+	vino-upnp.h \
+	vino-upnp.c \
 	$(VINO_HTTP_SRC) \
 	$(CORBA_SRCLIST) \
 	$(NULL)

Added: trunk/server/miniupnp/Changelog.txt
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/Changelog.txt	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,228 @@
+$Id: Changelog.txt,v 1.73 2008/10/07 13:51:50 nanard Exp $
+miniUPnP client Changelog.
+
+2008/10/07:
+  Update docs
+
+2008/09/25:
+  Integrated sameport patch from Dario Meloni : Added a "sameport"
+  argument to upnpDiscover().
+
+2008/07/18:
+  small modif to make Clang happy :)
+
+2008/07/17:
+  #define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV... 
+
+2008/07/14:
+  include declspec.h in installation (to /usr/include/miniupnpc)
+
+VERSION 1.1 :
+
+2008/07/04:
+  standard options for install/ln instead of gnu-specific stuff.
+
+2008/07/03:
+  now builds a .dll and .lib with win32. (mingw32)
+
+2008/04/28:
+  make install now install the binary of the upnpc tool
+
+2008/04/27:
+  added testupnpigd.py
+  added error strings for miniupnpc "internal" errors
+  improved python module error/exception reporting.
+
+2008/04/23:
+  Completely rewrite igd_desc_parse.c in order to be compatible with 
+  Linksys WAG200G
+  Added testigddescparse
+  updated python module
+
+VERSION 1.0 :
+
+2008/02/21:
+  put some #ifdef DEBUG around DisplayNameValueList()
+
+2008/02/18:
+  Improved error reporting in upnpcommands.c
+  UPNP_GetStatusInfo() returns LastConnectionError
+
+2008/02/16:
+  better error handling in minisoap.c
+  improving display of "valid IGD found" in upnpc.c
+
+2008/02/03:
+  Fixing UPNP_GetValidIGD()
+  improved make install :)
+
+2007/12/22:
+  Adding upnperrors.c/h to provide a strupnperror() function 
+  used to translate UPnP error codes to string.
+
+2007/12/19:
+  Fixing getDevicesFromMiniSSDPD()
+  improved error reporting of UPnP functions
+
+2007/12/18:
+  It is now possible to specify a different location for MiniSSDPd socket.
+  working with MiniSSDPd is now more efficient.
+  python module improved.
+
+2007/12/16:
+  improving error reporting
+
+2007/12/13:
+  Try to improve compatibility by using HTTP/1.0 instead of 1.1 and
+  XML a bit different for SOAP.
+
+2007/11/25:
+  fixed select() call for linux
+
+2007/11/15:
+  Added -fPIC to CFLAG for better shared library code.
+
+2007/11/02:
+  Fixed a potential socket leak in miniwget2()
+
+2007/10/16:
+  added a parameter to upnpDiscover() in order to allow the use of another
+  interface than the default multicast interface.
+
+2007/10/12:
+  Fixed the creation of symbolic link in Makefile
+
+2007/10/08:
+  Added man page
+
+2007/10/02:
+  fixed memory bug in GetUPNPUrls()
+
+2007/10/01:
+  fixes in the Makefile
+  Added UPNP_GetIGDFromUrl() and adapted the sample program accordingly.
+  Added SONAME in the shared library to please debian :)
+  fixed MS Windows compilation (minissdpd is not available under MS Windows).
+
+2007/09/25:
+  small change to Makefile to be able to install in a different location
+  (default is /usr)
+
+2007/09/24:
+  now compiling both shared and static library
+
+2007/09/19:
+  Cosmetic changes on upnpc.c
+
+2007/09/02:
+  adapting to new miniSSDPd (release version ?)
+
+2007/08/31:
+  Usage of miniSSDPd to skip discovery process.
+
+2007/08/27:
+  fixed python module to allow compilation with Python older than Python 2.4
+
+2007/06/12:
+  Added a python module.
+
+2007/05/19:
+  Fixed compilation under MinGW
+
+2007/05/15:
+  fixed a memory leak in AddPortMapping()
+  Added testupnpreplyparse executable to check the parsing of
+  upnp soap messages
+  minixml now ignore namespace prefixes.
+
+2007/04/26:
+  upnpc now displays external ip address with -s or -l
+
+2007/04/11:
+  changed MINIUPNPC_URL_MAXSIZE to 128 to accomodate the "BT Voyager 210"
+
+2007/03/19:
+  cleanup in miniwget.c
+
+2007/03/01:
+  Small typo fix...
+
+2007/01/30:
+  Now parsing the HTTP header from SOAP responses in order to
+  get content-length value.
+
+2007/01/29:
+  Fixed the Soap Query to speedup the HTTP request.
+  added some Win32 DLL stuff...
+
+2007/01/27:
+  Fixed some WIN32 compatibility issues
+
+2006/12/14:
+  Added UPNPIGD_IsConnected() function in miniupnp.c/.h
+  Added UPNP_GetValidIGD() in miniupnp.c/.h
+  cleaned upnpc.c main(). now using UPNP_GetValidIGD()
+
+2006/12/07:
+  Version 1.0-RC1 released
+
+2006/12/03:
+  Minor changes to compile under SunOS/Solaris
+
+2006/11/30:
+  made a minixml parser validator program
+  updated minixml to handle attributes correctly
+
+2006/11/22:
+  Added a -r option to the upnpc sample thanks to Alexander Hubmann.
+
+2006/11/19:
+  Cleanup code to make it more ANSI C compliant
+
+2006/11/10:
+  detect and display local lan address.
+
+2006/11/04:
+  Packets and Bytes Sent/Received are now unsigned int.
+
+2006/11/01:
+  Bug fix thanks to Giuseppe D'Angelo
+
+2006/10/31:
+  C++ compatibility for .h files.
+  Added a way to get ip Address on the LAN used to reach the IGD.
+
+2006/10/25:
+  Added M-SEARCH to the services in the discovery process.
+
+2006/10/22:
+  updated the Makefile to use makedepend, added a "make install"
+  update Makefile
+
+2006/10/20:
+  fixing the description url parsing thanks to patch sent by
+  Wayne Dawe.
+  Fixed/translated some comments.
+  Implemented a better discover process, first looking
+  for IGD then for root devices (as some devices only reply to
+  M-SEARCH for root devices).
+
+2006/09/02:
+  added freeUPNPDevlist() function.
+
+2006/08/04:
+  More command line arguments checking
+
+2006/08/01:
+  Added the .bat file to compile under Win32 with minGW32
+
+2006/07/31:
+  Fixed the rootdesc parser (igd_desc_parse.c)
+
+2006/07/20:
+  parseMSEARCHReply() is now returning the ST: line as well
+  starting changes to detect several UPnP devices on the network
+
+2006/07/19:
+  using GetCommonLinkProperties to get down/upload bitrate
+

Added: trunk/server/miniupnp/LICENCE
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/LICENCE	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,26 @@
+Copyright (c) 2005-2008, Thomas BERNARD 
+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.
+    * The name of the author may not 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 THE COPYRIGHT OWNER 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.
+

Added: trunk/server/miniupnp/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/Makefile.am	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,30 @@
+noinst_LTLIBRARIES = libminiupnp.la
+
+AM_CPPFLAGS = -DNDEBUG
+
+libminiupnp_la_SOURCES = \
+    igd_desc_parse.c \
+    minisoap.c \
+    minissdpc.c \
+    miniupnpc.c \
+    miniwget.c \
+    minixml.c \
+    upnpcommands.c \
+    upnpreplyparse.c
+
+noinst_HEADERS = \
+    bsdqueue.h \
+    codelength.h \
+    declspec.h \
+    igd_desc_parse.h \
+    minisoap.h \
+    minissdpc.h \
+    miniupnpc.h \
+    miniwget.h \
+    minixml.h \
+    upnpcommands.h \
+    upnpreplyparse.h
+
+extra_DIST = \
+    README \
+    LICENSE

Added: trunk/server/miniupnp/README
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/README	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,54 @@
+Project: miniupnp
+Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+Author: Thomas Bernard
+Copyright (c) 2005-2008 Thomas Bernard
+This software is subject to the conditions detailed in the
+LICENCE file provided within this distribution.
+
+For the comfort of Win32 users, bsdqueue.h is included in the distribution.
+Its licence is included in the header of the file.
+bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system.
+
+* miniupnp Client *
+
+To compile, simply run 'gmake' (could be 'make').
+Under win32, to compile with MinGW, type "mingw32make.bat".
+The compilation is known to work under linux, FreeBSD,
+OpenBSD, MacOS X and cygwin.
+To install the library and headers on the system use :
+> su
+> make install
+> exit
+
+alternatively, to install in a specific location, use :
+> INSTALLPREFIX=/usr/local make install
+
+upnpc.c is a sample client using the libminiupnpc.
+To use the libminiupnpc in your application, link it with
+libminiupnpc.a and use the following functions found in miniupnpc.h,
+upnpcommands.h and miniwget.h :
+- upnpDiscover()
+- miniwget()
+- parserootdesc()
+- GetUPNPUrls()
+- UPNP_* (calling UPNP methods)
+
+Note : use #include <miniupnpc/miniupnpc.h> etc... for the includes
+and -lminiupnpc for the link
+
+Discovery process is speeded up when MiniSSDPd is running on the machine.
+
+* Python module *
+
+you can build a python module with 'make pythonmodule' 
+and install it with 'make installpythonmodule'.
+setup.py (and setupmingw32.py) are included in the distribution.
+
+
+Feel free to contact me if you have any problem :
+e-mail : miniupnp free fr
+
+If you are using libminiupnpc in your application, please
+send me an email !
+
+

Added: trunk/server/miniupnp/bsdqueue.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/bsdqueue.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,531 @@
+/*	$OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $	*/
+/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University 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 REGENTS 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 THE REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists, 
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#ifdef QUEUE_MACRO_DEBUG
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+ 
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#ifdef SLIST_ENTRY
+#undef SLIST_ENTRY
+#endif
+
+#define SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+ 
+/*
+ * Singly-linked List access methods.
+ */
+#define	SLIST_FIRST(head)	((head)->slh_first)
+#define	SLIST_END(head)		NULL
+#define	SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head))
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for((var) = SLIST_FIRST(head);					\
+	    (var) != SLIST_END(head);					\
+	    (var) = SLIST_NEXT(var, field))
+
+#define	SLIST_FOREACH_PREVPTR(var, varp, head, field)			\
+	for ((varp) = &SLIST_FIRST((head));				\
+	    ((var) = *(varp)) != SLIST_END(head);			\
+	    (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_INIT(head) {						\
+	SLIST_FIRST(head) = SLIST_END(head);				\
+}
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (0)
+
+#define	SLIST_REMOVE_NEXT(head, elm, field) do {			\
+	(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;	\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->slh_first;		\
+									\
+		while (curelm->field.sle_next != (elm))			\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+		_Q_INVALIDATE((elm)->field.sle_next);			\
+	}								\
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List access methods
+ */
+#define	LIST_FIRST(head)		((head)->lh_first)
+#define	LIST_END(head)			NULL
+#define	LIST_EMPTY(head)		(LIST_FIRST(head) == LIST_END(head))
+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)					\
+	for((var) = LIST_FIRST(head);					\
+	    (var)!= LIST_END(head);					\
+	    (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST(head) = LIST_END(head);				\
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev =			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+	_Q_INVALIDATE((elm)->field.le_prev);				\
+	_Q_INVALIDATE((elm)->field.le_next);				\
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {				\
+	if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)	\
+		(elm2)->field.le_next->field.le_prev =			\
+		    &(elm2)->field.le_next;				\
+	(elm2)->field.le_prev = (elm)->field.le_prev;			\
+	*(elm2)->field.le_prev = (elm2);				\
+	_Q_INVALIDATE((elm)->field.le_prev);				\
+	_Q_INVALIDATE((elm)->field.le_next);				\
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define	SIMPLEQ_FIRST(head)	    ((head)->sqh_first)
+#define	SIMPLEQ_END(head)	    NULL
+#define	SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define	SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)				\
+	for((var) = SIMPLEQ_FIRST(head);				\
+	    (var) != SIMPLEQ_END(head);					\
+	    (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define	SIMPLEQ_INIT(head) do {						\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do {			\
+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/* 
+ * tail queue access methods 
+ */
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_END(head)			NULL
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define	TAILQ_EMPTY(head)						\
+	(TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)					\
+	for((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for((var) = TAILQ_LAST(head, headname);				\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+	_Q_INVALIDATE((elm)->field.tqe_prev);				\
+	_Q_INVALIDATE((elm)->field.tqe_next);				\
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
+		(elm2)->field.tqe_next->field.tqe_prev =		\
+		    &(elm2)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm2)->field.tqe_next;		\
+	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
+	*(elm2)->field.tqe_prev = (elm2);				\
+	_Q_INVALIDATE((elm)->field.tqe_prev);				\
+	_Q_INVALIDATE((elm)->field.tqe_next);				\
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue access methods 
+ */
+#define	CIRCLEQ_FIRST(head)		((head)->cqh_first)
+#define	CIRCLEQ_LAST(head)		((head)->cqh_last)
+#define	CIRCLEQ_END(head)		((void *)(head))
+#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
+#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
+#define	CIRCLEQ_EMPTY(head)						\
+	(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field)				\
+	for((var) = CIRCLEQ_FIRST(head);				\
+	    (var) != CIRCLEQ_END(head);					\
+	    (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for((var) = CIRCLEQ_LAST(head);					\
+	    (var) != CIRCLEQ_END(head);					\
+	    (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) do {						\
+	(head)->cqh_first = CIRCLEQ_END(head);				\
+	(head)->cqh_last = CIRCLEQ_END(head);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == CIRCLEQ_END(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = CIRCLEQ_END(head);			\
+	if ((head)->cqh_last == CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = CIRCLEQ_END(head);			\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if ((elm)->field.cqe_next == CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+	_Q_INVALIDATE((elm)->field.cqe_prev);				\
+	_Q_INVALIDATE((elm)->field.cqe_next);				\
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==		\
+	    CIRCLEQ_END(head))						\
+		(head).cqh_last = (elm2);				\
+	else								\
+		(elm2)->field.cqe_next->field.cqe_prev = (elm2);	\
+	if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==		\
+	    CIRCLEQ_END(head))						\
+		(head).cqh_first = (elm2);				\
+	else								\
+		(elm2)->field.cqe_prev->field.cqe_next = (elm2);	\
+	_Q_INVALIDATE((elm)->field.cqe_prev);				\
+	_Q_INVALIDATE((elm)->field.cqe_next);				\
+} while (0)
+
+#endif	/* !_SYS_QUEUE_H_ */

Added: trunk/server/miniupnp/codelength.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/codelength.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,24 @@
+/* $Id: codelength.h,v 1.1 2008/10/06 22:04:06 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2008 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef __CODELENGTH_H__
+#define __CODELENGTH_H__
+
+/* Encode length by using 7bit per Byte :
+ * Most significant bit of each byte specifies that the
+ * following byte is part of the code */
+#define DECODELENGTH(n, p) n = 0; \
+                           do { n = (n << 7) | (*p & 0x7f); } \
+                           while(*(p++)&0x80);
+
+#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
+                         if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
+                         if(n>=16384) *(p++) = (n >> 14) | 0x80; \
+                         if(n>=128) *(p++) = (n >> 7) | 0x80; \
+                         *(p++) = n & 0x7f;
+
+#endif
+

Added: trunk/server/miniupnp/declspec.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/declspec.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,15 @@
+#ifndef __DECLSPEC_H__
+#define __DECLSPEC_H__
+
+#if defined(WIN32) && !defined(STATICLIB)
+	#ifdef MINIUPNP_EXPORTS
+		#define LIBSPEC __declspec(dllexport)
+	#else
+		#define LIBSPEC __declspec(dllimport)
+	#endif
+#else
+	#define LIBSPEC
+#endif
+
+#endif
+

Added: trunk/server/miniupnp/igd_desc_parse.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/igd_desc_parse.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,113 @@
+/* $Id: igd_desc_parse.c,v 1.8 2008/04/23 11:51:06 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2008 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include "igd_desc_parse.h"
+#include <stdio.h>
+#include <string.h>
+
+/* TODO : rewrite this code so it correctly handle descriptions with
+ * both WANIPConnection and/or WANPPPConnection */
+
+/* Start element handler :
+ * update nesting level counter and copy element name */
+void IGDstartelt(void * d, const char * name, int l)
+{
+	struct IGDdatas * datas = (struct IGDdatas *)d;
+	memcpy( datas->cureltname, name, l);
+	datas->cureltname[l] = '\0';
+	datas->level++;
+	if( (l==7) && !memcmp(name, "service", l) ) {
+		datas->controlurl_tmp[0] = '\0';
+		datas->eventsuburl_tmp[0] = '\0';
+		datas->scpdurl_tmp[0] = '\0';
+		datas->servicetype_tmp[0] = '\0';
+	}
+}
+
+/* End element handler :
+ * update nesting level counter and update parser state if
+ * service element is parsed */
+void IGDendelt(void * d, const char * name, int l)
+{
+	struct IGDdatas * datas = (struct IGDdatas *)d;
+	datas->level--;
+	/*printf("endelt %2d %.*s\n", datas->level, l, name);*/
+	if( (l==7) && !memcmp(name, "service", l) )
+	{
+		/*
+		if( datas->state < 1
+			&& !strcmp(datas->servicetype,
+				//	"urn:schemas-upnp-org:service:WANIPConnection:1") )
+				"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
+			datas->state ++;
+		*/
+		if(0==strcmp(datas->servicetype_tmp,
+				"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
+			memcpy(datas->controlurl_CIF, datas->controlurl_tmp, MINIUPNPC_URL_MAXSIZE);
+			memcpy(datas->eventsuburl_CIF, datas->eventsuburl_tmp, MINIUPNPC_URL_MAXSIZE);
+			memcpy(datas->scpdurl_CIF, datas->scpdurl_tmp, MINIUPNPC_URL_MAXSIZE);
+			memcpy(datas->servicetype_CIF, datas->servicetype_tmp, MINIUPNPC_URL_MAXSIZE);
+		} else if(0==strcmp(datas->servicetype_tmp,
+				"urn:schemas-upnp-org:service:WANIPConnection:1")
+				 || 0==strcmp(datas->servicetype_tmp,
+				"urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
+			memcpy(datas->controlurl, datas->controlurl_tmp, MINIUPNPC_URL_MAXSIZE);
+			memcpy(datas->eventsuburl, datas->eventsuburl_tmp, MINIUPNPC_URL_MAXSIZE);
+			memcpy(datas->scpdurl, datas->scpdurl_tmp, MINIUPNPC_URL_MAXSIZE);
+			memcpy(datas->servicetype, datas->servicetype_tmp, MINIUPNPC_URL_MAXSIZE);
+		}
+	}
+}
+
+/* Data handler :
+ * copy data depending on the current element name and state */
+void IGDdata(void * d, const char * data, int l)
+{
+	struct IGDdatas * datas = (struct IGDdatas *)d;
+	char * dstmember = 0;
+	/*printf("%2d %s : %.*s\n",
+           datas->level, datas->cureltname, l, data);	*/
+	if( !strcmp(datas->cureltname, "URLBase") )
+		dstmember = datas->urlbase;
+	else if( !strcmp(datas->cureltname, "serviceType") )
+		dstmember = datas->servicetype_tmp;
+	else if( !strcmp(datas->cureltname, "controlURL") )
+		dstmember = datas->controlurl_tmp;
+	else if( !strcmp(datas->cureltname, "eventSubURL") )
+		dstmember = datas->eventsuburl_tmp;
+	else if( !strcmp(datas->cureltname, "SCPDURL") )
+		dstmember = datas->scpdurl_tmp;
+/*	else if( !strcmp(datas->cureltname, "deviceType") )
+		dstmember = datas->devicetype_tmp;*/
+	if(dstmember)
+	{
+		if(l>=MINIUPNPC_URL_MAXSIZE)
+			l = MINIUPNPC_URL_MAXSIZE-1;
+		memcpy(dstmember, data, l);
+		dstmember[l] = '\0';
+	}
+}
+
+void printIGD(struct IGDdatas * d)
+{
+	printf("urlbase = %s\n", d->urlbase);
+	printf("WAN Device (Common interface config) :\n");
+	/*printf(" deviceType = %s\n", d->devicetype_CIF);*/
+	printf(" serviceType = %s\n", d->servicetype_CIF);
+	printf(" controlURL = %s\n", d->controlurl_CIF);
+	printf(" eventSubURL = %s\n", d->eventsuburl_CIF);
+	printf(" SCPDURL = %s\n", d->scpdurl_CIF);
+	printf("WAN Connection Device (IP or PPP Connection):\n");
+	/*printf(" deviceType = %s\n", d->devicetype);*/
+	printf(" servicetype = %s\n", d->servicetype);
+	printf(" controlURL = %s\n", d->controlurl);
+	printf(" eventSubURL = %s\n", d->eventsuburl);
+	printf(" SCPDURL = %s\n", d->scpdurl);
+}
+
+

Added: trunk/server/miniupnp/igd_desc_parse.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/igd_desc_parse.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,47 @@
+/* $Id: igd_desc_parse.h,v 1.6 2008/04/23 11:51:07 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2008 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __IGD_DESC_PARSE_H__
+#define __IGD_DESC_PARSE_H__
+
+/* Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices */
+#define MINIUPNPC_URL_MAXSIZE (128)
+struct IGDdatas {
+	char cureltname[MINIUPNPC_URL_MAXSIZE];
+	char urlbase[MINIUPNPC_URL_MAXSIZE];
+	int level;
+	/*int state;*/
+	/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+	char controlurl_CIF[MINIUPNPC_URL_MAXSIZE];
+	char eventsuburl_CIF[MINIUPNPC_URL_MAXSIZE];
+	char scpdurl_CIF[MINIUPNPC_URL_MAXSIZE];
+	char servicetype_CIF[MINIUPNPC_URL_MAXSIZE];
+	/*char devicetype_CIF[MINIUPNPC_URL_MAXSIZE];*/
+	/* "urn:schemas-upnp-org:service:WANIPConnection:1"
+	 * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+	char controlurl[MINIUPNPC_URL_MAXSIZE];
+	char eventsuburl[MINIUPNPC_URL_MAXSIZE];
+	char scpdurl[MINIUPNPC_URL_MAXSIZE];
+	char servicetype[MINIUPNPC_URL_MAXSIZE];
+	/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
+	/* tmp */
+	char controlurl_tmp[MINIUPNPC_URL_MAXSIZE];
+	char eventsuburl_tmp[MINIUPNPC_URL_MAXSIZE];
+	char scpdurl_tmp[MINIUPNPC_URL_MAXSIZE];
+	char servicetype_tmp[MINIUPNPC_URL_MAXSIZE];
+	/*char devicetype_tmp[MINIUPNPC_URL_MAXSIZE];*/
+};
+
+void IGDstartelt(void *, const char *, int);
+void IGDendelt(void *, const char *, int);
+void IGDdata(void *, const char *, int);
+void printIGD(struct IGDdatas *);
+
+#endif
+

Added: trunk/server/miniupnp/minisoap.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/minisoap.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,112 @@
+/* $Id: minisoap.c,v 1.15 2008/02/17 17:57:07 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ *
+ * Minimal SOAP implementation for UPnP protocol.
+ */
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#include <io.h>
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include "minisoap.h"
+
+/* only for malloc */
+#include <stdlib.h>
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+/* httpWrite sends the headers and the body to the socket
+ * and returns the number of bytes sent */
+static int
+httpWrite(int fd, const char * body, int bodysize,
+          const char * headers, int headerssize)
+{
+	int n = 0;
+	/*n = write(fd, headers, headerssize);*/
+	/*if(bodysize>0)
+		n += write(fd, body, bodysize);*/
+	/* Note : my old linksys router only took into account
+	 * soap request that are sent into only one packet */
+	char * p;
+	/* TODO: AVOID MALLOC */
+	p = malloc(headerssize+bodysize);
+	if(!p)
+	  return 0;
+	memcpy(p, headers, headerssize);
+	memcpy(p+headerssize, body, bodysize);
+	/*n = write(fd, p, headerssize+bodysize);*/
+	n = send(fd, p, headerssize+bodysize, 0);
+	if(n<0) {
+	  PRINT_SOCKET_ERROR("send");
+	}
+	/* disable send on the socket */
+	/* draytek routers dont seems to like that... */
+#if 0
+#ifdef WIN32
+	if(shutdown(fd, SD_SEND)<0) {
+#else
+	if(shutdown(fd, SHUT_WR)<0)	{ /*SD_SEND*/
+#endif
+		PRINT_SOCKET_ERROR("shutdown");
+	}
+#endif
+	free(p);
+	return n;
+}
+
+/* self explanatory  */
+int soapPostSubmit(int fd,
+                   const char * url,
+				   const char * host,
+				   unsigned short port,
+				   const char * action,
+				   const char * body)
+{
+	int bodysize;
+	char headerbuf[512];
+	int headerssize;
+	char portstr[8];
+	bodysize = (int)strlen(body);
+	/* We are not using keep-alive HTTP connections.
+	 * HTTP/1.1 needs the header Connection: close to do that.
+	 * This is the default with HTTP/1.0 */
+    /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+	portstr[0] = '\0';
+	if(port != 80)
+		snprintf(portstr, sizeof(portstr), ":%hu", port);
+	headerssize = snprintf(headerbuf, sizeof(headerbuf),
+                       "POST %s HTTP/1.1\r\n"
+/*                       "POST %s HTTP/1.0\r\n"*/
+	                   "Host: %s%s\r\n"
+					   "User-Agent: POSIX, UPnP/1.0, miniUPnPc/1.0\r\n"
+	                   "Content-Length: %d\r\n"
+					   "Content-Type: text/xml\r\n"
+					   "SOAPAction: \"%s\"\r\n"
+					   "Connection: Close\r\n"
+					   "Cache-Control: no-cache\r\n"	/* ??? */
+					   "Pragma: no-cache\r\n"
+					   "\r\n",
+					   url, host, portstr, bodysize, action);
+#ifdef DEBUG
+	printf("SOAP request : headersize=%d bodysize=%d\n",
+	       headerssize, bodysize);
+	/*printf("%s", headerbuf);*/
+#endif
+	return httpWrite(fd, body, bodysize, headerbuf, headerssize);
+}
+
+

Added: trunk/server/miniupnp/minisoap.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/minisoap.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,15 @@
+/* $Id: minisoap.h,v 1.3 2006/11/19 22:32:34 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+#ifndef __MINISOAP_H__
+#define __MINISOAP_H__
+
+/*int httpWrite(int, const char *, int, const char *);*/
+int soapPostSubmit(int, const char *, const char *, unsigned short,
+				   const char *, const char *);
+
+#endif
+

Added: trunk/server/miniupnp/minissdpc.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/minissdpc.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,103 @@
+/* $Id: minissdpc.c,v 1.6 2008/10/06 23:08:39 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2008 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+/*#include <syslog.h>*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "minissdpc.h"
+#include "miniupnpc.h"
+
+#include "codelength.h"
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath)
+{
+	struct UPNPDev * tmp;
+	struct UPNPDev * devlist = NULL;
+	unsigned char buffer[2048];
+	ssize_t n;
+	unsigned char * p;
+	unsigned char * url;
+	unsigned int i;
+	unsigned int urlsize, stsize, usnsize, l;
+	int s;
+	struct sockaddr_un addr;
+
+	s = socket(AF_UNIX, SOCK_STREAM, 0);
+	if(s < 0)
+	{
+		/*syslog(LOG_ERR, "socket(unix): %m");*/
+		perror("socket(unix)");
+		return NULL;
+	}
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
+	if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
+	{
+		/*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
+		close(s);
+		return NULL;
+	}
+	stsize = strlen(devtype);
+	buffer[0] = 1; /* request type 1 : request devices/services by type */
+	p = buffer + 1;
+	l = stsize;	CODELENGTH(l, p);
+	memcpy(p, devtype, stsize);
+	p += stsize;
+	if(write(s, buffer, p - buffer) < 0)
+	{
+		/*syslog(LOG_ERR, "write(): %m");*/
+		perror("minissdpc.c: write()");
+		close(s);
+		return NULL;
+	}
+	n = read(s, buffer, sizeof(buffer));
+	if(n<=0)
+	{
+		perror("minissdpc.c: read()");
+		close(s);
+		return NULL;
+	}
+	p = buffer + 1;
+	for(i = 0; i < buffer[0]; i++)
+	{
+		if(p+2>=buffer+sizeof(buffer))
+			break;
+		DECODELENGTH(urlsize, p);
+		if(p+urlsize+2>=buffer+sizeof(buffer))
+			break;
+		url = p;
+		p += urlsize;
+		DECODELENGTH(stsize, p);
+		if(p+stsize+2>=buffer+sizeof(buffer))
+			break;
+		tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
+		tmp->pNext = devlist;
+		tmp->descURL = tmp->buffer;
+		tmp->st = tmp->buffer + 1 + urlsize;
+		memcpy(tmp->buffer, url, urlsize);
+		tmp->buffer[urlsize] = '\0';
+		memcpy(tmp->buffer + urlsize + 1, p, stsize);
+		p += stsize;
+		tmp->buffer[urlsize+1+stsize] = '\0';
+		devlist = tmp;
+		/* added for compatibility with recent versions of MiniSSDPd 
+		 * >= 2007/12/19 */
+		DECODELENGTH(usnsize, p);
+		p += usnsize;
+		if(p>buffer + sizeof(buffer))
+			break;
+	}
+	close(s);
+	return devlist;
+}
+

Added: trunk/server/miniupnp/minissdpc.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/minissdpc.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,15 @@
+/* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2007 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __MINISSDPC_H__
+#define __MINISSDPC_H__
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath);
+
+#endif
+

Added: trunk/server/miniupnp/miniupnpc.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/miniupnpc.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,739 @@
+/* $Id: miniupnpc.c,v 1.55 2008/09/25 18:02:50 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2007 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+#include <io.h>
+#define snprintf _snprintf
+#define strncasecmp memicmp
+#define MAXHOSTNAMELEN 64
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <netdb.h>
+#define closesocket close
+#endif
+#include "miniupnpc.h"
+#include "minissdpc.h"
+#include "miniwget.h"
+#include "minisoap.h"
+#include "minixml.h"
+#include "upnpcommands.h"
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#define SOAPPREFIX "s"
+
+/* root description parsing */
+void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
+{
+	struct xmlparser parser;
+	/* xmlparser object */
+	parser.xmlstart = buffer;
+	parser.xmlsize = bufsize;
+	parser.data = data;
+	parser.starteltfunc = IGDstartelt;
+	parser.endeltfunc = IGDendelt;
+	parser.datafunc = IGDdata;
+	parser.attfunc = 0;
+	parsexml(&parser);
+#ifndef NDEBUG
+	printIGD(data);
+#endif
+}
+
+/* Content-length: nnn */
+static int getcontentlenfromline(const char * p, int n)
+{
+	static const char contlenstr[] = "content-length";
+	const char * p2 = contlenstr;
+	int a = 0;
+	while(*p2)
+	{
+		if(n==0)
+			return -1;
+		if(*p2 != *p && *p2 != (*p + 32))
+			return -1;
+		p++; p2++; n--;
+	}
+	if(n==0)
+		return -1;
+	if(*p != ':')
+		return -1;
+	p++; n--;
+	while(*p == ' ')
+	{
+		if(n==0)
+			return -1;
+		p++; n--;
+	}
+	while(*p >= '0' && *p <= '9')
+	{
+		if(n==0)
+			return -1;
+		a = (a * 10) + (*p - '0');
+		p++; n--;
+	}
+	return a;
+}
+
+static void
+getContentLengthAndHeaderLength(char * p, int n,
+                                int * contentlen, int * headerlen)
+{
+	char * line;
+	int linelen;
+	int r;
+	line = p;
+	while(line < p + n)
+	{
+		linelen = 0;
+		while(line[linelen] != '\r' && line[linelen] != '\r')
+		{
+			if(line+linelen >= p+n)
+				return;
+			linelen++;
+		}
+		r = getcontentlenfromline(line, linelen);
+		if(r>0)
+			*contentlen = r;
+		line = line + linelen + 2;
+		if(line[0] == '\r' && line[1] == '\n')
+		{
+			*headerlen = (line - p) + 2;
+			return;
+		}
+	}
+}
+
+/* simpleUPnPcommand :
+ * not so simple !
+ * return values :
+ *   0 - OK
+ *  -1 - error */
+int simpleUPnPcommand(int s, const char * url, const char * service,
+                      const char * action, struct UPNParg * args,
+                      char * buffer, int * bufsize)
+{
+	struct sockaddr_in dest;
+	char hostname[MAXHOSTNAMELEN+1];
+	unsigned short port = 0;
+	char * path;
+	char soapact[128];
+	char soapbody[2048];
+	char * buf;
+	int buffree;
+    int n;
+	int contentlen, headerlen;	/* for the response */
+	snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
+	if(args==NULL)
+	{
+		/*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
+						"<?xml version=\"1.0\"?>\r\n"
+	    	              "<" SOAPPREFIX ":Envelope "
+						  "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\"; "
+						  SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\";>"
+						  "<" SOAPPREFIX ":Body>"
+						  "<m:%s xmlns:m=\"%s\">"
+						  "</m:%s>"
+						  "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
+					 	  "\r\n", action, service, action);
+	}
+	else
+	{
+		char * p;
+		const char * pe, * pv;
+		int soapbodylen;
+		soapbodylen = snprintf(soapbody, sizeof(soapbody),
+						"<?xml version=\"1.0\"?>\r\n"
+	    	            "<" SOAPPREFIX ":Envelope "
+						"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\"; "
+						SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\";>"
+						"<" SOAPPREFIX ":Body>"
+						"<m:%s xmlns:m=\"%s\">",
+						action, service);
+		p = soapbody + soapbodylen;
+		while(args->elt)
+		{
+			/* check that we are never overflowing the string... */
+			if(soapbody + sizeof(soapbody) <= p + 100)
+			{
+				/* we keep a margin of at least 100 bytes */
+				*bufsize = 0;
+				return -1;
+			}
+			*(p++) = '<';
+			pe = args->elt;
+			while(*pe)
+				*(p++) = *(pe++);
+			*(p++) = '>';
+			if((pv = args->val))
+			{
+				while(*pv)
+					*(p++) = *(pv++);
+			}
+			*(p++) = '<';
+			*(p++) = '/';
+			pe = args->elt;
+			while(*pe)
+				*(p++) = *(pe++);
+			*(p++) = '>';
+			args++;
+		}
+		*(p++) = '<';
+		*(p++) = '/';
+		*(p++) = 'm';
+		*(p++) = ':';
+		pe = action;
+		while(*pe)
+			*(p++) = *(pe++);
+		strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
+		        soapbody + sizeof(soapbody) - p);
+	}
+	if(!parseURL(url, hostname, &port, &path)) return -1;
+	if(s<0)
+	{
+		s = socket(PF_INET, SOCK_STREAM, 0);
+		if(s<0)
+		{
+			PRINT_SOCKET_ERROR("socket");
+			*bufsize = 0;
+			return -1;
+		}
+		dest.sin_family = AF_INET;
+		dest.sin_port = htons(port);
+		dest.sin_addr.s_addr = inet_addr(hostname);
+		if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0)
+		{
+			PRINT_SOCKET_ERROR("connect");
+			closesocket(s);
+			*bufsize = 0;
+			return -1;
+		}
+	}
+
+	n = soapPostSubmit(s, path, hostname, port, soapact, soapbody);
+	if(n<=0) {
+#ifdef DEBUG
+		printf("Error sending SOAP request\n");
+#endif
+		closesocket(s);
+		return -1;
+	}
+
+	contentlen = -1;
+	headerlen = -1;
+	buf = buffer;
+	buffree = *bufsize;
+	*bufsize = 0;
+	while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) {
+		buffree -= n;
+		buf += n;
+		*bufsize += n;
+		getContentLengthAndHeaderLength(buffer, *bufsize,
+		                                &contentlen, &headerlen);
+#ifdef DEBUG
+		printf("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n",
+		       n, *bufsize, contentlen, headerlen);
+#endif
+		/* break if we received everything */
+		if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
+			break;
+	}
+	
+	closesocket(s);
+	return 0;
+}
+
+/* parseMSEARCHReply()
+ * the last 4 arguments are filled during the parsing :
+ *    - location/locationsize : "location:" field of the SSDP reply packet
+ *    - st/stsize : "st:" field of the SSDP reply packet.
+ * The strings are NOT null terminated */
+static void
+parseMSEARCHReply(const char * reply, int size,
+                  const char * * location, int * locationsize,
+			      const char * * st, int * stsize)
+{
+	int a, b, i;
+	i = 0;
+	a = i;	/* start of the line */
+	b = 0;
+	while(i<size)
+	{
+		switch(reply[i])
+		{
+		case ':':
+				if(b==0)
+				{
+					b = i; /* end of the "header" */
+					/*for(j=a; j<b; j++)
+					{
+						putchar(reply[j]);
+					}
+					*/
+				}
+				break;
+		case '\x0a':
+		case '\x0d':
+				if(b!=0)
+				{
+					/*for(j=b+1; j<i; j++)
+					{
+						putchar(reply[j]);
+					}
+					putchar('\n');*/
+					do { b++; } while(reply[b]==' ');
+					if(0==strncasecmp(reply+a, "location", 8))
+					{
+						*location = reply+b;
+						*locationsize = i-b;
+					}
+					else if(0==strncasecmp(reply+a, "st", 2))
+					{
+						*st = reply+b;
+						*stsize = i-b;
+					}
+					b = 0;
+				}
+				a = i+1;
+				break;
+		default:
+				break;
+		}
+		i++;
+	}
+}
+
+/* port upnp discover : SSDP protocol */
+#define PORT 1900
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define UPNP_MCAST_ADDR "239.255.255.250"
+
+/* upnpDiscover() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll) */
+struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
+                              const char * minissdpdsock, int sameport)
+{
+	struct UPNPDev * tmp;
+	struct UPNPDev * devlist = 0;
+	int opt = 1;
+	static const char MSearchMsgFmt[] = 
+	"M-SEARCH * HTTP/1.1\r\n"
+	"HOST: " UPNP_MCAST_ADDR ":" XSTR(PORT) "\r\n"
+	"ST: %s\r\n"
+	"MAN: \"ssdp:discover\"\r\n"
+	"MX: 3\r\n"
+	"\r\n";
+	static const char * const deviceList[] = {
+		"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+		"urn:schemas-upnp-org:service:WANIPConnection:1",
+		"urn:schemas-upnp-org:service:WANPPPConnection:1",
+		"upnp:rootdevice",
+		0
+	};
+	int deviceIndex = 0;
+	char bufr[1536];	/* reception and emission buffer */
+	int sudp;
+	int n;
+	struct sockaddr_in sockudp_r, sockudp_w;
+
+#ifndef WIN32
+	/* first try to get infos from minissdpd ! */
+	if(!minissdpdsock)
+		minissdpdsock = "/var/run/minissdpd.sock";
+	while(!devlist && deviceList[deviceIndex]) {
+		devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
+		                                  minissdpdsock);
+		/* We return what we have found if it was not only a rootdevice */
+		if(devlist && !strstr(deviceList[deviceIndex], "rootdevice"))
+			return devlist;
+		deviceIndex++;
+	}
+	deviceIndex = 0;
+#endif
+	/* fallback to direct discovery */
+#ifdef WIN32
+	sudp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else
+	sudp = socket(PF_INET, SOCK_DGRAM, 0);
+#endif
+	if(sudp < 0)
+	{
+		PRINT_SOCKET_ERROR("socket");
+		return NULL;
+	}
+    /* reception */
+    memset(&sockudp_r, 0, sizeof(struct sockaddr_in));
+    sockudp_r.sin_family = AF_INET;
+	if(sameport)
+    	sockudp_r.sin_port = htons(PORT);
+    sockudp_r.sin_addr.s_addr = INADDR_ANY;
+    /* emission */
+    memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
+    sockudp_w.sin_family = AF_INET;
+    sockudp_w.sin_port = htons(PORT);
+    sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
+
+#ifdef WIN32
+	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
+#else
+	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
+#endif
+	{
+		PRINT_SOCKET_ERROR("setsockopt");
+		return NULL;
+	}
+
+	if(multicastif)
+	{
+		struct in_addr mc_if;
+		mc_if.s_addr = inet_addr(multicastif);
+    	sockudp_r.sin_addr.s_addr = mc_if.s_addr;
+		if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+		{
+			PRINT_SOCKET_ERROR("setsockopt");
+		}
+	}
+
+	/* Avant d'envoyer le paquet on bind pour recevoir la reponse */
+    if (bind(sudp, (struct sockaddr *)&sockudp_r, sizeof(struct sockaddr_in)) != 0)
+	{
+        PRINT_SOCKET_ERROR("bind");
+		closesocket(sudp);
+		return NULL;
+    }
+
+	/* receiving SSDP response packet */
+	for(n = 0;;)
+	{
+	if(n == 0)
+	{
+		/* sending the SSDP M-SEARCH packet */
+		n = snprintf(bufr, sizeof(bufr),
+		             MSearchMsgFmt, deviceList[deviceIndex++]);
+		/*printf("Sending %s", bufr);*/
+		n = sendto(sudp, bufr, n, 0,
+		           (struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
+		if (n < 0) {
+			PRINT_SOCKET_ERROR("sendto");
+			closesocket(sudp);
+			return devlist;
+		}
+	}
+	/* Waiting for SSDP REPLY packet to M-SEARCH */
+	n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
+	if (n < 0) {
+		/* error */
+		closesocket(sudp);
+		return devlist;
+	} else if (n == 0) {
+		/* no data or Time Out */
+		if (devlist || (deviceList[deviceIndex] == 0)) {
+			/* no more device type to look for... */
+			closesocket(sudp);
+			return devlist;
+		}
+	} else {
+		const char * descURL=NULL;
+		int urlsize=0;
+		const char * st=NULL;
+		int stsize=0;
+        /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
+		parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
+		if(st&&descURL)
+		{
+			/*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
+			       stsize, st, urlsize, descURL); */
+			tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
+			tmp->pNext = devlist;
+			tmp->descURL = tmp->buffer;
+			tmp->st = tmp->buffer + 1 + urlsize;
+			memcpy(tmp->buffer, descURL, urlsize);
+			tmp->buffer[urlsize] = '\0';
+			memcpy(tmp->buffer + urlsize + 1, st, stsize);
+			tmp->buffer[urlsize+1+stsize] = '\0';
+			devlist = tmp;
+		}
+	}
+	}
+}
+
+/* freeUPNPDevlist() should be used to
+ * free the chained list returned by upnpDiscover() */
+void freeUPNPDevlist(struct UPNPDev * devlist)
+{
+	struct UPNPDev * next;
+	while(devlist)
+	{
+		next = devlist->pNext;
+		free(devlist);
+		devlist = next;
+	}
+}
+
+static void
+url_cpy_or_cat(char * dst, const char * src, int n)
+{
+	if(  (src[0] == 'h')
+	   &&(src[1] == 't')
+	   &&(src[2] == 't')
+	   &&(src[3] == 'p')
+	   &&(src[4] == ':')
+	   &&(src[5] == '/')
+	   &&(src[6] == '/'))
+	{
+		strncpy(dst, src, n);
+	}
+	else
+	{
+		int l = strlen(dst);
+		if(src[0] != '/')
+			dst[l++] = '/';
+		if(l<=n)
+			strncpy(dst + l, src, n - l);
+	}
+}
+
+/* Prepare the Urls for usage...
+ */
+void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
+                 const char * descURL)
+{
+	char * p;
+	int n1, n2, n3;
+	n1 = strlen(data->urlbase);
+	if(n1==0)
+		n1 = strlen(descURL);
+	n1 += 2;	/* 1 byte more for Null terminator, 1 byte for '/' if needed */
+	n2 = n1; n3 = n1;
+	n1 += strlen(data->scpdurl);
+	n2 += strlen(data->controlurl);
+	n3 += strlen(data->controlurl_CIF);
+
+	urls->ipcondescURL = (char *)malloc(n1);
+	urls->controlURL = (char *)malloc(n2);
+	urls->controlURL_CIF = (char *)malloc(n3);
+	/* maintenant on chope la desc du WANIPConnection */
+	if(data->urlbase[0] != '\0')
+		strncpy(urls->ipcondescURL, data->urlbase, n1);
+	else
+		strncpy(urls->ipcondescURL, descURL, n1);
+	p = strchr(urls->ipcondescURL+7, '/');
+	if(p) p[0] = '\0';
+	strncpy(urls->controlURL, urls->ipcondescURL, n2);
+	strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
+	
+	url_cpy_or_cat(urls->ipcondescURL, data->scpdurl, n1);
+
+	url_cpy_or_cat(urls->controlURL, data->controlurl, n2);
+
+	url_cpy_or_cat(urls->controlURL_CIF, data->controlurl_CIF, n3);
+
+#ifdef DEBUG
+	printf("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL,
+	       strlen(urls->ipcondescURL), n1);
+	printf("urls->controlURL='%s' %d n2=%d\n", urls->controlURL,
+	       strlen(urls->controlURL), n2);
+	printf("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF,
+	       strlen(urls->controlURL_CIF), n3);
+#endif
+}
+
+void
+FreeUPNPUrls(struct UPNPUrls * urls)
+{
+	if(!urls)
+		return;
+	free(urls->controlURL);
+	urls->controlURL = 0;
+	free(urls->ipcondescURL);
+	urls->ipcondescURL = 0;
+	free(urls->controlURL_CIF);
+	urls->controlURL_CIF = 0;
+}
+
+
+int ReceiveData(int socket, char * data, int length, int timeout)
+{
+    int n;
+#ifndef WIN32
+    struct pollfd fds[1]; /* for the poll */
+    fds[0].fd = socket;
+    fds[0].events = POLLIN;
+    n = poll(fds, 1, timeout);
+    if(n < 0)
+    {
+        PRINT_SOCKET_ERROR("poll");
+        return -1;
+    }
+    else if(n == 0)
+    {
+        return 0;
+    }
+#else
+    fd_set socketSet;
+    TIMEVAL timeval;
+    FD_ZERO(&socketSet);
+    FD_SET(socket, &socketSet);
+    timeval.tv_sec = timeout / 1000;
+    timeval.tv_usec = (timeout % 1000) * 1000;
+    /*n = select(0, &socketSet, NULL, NULL, &timeval);*/
+    n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
+    if(n < 0)
+    {
+        PRINT_SOCKET_ERROR("select");
+        return -1;
+    }
+    else if(n == 0)
+    {
+        return 0;
+    }    
+#endif
+	n = recv(socket, data, length, 0);
+	if(n<0)
+	{
+		PRINT_SOCKET_ERROR("recv");
+	}
+	return n;
+}
+
+int
+UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+	char status[64];
+	unsigned int uptime;
+	status[0] = '\0';
+	UPNP_GetStatusInfo(urls->controlURL, data->servicetype,
+	                   status, &uptime, NULL);
+	if(0 == strcmp("Connected", status))
+	{
+		return 1;
+	}
+	else
+		return 0;
+}
+
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ *     0 = NO IGD found
+ *     1 = A valid connected IGD has been found
+ *     2 = A valid IGD has been found but it reported as
+ *         not connected
+ *     3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+                 struct UPNPUrls * urls,
+				 struct IGDdatas * data,
+				 char * lanaddr, int lanaddrlen)
+{
+	char * descXML;
+	int descXMLsize = 0;
+	struct UPNPDev * dev;
+	int ndev = 0;
+	int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
+	if(!devlist)
+	{
+#ifdef DEBUG
+		printf("Empty devlist\n");
+#endif
+		return 0;
+	}
+	for(state = 1; state <= 3; state++)
+	{
+		for(dev = devlist; dev; dev = dev->pNext)
+		{
+			/* we should choose an internet gateway device.
+		 	* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
+			descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
+			   	                        lanaddr, lanaddrlen);
+			if(descXML)
+			{
+				ndev++;
+				memset(data, 0, sizeof(struct IGDdatas));
+				memset(urls, 0, sizeof(struct UPNPUrls));
+				parserootdesc(descXML, descXMLsize, data);
+				free(descXML);
+				descXML = NULL;
+				if(0==strcmp(data->servicetype_CIF,
+				   "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
+				   || state >= 3 )
+				{
+				  GetUPNPUrls(urls, data, dev->descURL);
+
+#ifdef DEBUG
+				  printf("UPNPIGD_IsConnected(%s) = %d\n",
+				     urls->controlURL,
+			         UPNPIGD_IsConnected(urls, data));
+#endif
+				  if((state >= 2) || UPNPIGD_IsConnected(urls, data))
+					return state;
+				  FreeUPNPUrls(urls);
+				}
+				memset(data, 0, sizeof(struct IGDdatas));
+			}
+#ifdef DEBUG
+			else
+			{
+				printf("error getting XML description %s\n", dev->descURL);
+			}
+#endif
+		}
+	}
+	return 0;
+}
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ *   0 - Not ok
+ *   1 - OK */
+int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+                   struct UPNPUrls * urls,
+                   struct IGDdatas * data,
+                   char * lanaddr, int lanaddrlen)
+{
+	char * descXML;
+	int descXMLsize = 0;
+	descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
+	   	                       lanaddr, lanaddrlen);
+	if(descXML) {
+		memset(data, 0, sizeof(struct IGDdatas));
+		memset(urls, 0, sizeof(struct UPNPUrls));
+		parserootdesc(descXML, descXMLsize, data);
+		free(descXML);
+		descXML = NULL;
+		GetUPNPUrls(urls, data, rootdescurl);
+		return 1;
+	} else {
+		return 0;
+	}
+}
+

Added: trunk/server/miniupnp/miniupnpc.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/miniupnpc.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,110 @@
+/* $Id: miniupnpc.h,v 1.18 2008/09/25 18:02:50 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2006 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __MINIUPNPC_H__
+#define __MINIUPNPC_H__
+
+#include "declspec.h"
+#include "igd_desc_parse.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+int simpleUPnPcommand(int, const char *, const char *,
+                      const char *, struct UPNParg *,
+                      char *, int *);
+
+struct UPNPDev {
+	struct UPNPDev * pNext;
+	char * descURL;
+	char * st;
+	char buffer[2];
+};
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If sameport is not null, SSDP packets will be sent from the source port
+ * 1900 (same as destination port) otherwise system assign a source port. */
+LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
+                                      const char * minissdpdsock, int sameport);
+/* freeUPNPDevlist()
+ * free list returned by upnpDiscover() */
+LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ */
+struct UPNPUrls {
+	char * controlURL;
+	char * ipcondescURL;
+	char * controlURL_CIF;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ *     0 = NO IGD found
+ *     1 = A valid connected IGD has been found
+ *     2 = A valid IGD has been found but it reported as
+ *         not connected
+ *     3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+                 struct UPNPUrls * urls,
+				 struct IGDdatas * data,
+				 char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ *   0 - Not ok
+ *   1 - OK */
+LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+                   struct UPNPUrls * urls,
+                   struct IGDdatas * data,
+                   char * lanaddr, int lanaddrlen);
+
+LIBSPEC void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);
+
+LIBSPEC void FreeUPNPUrls(struct UPNPUrls *);
+
+/* Reads data from the specified socket. 
+ * Returns the number of bytes read if successful, zero if no bytes were 
+ * read or if we timed out. Returns negative if there was an error. */
+int ReceiveData(int socket, char * data, int length, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

Added: trunk/server/miniupnp/miniwget.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/miniwget.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,219 @@
+/* $Id: miniwget.c,v 1.19 2007/11/02 14:16:19 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "miniupnpc.h"
+#ifdef WIN32
+#include <winsock2.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#define snprintf _snprintf
+#define herror
+#define socklen_t int
+#else
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define closesocket close
+#endif
+/* for MIN() macro : */
+#if defined(__sun) || defined(sun)
+#include <utility.h>
+#endif
+
+/* miniwget2() :
+ * */
+static void *
+miniwget2(const char * url, const char * host,
+		  unsigned short port, const char * path,
+		  int * size, char * addr_str, int addr_str_len)
+{
+	char buf[2048];
+    int s;
+	struct sockaddr_in dest;
+	struct hostent *hp;
+	*size = 0;
+	hp = gethostbyname(host);
+	if(hp==NULL)
+	{
+		herror(host);
+		return NULL;
+	}
+	/*  memcpy((char *)&dest.sin_addr, hp->h_addr, hp->h_length);  */
+	memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
+	memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
+	s = socket(PF_INET, SOCK_STREAM, 0);
+	if(s < 0)
+	{
+		perror("socket");
+		return NULL;
+	}
+	dest.sin_family = AF_INET;
+	dest.sin_port = htons(port);
+	if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in))<0)
+	{
+		perror("connect");
+		closesocket(s);
+		return NULL;
+	}
+
+	/* get address for caller ! */
+	if(addr_str)
+	{
+		struct sockaddr_in saddr;
+		socklen_t len;
+
+		len = sizeof(saddr);
+		getsockname(s, (struct sockaddr *)&saddr, &len);
+#ifndef WIN32
+		inet_ntop(AF_INET, &saddr.sin_addr, addr_str, addr_str_len);
+#else
+	/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
+     * But his function make a string with the port :  nn.nn.nn.nn:port */
+/*		if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
+                            NULL, addr_str, (DWORD *)&addr_str_len))
+		{
+		    printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
+		}*/
+		strncpy(addr_str, inet_ntoa(saddr.sin_addr), addr_str_len);
+#endif
+#ifdef DEBUG
+		printf("address miniwget : %s\n", addr_str);
+#endif
+	}
+
+	snprintf(buf, sizeof(buf),
+                 "GET %s HTTP/1.1\r\n"
+			     "Host: %s:%d\r\n"
+				 "Connection: Close\r\n"
+				 "\r\n",
+		    path, host, port);
+	/*write(s, buf, strlen(buf));*/
+	send(s, buf, strlen(buf), 0);
+	{
+		int n, headers=1;
+		char * respbuffer = NULL;
+		int allreadyread = 0;
+		/*while((n = recv(s, buf, 2048, 0)) > 0)*/
+		while((n = ReceiveData(s, buf, 2048, 5000)) > 0)
+		{
+			if(headers)
+			{
+				int i=0;
+				while(i<n-3)
+				{
+					if(buf[i]=='\r' && buf[i+1]=='\n'
+					   && buf[i+2]=='\r' && buf[i+3]=='\n')
+					{
+						headers = 0;	/* end */
+						if(i<n-4)
+						{
+							respbuffer = (char *)realloc((void *)respbuffer, 
+														 allreadyread+(n-i-4));
+							memcpy(respbuffer+allreadyread, buf + i + 4, n-i-4);
+							allreadyread += (n-i-4);
+						}
+						break;
+					}
+					i++;
+				}
+			}
+			else
+			{
+				respbuffer = (char *)realloc((void *)respbuffer, 
+								 allreadyread+n);
+				memcpy(respbuffer+allreadyread, buf, n);
+				allreadyread += n;
+			}
+		}
+		*size = allreadyread;
+#ifndef NDEBUG
+		printf("%d bytes read\n", *size);
+#endif
+		closesocket(s);
+		return respbuffer;
+	}
+}
+
+/* parseURL()
+ * arguments :
+ *   url :		source string not modified
+ *   hostname :	hostname destination string (size of MAXHOSTNAMELEN+1)
+ *   port :		port (destination)
+ *   path :		pointer to the path part of the URL 
+ *
+ * Return values :
+ *    0 - Failure
+ *    1 - Success         */
+int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
+{
+	char * p1, *p2, *p3;
+	p1 = strstr(url, "://");
+	if(!p1)
+		return 0;
+	p1 += 3;
+	if(  (url[0]!='h') || (url[1]!='t')
+	   ||(url[2]!='t') || (url[3]!='p'))
+		return 0;
+	p2 = strchr(p1, ':');
+	p3 = strchr(p1, '/');
+	if(!p3)
+		return 0;
+	memset(hostname, 0, MAXHOSTNAMELEN + 1);
+	if(!p2 || (p2>p3))
+	{
+		strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
+		*port = 80;
+	}
+	else
+	{
+		strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+		*port = 0;
+		p2++;
+		while( (*p2 >= '0') && (*p2 <= '9'))
+		{
+			*port *= 10;
+			*port += (unsigned short)(*p2 - '0');
+			p2++;
+		}
+	}
+	*path = p3;
+	return 1;
+}
+
+void * miniwget(const char * url, int * size)
+{
+	unsigned short port;
+	char * path;
+	/* protocol://host:port/chemin */
+	char hostname[MAXHOSTNAMELEN+1];
+	*size = 0;
+	if(!parseURL(url, hostname, &port, &path))
+		return NULL;
+	return miniwget2(url, hostname, port, path, size, 0, 0);
+}
+
+void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen)
+{
+	unsigned short port;
+	char * path;
+	/* protocol://host:port/chemin */
+	char hostname[MAXHOSTNAMELEN+1];
+	*size = 0;
+	if(addr)
+		addr[0] = '\0';
+	if(!parseURL(url, hostname, &port, &path))
+		return NULL;
+	return miniwget2(url, hostname, port, path, size, addr, addrlen);
+}
+

Added: trunk/server/miniupnp/miniwget.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/miniwget.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,28 @@
+/* $Id: miniwget.h,v 1.5 2007/01/29 20:27:23 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __MINIWGET_H__
+#define __MINIWGET_H__
+
+#include "declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBSPEC void * miniwget(const char *, int *);
+
+LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int);
+
+int parseURL(const char *, char *, unsigned short *, char * *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

Added: trunk/server/miniupnp/minixml.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/minixml.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,191 @@
+/* $Id: minixml.c,v 1.6 2007/05/15 18:14:08 nanard Exp $ */
+/* minixml.c : the minimum size a xml parser can be ! */
+/* Project : miniupnp
+ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+
+Copyright (c) 2005-2007, Thomas BERNARD 
+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.
+    * The name of the author may not 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 THE COPYRIGHT OWNER 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 "minixml.h"
+
+/* parseatt : used to parse the argument list
+ * return 0 (false) in case of success and -1 (true) if the end
+ * of the xmlbuffer is reached. */
+int parseatt(struct xmlparser * p)
+{
+	const char * attname;
+	int attnamelen;
+	const char * attvalue;
+	int attvaluelen;
+	while(p->xml < p->xmlend)
+	{
+		if(*p->xml=='/' || *p->xml=='>')
+			return 0;
+		if( !IS_WHITE_SPACE(*p->xml) )
+		{
+			char sep;
+			attname = p->xml;
+			attnamelen = 0;
+			while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
+			{
+				attnamelen++; p->xml++;
+				if(p->xml >= p->xmlend)
+					return -1;
+			}
+			while(*(p->xml++) != '=')
+			{
+				if(p->xml >= p->xmlend)
+					return -1;
+			}
+			while(IS_WHITE_SPACE(*p->xml))
+			{
+				p->xml++;
+				if(p->xml >= p->xmlend)
+					return -1;
+			}
+			sep = *p->xml;
+			if(sep=='\'' || sep=='\"')
+			{
+				p->xml++;
+				if(p->xml >= p->xmlend)
+					return -1;
+				attvalue = p->xml;
+				attvaluelen = 0;
+				while(*p->xml != sep)
+				{
+					attvaluelen++; p->xml++;
+					if(p->xml >= p->xmlend)
+						return -1;
+				}
+			}
+			else
+			{
+				attvalue = p->xml;
+				attvaluelen = 0;
+				while(   !IS_WHITE_SPACE(*p->xml)
+					  && *p->xml != '>' && *p->xml != '/')
+				{
+					attvaluelen++; p->xml++;
+					if(p->xml >= p->xmlend)
+						return -1;
+				}
+			}
+			/*printf("%.*s='%.*s'\n",
+			       attnamelen, attname, attvaluelen, attvalue);*/
+			if(p->attfunc)
+				p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
+		}
+		p->xml++;
+	}
+	return -1;
+}
+
+/* parseelt parse the xml stream and
+ * call the callback functions when needed... */
+void parseelt(struct xmlparser * p)
+{
+	int i;
+	const char * elementname;
+	while(p->xml < (p->xmlend - 1))
+	{
+		if((p->xml)[0]=='<' && (p->xml)[1]!='?')
+		{
+			i = 0; elementname = ++p->xml;
+			while( !IS_WHITE_SPACE(*p->xml)
+				  && (*p->xml!='>') && (*p->xml!='/')
+				 )
+			{
+				i++; p->xml++;
+				if (p->xml >= p->xmlend)
+					return;
+				/* to ignore namespace : */
+				if(*p->xml==':')
+				{
+					i = 0;
+					elementname = ++p->xml;
+				}
+			}
+			if(i>0)
+			{
+				if(p->starteltfunc)
+					p->starteltfunc(p->data, elementname, i);
+				if(parseatt(p))
+					return;
+				if(*p->xml!='/')
+				{
+					const char * data;
+					i = 0; data = ++p->xml;
+					if (p->xml >= p->xmlend)
+						return;
+					while( IS_WHITE_SPACE(*p->xml) )
+					{
+						p->xml++;
+						if (p->xml >= p->xmlend)
+							return;
+					}
+					while(*p->xml!='<')
+					{
+						i++; p->xml++;
+						if (p->xml >= p->xmlend)
+							return;
+					}
+					if(i>0 && p->datafunc)
+						p->datafunc(p->data, data, i);
+				}
+			}
+			else if(*p->xml == '/')
+			{
+				i = 0; elementname = ++p->xml;
+				if (p->xml >= p->xmlend)
+					return;
+				while((*p->xml != '>'))
+				{
+					i++; p->xml++;
+					if (p->xml >= p->xmlend)
+						return;
+				}
+				if(p->endeltfunc)
+					p->endeltfunc(p->data, elementname, i);
+				p->xml++;
+			}
+		}
+		else
+		{
+			p->xml++;
+		}
+	}
+}
+
+/* the parser must be initialized before calling this function */
+void parsexml(struct xmlparser * parser)
+{
+	parser->xml = parser->xmlstart;
+	parser->xmlend = parser->xmlstart + parser->xmlsize;
+	parseelt(parser);
+}
+
+

Added: trunk/server/miniupnp/minixml.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/minixml.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,37 @@
+/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
+/* minimal xml parser
+ *
+ * Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __MINIXML_H__
+#define __MINIXML_H__
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/* if a callback function pointer is set to NULL,
+ * the function is not called */
+struct xmlparser {
+	const char *xmlstart;
+	const char *xmlend;
+	const char *xml;	/* pointer to current character */
+	int xmlsize;
+	void * data;
+	void (*starteltfunc) (void *, const char *, int);
+	void (*endeltfunc) (void *, const char *, int);
+	void (*datafunc) (void *, const char *, int);
+	void (*attfunc) (void *, const char *, int, const char *, int);
+};
+
+/* parsexml()
+ * the xmlparser structure must be initialized before the call
+ * the following structure members have to be initialized :
+ * xmlstart, xmlsize, data, *func
+ * xml is for internal usage, xmlend is computed automatically */
+void parsexml(struct xmlparser *);
+
+#endif
+

Added: trunk/server/miniupnp/upnpcommands.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/upnpcommands.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,569 @@
+/* $Id: upnpcommands.c,v 1.19 2008/02/18 13:27:23 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+
+static unsigned int
+my_atoui(const char * s)
+{
+	return s ? ((unsigned int)strtoul(s, NULL, 0)) : 0;
+}
+
+/*
+ * */
+unsigned int
+UPNP_GetTotalBytesSent(const char * controlURL,
+					const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	unsigned int r = 0;
+	char * p;
+	simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesSent", 0, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/*
+ * */
+unsigned int
+UPNP_GetTotalBytesReceived(const char * controlURL,
+						const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	unsigned int r = 0;
+	char * p;
+	simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesReceived", 0, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/*
+ * */
+unsigned int
+UPNP_GetTotalPacketsSent(const char * controlURL,
+						const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	unsigned int r = 0;
+	char * p;
+	simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsSent", 0, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/*
+ * */
+unsigned int
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+						const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	unsigned int r = 0;
+	char * p;
+	simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsReceived", 0, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/* UPNP_GetStatusInfo() call the corresponding UPNP method
+ * returns the current status and uptime */
+int UPNP_GetStatusInfo(const char * controlURL,
+					const char * servicetype,
+					char * status, 
+					unsigned int * uptime,
+					char * lastconnerror)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	char * p;
+	char * up;
+	char * err;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!status && !uptime)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	simpleUPnPcommand(-1, controlURL, servicetype, "GetStatusInfo", 0, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	up = GetValueFromNameValueList(&pdata, "NewUptime");
+	p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
+	err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
+	if(p && up)
+	  ret = UPNPCOMMAND_SUCCESS;
+
+	if(status) {
+		if(p){
+			strncpy(status, p, 64 );
+			status[63] = '\0';
+		}else
+			status[0]= '\0';
+	}
+
+	if(uptime) {
+		if(up)
+			sscanf(up,"%u",uptime);
+		else
+			uptime = 0;
+	}
+
+	if(lastconnerror) {
+		if(err) {
+			strncpy(lastconnerror, err, 64 );
+			lastconnerror[63] = '\0';
+		} else
+			lastconnerror[0] = '\0';
+	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
+ * returns the connection type */
+int UPNP_GetConnectionTypeInfo(const char * controlURL,
+                               const char * servicetype,
+                               char * connectionType)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!connectionType)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	simpleUPnPcommand(-1, controlURL, servicetype,
+	                  "GetConnectionTypeInfo", 0, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	p = GetValueFromNameValueList(&pdata, "NewConnectionType");
+	/*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
+	/* PossibleConnectionTypes will have several values.... */
+	if(p) {
+		strncpy(connectionType, p, 64 );
+		connectionType[63] = '\0';
+		ret = UPNPCOMMAND_SUCCESS;
+	} else
+		connectionType[0] = '\0';
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
+ * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
+ * One of the values can be null 
+ * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only 
+ * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
+int UPNP_GetLinkLayerMaxBitRates(const char * controlURL, const char * servicetype, unsigned int * bitrateDown, unsigned int* bitrateUp)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+	char * down;
+	char * up;
+	char * p;
+
+	if(!bitrateDown && !bitrateUp)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	/* shouldn't we use GetCommonLinkProperties ? */
+	simpleUPnPcommand(-1, controlURL, servicetype,
+	                  "GetCommonLinkProperties", 0, buffer, &bufsize);
+	                  /*"GetLinkLayerMaxBitRates", 0, buffer, &bufsize);*/
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
+	/*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
+	down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
+	up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
+	/*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
+	/*GetValueFromNameValueList(&pdata, "NewPhysicalLinkSatus");*/
+	if(down && up)
+		ret = UPNPCOMMAND_SUCCESS;
+
+	if(bitrateDown)
+	{
+		if(down)
+			sscanf(down,"%u",bitrateDown);
+		else
+			*bitrateDown = 0;
+	}
+
+	if(bitrateUp)
+	{
+		if(up)
+			sscanf(up,"%u",bitrateUp);
+		else
+			*bitrateUp = 0;
+	}
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ * 
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+int UPNP_GetExternalIPAddress(const char * controlURL,
+                              const char * servicetype,
+							  char * extIpAdd)
+{
+	struct NameValueParserData pdata;
+	char buffer[4096];
+	int bufsize = 4096;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!extIpAdd || !controlURL || !servicetype)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	simpleUPnPcommand(-1, controlURL, servicetype, "GetExternalIPAddress", 0, buffer, &bufsize);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
+	p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
+	if(p) {
+		strncpy(extIpAdd, p, 16 );
+		extIpAdd[15] = '\0';
+		ret = UPNPCOMMAND_SUCCESS;
+	} else
+		extIpAdd[0] = '\0';
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+                    const char * extPort,
+					const char * inPort,
+					const char * inClient,
+					const char * desc,
+					const char * proto)
+{
+	struct UPNParg * AddPortMappingArgs;
+	char buffer[4096];
+	int bufsize = 4096;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	int ret;
+
+	if(!inPort || !inClient || !proto || !extPort)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+	AddPortMappingArgs[0].elt = "NewRemoteHost";
+	AddPortMappingArgs[1].elt = "NewExternalPort";
+	AddPortMappingArgs[1].val = extPort;
+	AddPortMappingArgs[2].elt = "NewProtocol";
+	AddPortMappingArgs[2].val = proto;
+	AddPortMappingArgs[3].elt = "NewInternalPort";
+	AddPortMappingArgs[3].val = inPort;
+	AddPortMappingArgs[4].elt = "NewInternalClient";
+	AddPortMappingArgs[4].val = inClient;
+	AddPortMappingArgs[5].elt = "NewEnabled";
+	AddPortMappingArgs[5].val = "1";
+	AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+	AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+	AddPortMappingArgs[7].elt = "NewLeaseDuration";
+	AddPortMappingArgs[7].val = "0";
+	simpleUPnPcommand(-1, controlURL, servicetype, "AddPortMapping", AddPortMappingArgs, buffer, &bufsize);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	/*buffer[bufsize] = '\0';*/
+	/*puts(buffer);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal) {
+		/*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	} else {
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(AddPortMappingArgs);
+	return ret;
+}
+
+int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+                       const char * extPort, const char * proto)
+{
+	/*struct NameValueParserData pdata;*/
+	struct UPNParg * DeletePortMappingArgs;
+	char buffer[4096];
+	int bufsize = 4096;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	int ret;
+
+	if(!extPort || !proto)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
+	DeletePortMappingArgs[0].elt = "NewRemoteHost";
+	DeletePortMappingArgs[1].elt = "NewExternalPort";
+	DeletePortMappingArgs[1].val = extPort;
+	DeletePortMappingArgs[2].elt = "NewProtocol";
+	DeletePortMappingArgs[2].val = proto;
+	simpleUPnPcommand(-1, controlURL, servicetype,
+	                  "DeletePortMapping",
+					  DeletePortMappingArgs, buffer, &bufsize);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	} else {
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(DeletePortMappingArgs);
+	return ret;
+}
+
+int UPNP_GetGenericPortMappingEntry(const char * controlURL,
+                                     const char * servicetype,
+									 const char * index,
+									 char * extPort,
+									 char * intClient,
+									 char * intPort,
+									 char * protocol,
+									 char * desc,
+									 char * enabled,
+									 char * rHost,
+									 char * duration)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * GetPortMappingArgs;
+	char buffer[4096];
+	int bufsize = 4096;
+	char * p;
+	int r = UPNPCOMMAND_UNKNOWN_ERROR;
+	if(!index)
+		return UPNPCOMMAND_INVALID_ARGS;
+	intClient[0] = '\0';
+	intPort[0] = '\0';
+	GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
+	GetPortMappingArgs[0].elt = "NewPortMappingIndex";
+	GetPortMappingArgs[0].val = index;
+	simpleUPnPcommand(-1, controlURL, servicetype,
+	                  "GetGenericPortMappingEntry",
+					  GetPortMappingArgs, buffer, &bufsize);
+	ParseNameValue(buffer, bufsize, &pdata);
+	p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
+	if(p && rHost)
+	{
+		strncpy(rHost, p, 64);
+		rHost[63] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewExternalPort");
+	if(p && extPort)
+	{
+		strncpy(extPort, p, 6);
+		extPort[5] = '\0';
+		r = UPNPCOMMAND_SUCCESS;
+	}
+	p = GetValueFromNameValueList(&pdata, "NewProtocol");
+	if(p && protocol)
+	{
+		strncpy(protocol, p, 4);
+		protocol[3] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+	if(p && intClient)
+	{
+		strncpy(intClient, p, 16);
+		intClient[15] = '\0';
+		r = 0;
+	}
+	p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+	if(p && intPort)
+	{
+		strncpy(intPort, p, 6);
+		intPort[5] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewEnabled");
+	if(p && enabled)
+	{
+		strncpy(enabled, p, 4);
+		enabled[3] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+	if(p && desc)
+	{
+		strncpy(desc, p, 80);
+		desc[79] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+	if(p && duration)
+	{
+		strncpy(duration, p, 16);
+		duration[15] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		r = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &r);
+	}
+	ClearNameValueList(&pdata);
+	free(GetPortMappingArgs);
+	return r;
+}
+
+int UPNP_GetPortMappingNumberOfEntries(const char * controlURL, const char * servicetype, unsigned int * numEntries)
+{
+ 	struct NameValueParserData pdata;
+ 	char buffer[4096];
+ 	int bufsize = 4096;
+ 	char* p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ 	simpleUPnPcommand(-1, controlURL, servicetype, "GetPortMappingNumberOfEntries", 0, buffer, &bufsize);
+#ifndef NDEBUG
+	DisplayNameValueList(buffer, bufsize);
+#endif
+ 	ParseNameValue(buffer, bufsize, &pdata);
+
+ 	p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
+ 	if(numEntries && p) {
+		*numEntries = 0;
+ 		sscanf(p, "%u", numEntries);
+		ret = UPNPCOMMAND_SUCCESS;
+ 	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+ 	ClearNameValueList(&pdata);
+	return ret;
+}
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data */
+int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+                                 const char * servicetype,
+                                 const char * extPort,
+							     const char * proto,
+                                 char * intClient,
+                                 char * intPort)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * GetPortMappingArgs;
+	char buffer[4096];
+	int bufsize = 4096;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!intPort || !intClient || !extPort || !proto)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
+	GetPortMappingArgs[0].elt = "NewRemoteHost";
+	GetPortMappingArgs[1].elt = "NewExternalPort";
+	GetPortMappingArgs[1].val = extPort;
+	GetPortMappingArgs[2].elt = "NewProtocol";
+	GetPortMappingArgs[2].val = proto;
+	simpleUPnPcommand(-1, controlURL, servicetype,
+	                  "GetSpecificPortMappingEntry",
+					  GetPortMappingArgs, buffer, &bufsize);
+	/*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "GetSpecificPortMappingEntry", AddPortMappingArgs, buffer, &bufsize); */
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+
+	p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+	if(p) {
+		strncpy(intClient, p, 16);
+		intClient[15] = '\0';
+		ret = UPNPCOMMAND_SUCCESS;
+	} else
+		intClient[0] = '\0';
+
+	p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+	if(p) {
+		strncpy(intPort, p, 6);
+		intPort[5] = '\0';
+	} else
+		intPort[0] = '\0';
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+	ClearNameValueList(&pdata);
+	free(GetPortMappingArgs);
+	return ret;
+}
+
+

Added: trunk/server/miniupnp/upnpcommands.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/upnpcommands.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,179 @@
+/* $Id: upnpcommands.h,v 1.14 2008/09/25 18:02:50 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2008 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef __UPNPCOMMANDS_H__
+#define __UPNPCOMMANDS_H__
+
+#include "upnpreplyparse.h"
+#include "declspec.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBSPEC unsigned int
+UPNP_GetTotalBytesSent(const char * controlURL,
+					const char * servicetype);
+
+LIBSPEC unsigned int
+UPNP_GetTotalBytesReceived(const char * controlURL,
+						const char * servicetype);
+
+LIBSPEC unsigned int
+UPNP_GetTotalPacketsSent(const char * controlURL,
+					const char * servicetype);
+
+LIBSPEC unsigned int
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+					const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+			       const char * servicetype,
+				   char * status,
+				   unsigned int * uptime,
+                   char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+                           const char * servicetype,
+						   char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available 
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ * 
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+                          const char * servicetype,
+                          char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+							const char* servicetype,
+							unsigned int * bitrateDown,
+							unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ * 
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ *                                   wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ *                     with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ *                              must be the same 
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ *                  permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ *                             and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ *                                        cannot be a specific port value */
+LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+                    const char * extPort,
+				    const char * inPort,
+					const char * inClient,
+					const char * desc,
+                    const char * proto);
+
+/* UPNP_DeletePortMapping()
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+                       const char * extPort, const char * proto);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL, const char* servicetype, unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+                                 const char * servicetype,
+                                 const char * extPort,
+                                 const char * proto,
+                                 char * intClient,
+                                 char * intPort);
+
+/* UPNP_GetGenericPortMappingEntry()
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+                                const char * servicetype,
+								const char * index,
+								char * extPort,
+								char * intClient,
+								char * intPort,
+								char * protocol,
+								char * desc,
+								char * enabled,
+								char * rHost,
+								char * duration);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

Added: trunk/server/miniupnp/upnperrors.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/upnperrors.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,66 @@
+/* $Id: upnperrors.c,v 1.3 2008/04/27 17:21:51 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2007 Thomas Bernard
+ * All Right reserved.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <string.h>
+#include "upnperrors.h"
+#include "upnpcommands.h"
+
+const char * strupnperror(int err)
+{
+	const char * s = NULL;
+	switch(err) {
+	case UPNPCOMMAND_SUCCESS:
+		s = "Success";
+		break;
+	case UPNPCOMMAND_UNKNOWN_ERROR:
+		s = "Miniupnpc Unknown Error";
+		break;
+	case UPNPCOMMAND_INVALID_ARGS:
+		s = "Miniupnpc Invalid Arguments";
+		break;
+	case 401:
+		s = "Invalid Action";
+		break;
+	case 402:
+		s = "Invalid Args";
+		break;
+	case 501:
+		s = "Action Failed";
+		break;
+	case 713:
+		s = "SpecifiedArrayIndexInvalid";
+		break;
+	case 714:
+		s = "NoSuchEntryInArray";
+		break;
+	case 715:
+		s = "WildCardNotPermittedInSrcIP";
+		break;
+	case 716:
+		s = "WildCardNotPermittedInExtPort";
+		break;
+	case 718:
+		s = "ConflictInMappingEntry";
+		break;
+	case 724:
+		s = "SamePortValuesRequired";
+		break;
+	case 725:
+		s = "OnlyPermanentLeasesSupported";
+		break;
+	case 726:
+		s = "RemoteHostOnlySupportsWildcard";
+		break;
+	case 727:
+		s = "ExternalPortOnlySupportsWildcard";
+		break;
+	default:
+		s = NULL;
+	}
+	return s;
+}

Added: trunk/server/miniupnp/upnperrors.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/upnperrors.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,26 @@
+/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
+/* (c) 2007 Thomas Bernard
+ * All rights reserved.
+ * MiniUPnP Project.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef __UPNPERRORS_H__
+#define __UPNPERRORS_H__
+
+#include "declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strupnperror()
+ * Return a string description of the UPnP error code 
+ * or NULL for undefinded errors */
+LIBSPEC const char * strupnperror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

Added: trunk/server/miniupnp/upnpreplyparse.c
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/upnpreplyparse.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,127 @@
+/* $Id: upnpreplyparse.c,v 1.10 2008/02/21 13:05:27 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006 Thomas Bernard 
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "upnpreplyparse.h"
+#include "minixml.h"
+
+static void
+NameValueParserStartElt(void * d, const char * name, int l)
+{
+    struct NameValueParserData * data = (struct NameValueParserData *)d;
+    if(l>63)
+        l = 63;
+    memcpy(data->curelt, name, l);
+    data->curelt[l] = '\0';
+}
+
+static void
+NameValueParserGetData(void * d, const char * datas, int l)
+{
+    struct NameValueParserData * data = (struct NameValueParserData *)d;
+    struct NameValue * nv;
+    nv = malloc(sizeof(struct NameValue));
+    if(l>63)
+        l = 63;
+    strncpy(nv->name, data->curelt, 64);
+	nv->name[63] = '\0';
+    memcpy(nv->value, datas, l);
+    nv->value[l] = '\0';
+    LIST_INSERT_HEAD( &(data->head), nv, entries);
+}
+
+void
+ParseNameValue(const char * buffer, int bufsize,
+                    struct NameValueParserData * data)
+{
+    struct xmlparser parser;
+    LIST_INIT(&(data->head));
+    /* init xmlparser object */
+    parser.xmlstart = buffer;
+    parser.xmlsize = bufsize;
+    parser.data = data;
+    parser.starteltfunc = NameValueParserStartElt;
+    parser.endeltfunc = 0;
+    parser.datafunc = NameValueParserGetData;
+	parser.attfunc = 0;
+    parsexml(&parser);
+}
+
+void
+ClearNameValueList(struct NameValueParserData * pdata)
+{
+    struct NameValue * nv;
+    while((nv = pdata->head.lh_first) != NULL)
+    {
+        LIST_REMOVE(nv, entries);
+        free(nv);
+    }
+}
+
+char * 
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+                          const char * Name)
+{
+    struct NameValue * nv;
+    char * p = NULL;
+    for(nv = pdata->head.lh_first;
+        (nv != NULL) && (p == NULL);
+        nv = nv->entries.le_next)
+    {
+        if(strcmp(nv->name, Name) == 0)
+            p = nv->value;
+    }
+    return p;
+}
+
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+                                  const char * Name)
+{
+	struct NameValue * nv;
+	char * p = NULL;
+	char * pname;
+	for(nv = pdata->head.lh_first;
+	    (nv != NULL) && (p == NULL);
+		nv = nv->entries.le_next)
+	{
+		pname = strrchr(nv->name, ':');
+		if(pname)
+			pname++;
+		else
+			pname = nv->name;
+		if(strcmp(pname, Name)==0)
+			p = nv->value;
+	}
+	return p;
+}
+#endif
+
+/* debug all-in-one function 
+ * do parsing then display to stdout */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize)
+{
+    struct NameValueParserData pdata;
+    struct NameValue * nv;
+    ParseNameValue(buffer, bufsize, &pdata);
+    for(nv = pdata.head.lh_first;
+        nv != NULL;
+        nv = nv->entries.le_next)
+    {
+        printf("%s = %s\n", nv->name, nv->value);
+    }
+    ClearNameValueList(&pdata);
+}
+#endif
+

Added: trunk/server/miniupnp/upnpreplyparse.h
==============================================================================
--- (empty file)
+++ trunk/server/miniupnp/upnpreplyparse.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,62 @@
+/* $Id: upnpreplyparse.h,v 1.8 2008/02/21 13:05:27 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006 Thomas Bernard 
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef __UPNPREPLYPARSE_H__
+#define __UPNPREPLYPARSE_H__
+
+#if defined(NO_SYS_QUEUE_H) || defined(WIN32)
+#include "bsdqueue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NameValue {
+    LIST_ENTRY(NameValue) entries;
+    char name[64];
+    char value[64];
+};
+
+struct NameValueParserData {
+    LIST_HEAD(listhead, NameValue) head;
+    char curelt[64];
+};
+
+/* ParseNameValue() */
+void
+ParseNameValue(const char * buffer, int bufsize,
+               struct NameValueParserData * data);
+
+/* ClearNameValueList() */
+void
+ClearNameValueList(struct NameValueParserData * pdata);
+
+/* GetValueFromNameValueList() */
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+                          const char * Name);
+
+/* GetValueFromNameValueListIgnoreNS() */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+                                  const char * Name);
+
+/* DisplayNameValueList() */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

Modified: trunk/server/vino-prefs.c
==============================================================================
--- trunk/server/vino-prefs.c	(original)
+++ trunk/server/vino-prefs.c	Wed Dec 17 17:01:10 2008
@@ -26,6 +26,7 @@
 #include <glib/gstdio.h>
 #include <signal.h>
 #include <fcntl.h>
+#include <stdlib.h>
 
 #include "vino-prefs.h"
 #include "vino-util.h"
@@ -47,8 +48,9 @@
 #define VINO_PREFS_LOCK_SCREEN            VINO_PREFS_DIR "/lock_screen_on_disconnect"
 #define VINO_PREFS_ICON_VISIBILITY        VINO_PREFS_DIR "/icon_visibility"
 #define VINO_PREFS_DISABLE_BACKGROUND     VINO_PREFS_DIR "/disable_background"
+#define VINO_PREFS_USE_UPNP               VINO_PREFS_DIR "/use_upnp"
 
-#define VINO_N_LISTENERS                  12
+#define VINO_N_LISTENERS                  13
 
 #define VINO_PREFS_LOCKFILE               "vino-server.lock"
 
@@ -67,6 +69,7 @@
 static int             vino_alternative_port     = VINO_SERVER_DEFAULT_PORT;
 static gboolean        vino_lock_screen          = FALSE;
 static gboolean        vino_disable_background   = FALSE;
+static gboolean        vino_use_upnp             = TRUE;
 static VinoStatusIconVisibility vino_icon_visibility = VINO_STATUS_ICON_VISIBILITY_CLIENT;
 
 static void
@@ -440,6 +443,30 @@
     vino_server_set_disable_background (l->data, disable_background);
 }
 
+static void
+vino_prefs_use_upnp_changed (GConfClient *client,
+                             guint       cnxn_id,
+                             GConfEntry  *entry)
+{
+  gboolean  use_upnp;
+  GSList   *l;
+
+  if (!entry->value || entry->value->type != GCONF_VALUE_BOOL)
+    return;
+
+  use_upnp = gconf_value_get_bool (entry->value) != FALSE;
+
+  if (vino_use_upnp == use_upnp)
+    return;
+
+  vino_use_upnp = use_upnp;
+
+  dprintf (PREFS, "Use UPNP changed: %s\n", vino_use_upnp ? "(true)" : "(false)");
+
+  for (l = vino_servers; l; l = l->next)
+    vino_server_set_use_upnp (l->data, use_upnp);
+}
+
 void
 vino_prefs_create_server (GdkScreen *screen)
 {
@@ -460,6 +487,7 @@
 			 "screen",               screen,
 			 "lock-screen",          vino_lock_screen,
 			 "disable-background",   vino_disable_background,
+			 "use-upnp",             vino_use_upnp,
 			 NULL);
 
   vino_servers = g_slist_prepend (vino_servers, server);
@@ -474,7 +502,7 @@
 }
 
 static void
-vino_prefs_restore (void)
+vino_prefs_restore_background (void)
 {
   if (vino_background_get_status ())
     vino_background_draw (TRUE);
@@ -545,6 +573,16 @@
   return res;
 }
 
+static void
+vino_prefs_sighandler (int sig)
+{
+  g_message (_("Received signal %d, exiting...\n"), sig);
+  vino_prefs_restore_background ();
+  vino_mdns_shutdown ();
+  vino_prefs_shutdown ();
+  exit (0);
+}
+
 void
 vino_prefs_init (gboolean view_only)
 {
@@ -552,9 +590,9 @@
   int i = 0;
   char *key_str;
   
-  signal (SIGQUIT, vino_background_handler); /* Ctrl+C */
-  signal (SIGTERM, vino_background_handler); /* kill -15 */
-  signal (SIGSEGV, vino_background_handler); /* Segmentation fault */
+  signal (SIGQUIT, vino_prefs_sighandler); /* Ctrl+C */
+  signal (SIGTERM, vino_prefs_sighandler); /* kill -15 */
+  signal (SIGSEGV, vino_prefs_sighandler); /* Segmentation fault */
 
   vino_client = gconf_client_get_default ();
 
@@ -564,7 +602,7 @@
 			NULL);
 
   if(!vino_prefs_lock ())
-    vino_prefs_restore ();
+    vino_prefs_restore_background ();
 
   vino_enabled = gconf_client_get_bool (vino_client, VINO_PREFS_ENABLED, NULL);
   dprintf (PREFS, "Access enabled: %s\n", vino_enabled ? "(true)" : "(false)");
@@ -646,6 +684,11 @@
                                                    NULL);
   dprintf (PREFS, "Disable background: %s\n", vino_disable_background ? "(true)" : "(false)");
 
+  vino_use_upnp = gconf_client_get_bool (vino_client,
+                                         VINO_PREFS_USE_UPNP,
+                                         NULL);
+  dprintf (PREFS, "Use UPNP: %s\n", vino_use_upnp ? "(true)" : "(false)");
+
   key_str = gconf_client_get_string (vino_client,
                                      VINO_PREFS_ICON_VISIBILITY,
                                      NULL);
@@ -744,6 +787,14 @@
 
   i++;
 
+  vino_listeners [i] =
+    gconf_client_notify_add (vino_client,
+			     VINO_PREFS_USE_UPNP,
+			     (GConfClientNotifyFunc) vino_prefs_use_upnp_changed,
+			     NULL, NULL, NULL);
+
+  i++;
+
   g_assert (i == VINO_N_LISTENERS);
 }
 

Modified: trunk/server/vino-server.c
==============================================================================
--- trunk/server/vino-server.c	(original)
+++ trunk/server/vino-server.c	Wed Dec 17 17:01:10 2008
@@ -37,6 +37,7 @@
 #include "vino-util.h"
 #include "vino-enums.h"
 #include "vino-background.h"
+#include "vino-upnp.h"
 #include <sys/poll.h>
 #include <dbus/dbus-glib.h>
 #include <gtk/gtk.h>
@@ -60,6 +61,7 @@
   VinoPrompt       *prompt;
   VinoStatusIcon   *icon;
   VinoDBusListener *listener;
+  VinoUpnp         *upnp;
 
   GIOChannel       *io_channel[RFB_MAX_SOCKETLISTEN];
   guint             io_watch[RFB_MAX_SOCKETLISTEN];
@@ -84,6 +86,7 @@
   guint             use_alternative_port : 1;
   guint             lock_screen : 1;
   guint             disable_background : 1;
+  guint             use_upnp : 1;
 };
 
 struct _VinoClient
@@ -114,7 +117,8 @@
   PROP_VNC_PASSWORD,
   PROP_PORT,
   PROP_LOCK_SCREEN,
-  PROP_DISABLE_BACKGROUND
+  PROP_DISABLE_BACKGROUND,
+  PROP_USE_UPNP
 };
 
 static enum rfbNewClientAction vino_server_auth_client (VinoServer *server,
@@ -164,7 +168,6 @@
 
   g_object_unref (proxy);
   dbus_g_connection_unref (connection);
-
 }
 
 static void
@@ -196,7 +199,6 @@
 
   g_object_unref (proxy);
   dbus_g_connection_unref (connection);
-
 }
 
 #undef GNOME_SCREENSAVER_BUS_NAME
@@ -228,6 +230,47 @@
 }
 
 static void
+vino_server_control_upnp (VinoServer *server)
+{
+  if (server->priv->use_upnp && !server->priv->on_hold)
+    {
+      if (!server->priv->upnp)
+	server->priv->upnp = vino_upnp_new ();
+      vino_upnp_add_port (server->priv->upnp, server->priv->rfb_screen->rfbPort);
+    }
+  else
+    if (server->priv->upnp)
+      {
+	g_object_unref (server->priv->upnp);
+	server->priv->upnp = NULL;
+      }
+}
+
+void
+vino_server_set_use_upnp (VinoServer *server,
+                          gboolean use_upnp)
+{
+  g_return_if_fail (VINO_IS_SERVER (server));
+
+  use_upnp = use_upnp != FALSE;
+
+  if (server->priv->use_upnp != use_upnp)
+    {
+      server->priv->use_upnp = use_upnp;
+      vino_server_control_upnp (server);
+      g_object_notify (G_OBJECT (server), "use-upnp");
+    }
+}
+
+gboolean
+vino_server_get_use_upnp (VinoServer *server)
+{
+  g_return_val_if_fail (VINO_IS_SERVER (server), FALSE);
+
+  return server->priv->use_upnp;
+}
+
+static void
 vino_server_client_accepted (VinoServer *server,
                              VinoClient *client)
 {
@@ -983,6 +1026,7 @@
                     server);
 
   server->priv->icon = vino_status_icon_new (server, server->priv->screen);
+  server->priv->upnp = NULL;
 }
 
 static void
@@ -1048,6 +1092,10 @@
   if (server->priv->icon)
     g_object_unref (server->priv->icon);
   server->priv->icon = NULL;
+
+  if (server->priv->upnp)
+    g_object_unref (server->priv->upnp);
+  server->priv->upnp = NULL;
   
   g_free (server->priv);
   server->priv = NULL;
@@ -1102,6 +1150,9 @@
     case PROP_DISABLE_BACKGROUND:
       vino_server_set_disable_background (server, g_value_get_boolean (value));
       break;
+    case PROP_USE_UPNP:
+      vino_server_set_use_upnp (server, g_value_get_boolean (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1157,6 +1208,9 @@
     case PROP_DISABLE_BACKGROUND:
       g_value_set_boolean (value, server->priv->disable_background);
       break;
+    case PROP_USE_UPNP:
+      g_value_set_boolean (value, server->priv->use_upnp);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1341,6 +1395,18 @@
                                                          G_PARAM_STATIC_NICK |
                                                          G_PARAM_STATIC_BLURB));
 
+  g_object_class_install_property (gobject_class,
+				   PROP_USE_UPNP,
+				   g_param_spec_boolean ("use-upnp",
+							 "Use UPNP",
+							 "Whether to use UPNP",
+							 FALSE,
+                                                         G_PARAM_READWRITE   |
+                                                         G_PARAM_CONSTRUCT   |
+                                                         G_PARAM_STATIC_NAME |
+                                                         G_PARAM_STATIC_NICK |
+                                                         G_PARAM_STATIC_BLURB));
+
 }
 
 GType
@@ -1472,6 +1538,8 @@
 
           rfbSetAutoPort (server->priv->rfb_screen,
                           !server->priv->use_alternative_port);
+
+	  vino_server_control_upnp (server);
         }
 
       g_object_notify (G_OBJECT (server), "use-alternative-port");
@@ -1498,7 +1566,10 @@
 
       if (server->priv->rfb_screen &&
           server->priv->use_alternative_port)
-        rfbSetPort (server->priv->rfb_screen, server->priv->alternative_port);
+	{
+	  rfbSetPort (server->priv->rfb_screen, server->priv->alternative_port);
+	  vino_server_control_upnp (server);
+	}
 
       g_object_notify (G_OBJECT (server), "alternative-port");
     }
@@ -1543,9 +1614,11 @@
 	}
 
       g_object_notify (G_OBJECT (server), "on-hold");
-      
+
       if (server->priv->icon)
         vino_status_icon_update_state (server->priv->icon);
+
+      vino_server_control_upnp (server);
     }
 }
 

Modified: trunk/server/vino-server.h
==============================================================================
--- trunk/server/vino-server.h	(original)
+++ trunk/server/vino-server.h	Wed Dec 17 17:01:10 2008
@@ -106,6 +106,10 @@
                                                            gboolean        disable_background);
 gboolean             vino_server_get_disable_background   (VinoServer     *server);
 
+void                 vino_server_set_use_upnp              (VinoServer     *server,
+                                                            gboolean        use_upnp);
+gboolean             vino_server_get_use_upnp              (VinoServer     *server);
+
 #include "vino-status-icon.h"
 VinoStatusIcon      *vino_server_get_status_icon          (VinoServer     *server);
 

Modified: trunk/server/vino-server.schemas.in
==============================================================================
--- trunk/server/vino-server.schemas.in	(original)
+++ trunk/server/vino-server.schemas.in	Wed Dec 17 17:01:10 2008
@@ -214,5 +214,20 @@
       </locale>
     </schema>
 
+    <schema>
+      <key>/schemas/desktop/gnome/remote_access/use_upnp</key>
+      <applyto>/desktop/gnome/remote_access/use_upnp</applyto>
+      <owner>gnome</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+         <short>Whether we should use UPNP to forward the port in routers</short>
+         <long>
+           If true, we will use UPNP protocol to automatically forward the port
+           used by vino in the router.
+         </long>
+      </locale>
+    </schema>
+
   </schemalist>  
 </gconfschemafile>

Added: trunk/server/vino-upnp.c
==============================================================================
--- (empty file)
+++ trunk/server/vino-upnp.c	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2008 Jonh Wendell
+ *
+ * This program 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Authors:
+ *      Jonh Wendell <wendell bani com br>
+ */
+
+#include <miniupnp/miniupnpc.h>
+#include <miniupnp/upnpcommands.h>
+
+#include "vino-upnp.h"
+#include "vino-util.h"
+
+struct _VinoUpnpPrivate
+{
+  struct UPNPUrls *urls;
+  struct IGDdatas *data;
+  char             lanaddr[16];
+  gboolean         have_igd;
+  int              port;
+};
+
+G_DEFINE_TYPE (VinoUpnp, vino_upnp, G_TYPE_OBJECT);
+
+static void
+clean_upnp_data (VinoUpnp *upnp)
+{
+  if (upnp->priv->urls)
+    {
+      FreeUPNPUrls (upnp->priv->urls);
+      g_free (upnp->priv->urls);
+      upnp->priv->urls = NULL;
+    }
+
+  if (upnp->priv->data)
+    {
+      g_free (upnp->priv->data);
+      upnp->priv->data = NULL;
+    }
+}
+
+static gboolean
+update_upnp_status (VinoUpnp *upnp)
+{
+  struct UPNPDev * devlist;
+  int res;
+
+  if (upnp->priv->have_igd)
+    return TRUE;
+
+  clean_upnp_data (upnp);
+
+  dprintf (UPNP, "UPnP: Doing the discovery... ");
+  devlist = upnpDiscover (2000, NULL, NULL, 0);
+  if (!devlist)
+    {
+      dprintf (UPNP, "nothing found, aborting.");
+      return FALSE;
+    }
+  dprintf (UPNP, "found.\n");
+  dprintf (UPNP, "UPnP: Looking for a valid IGD... ");
+
+  upnp->priv->urls = g_new (struct UPNPUrls, 1);
+  upnp->priv->data = g_new (struct IGDdatas, 1);
+
+  res = UPNP_GetValidIGD (devlist,
+			  upnp->priv->urls,
+			  upnp->priv->data,
+                          upnp->priv->lanaddr,
+                          sizeof (upnp->priv->lanaddr));
+
+  if (res == 1 || res == 2)
+    {
+      dprintf (UPNP, "found: %s\n", upnp->priv->urls->controlURL);
+      upnp->priv->have_igd = TRUE;
+    }
+  else
+    {
+      dprintf (UPNP, "none found, aborting.\n");
+      upnp->priv->have_igd = FALSE;
+    }
+
+  freeUPNPDevlist (devlist);
+  return upnp->priv->have_igd;
+}
+
+static void
+vino_upnp_finalize (GObject *object)
+{
+  VinoUpnp *upnp = VINO_UPNP (object);
+
+  clean_upnp_data (upnp);
+
+  G_OBJECT_CLASS (vino_upnp_parent_class)->finalize (object);
+}
+
+static void
+vino_upnp_dispose (GObject *object)
+{
+  VinoUpnp *upnp = VINO_UPNP (object);
+
+  vino_upnp_remove_port (upnp);
+
+  G_OBJECT_CLASS (vino_upnp_parent_class)->dispose (object);
+}
+
+static void
+vino_upnp_class_init (VinoUpnpClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose  = vino_upnp_dispose;
+  gobject_class->finalize = vino_upnp_finalize;
+
+  g_type_class_add_private (gobject_class, sizeof (VinoUpnpPrivate));
+}
+
+static void
+vino_upnp_init (VinoUpnp *upnp)
+{
+  upnp->priv = G_TYPE_INSTANCE_GET_PRIVATE (upnp, VINO_TYPE_UPNP, VinoUpnpPrivate);
+
+  upnp->priv->urls = NULL;
+  upnp->priv->data = NULL;
+  upnp->priv->have_igd = FALSE;
+  upnp->priv->port = -1;
+}
+
+VinoUpnp *
+vino_upnp_new (void)
+{
+  return VINO_UPNP (g_object_new (VINO_TYPE_UPNP, NULL));
+}
+
+gchar *
+vino_upnp_get_external_ip (VinoUpnp *upnp)
+{
+  gchar ip[16];
+
+  g_return_val_if_fail (VINO_IS_UPNP (upnp), NULL);
+
+  if (!update_upnp_status (upnp))
+    return NULL;
+
+  UPNP_GetExternalIPAddress (upnp->priv->urls->controlURL,
+			     upnp->priv->data->servicetype,
+			     ip);
+  if (ip[0])
+    if (strcmp (ip, "0.0.0.0") == 0)
+      return NULL;
+    else
+      return g_strdup (ip);
+  else
+    return NULL;
+}
+
+int
+vino_upnp_add_port (VinoUpnp *upnp, int port)
+{
+  char *ext_port, *int_port, *desc;
+  int   err, local_port;
+  char  int_client_tmp[16], int_port_tmp[6];
+
+  g_return_val_if_fail (VINO_IS_UPNP (upnp), -1);
+
+  if (!update_upnp_status (upnp))
+    return -1;
+
+  vino_upnp_remove_port (upnp);
+
+  local_port = port;
+  do
+    {
+      ext_port = g_strdup_printf ("%d", local_port);
+      dprintf (UPNP, "UPnP: Trying to forward port %d...: ", local_port);
+      UPNP_GetSpecificPortMappingEntry (upnp->priv->urls->controlURL,
+					upnp->priv->data->servicetype,
+					ext_port,
+					"TCP",
+					int_client_tmp,
+					int_port_tmp);
+      if (int_client_tmp[0])
+	{
+	  dprintf (UPNP, "Failed, this port is already forwarded to %s:%s\n", int_client_tmp, int_port_tmp);
+	  g_free (ext_port);
+	}
+      else
+	{
+	  dprintf (UPNP, "OK, this port is free on the router\n");
+	  break;
+	}
+
+      local_port++;
+    } while (local_port < INT_MAX);
+
+  if (local_port == INT_MAX)
+    {
+      dprintf (UPNP, "UPnP: Not forwarding any port, tried so much\n");
+      return -1;
+    }
+
+  int_port = g_strdup_printf ("%d", port);
+  desc = g_strdup_printf ("VNC: %s %s",
+			  g_get_user_name (),
+			  g_get_host_name ());  
+
+  err = UPNP_AddPortMapping (upnp->priv->urls->controlURL,
+			     upnp->priv->data->servicetype,
+			     ext_port,
+			     int_port,
+			     upnp->priv->lanaddr,
+			     desc,
+			     "TCP");
+  if (err == 0)
+    {
+      upnp->priv->port = local_port;
+      dprintf (UPNP, "UPnP: Successfuly forwarded port %d\n", local_port);
+    }
+  else
+    dprintf (UPNP, "Failed to forward port %d, with status %d\n", local_port, err);
+
+  g_free (ext_port);
+  g_free (int_port);
+  g_free (desc);
+
+  return upnp->priv->port;
+}
+
+void
+vino_upnp_remove_port (VinoUpnp *upnp)
+{
+  char *port;
+  int   err;
+
+  g_return_if_fail (VINO_IS_UPNP (upnp));
+
+  if (upnp->priv->port == -1)
+    return;
+
+  if (!update_upnp_status (upnp))
+    return;
+
+  port = g_strdup_printf ("%d", upnp->priv->port);
+  err = UPNP_DeletePortMapping (upnp->priv->urls->controlURL,
+				upnp->priv->data->servicetype,
+				port,
+				"TCP");
+  if (err == 0)
+    dprintf (UPNP, "UPnP: Removed forwarded port %d\n", upnp->priv->port);
+  else
+    dprintf (UPNP, "UPnP: Failed to remove forwarded port %d with status %d\n", upnp->priv->port, err);
+
+  g_free (port);
+  upnp->priv->port = -1;
+}
+
+int
+vino_upnp_get_external_port (VinoUpnp *upnp)
+{
+  g_return_val_if_fail (VINO_IS_UPNP (upnp), -1);
+
+  return upnp->priv->port;
+}

Added: trunk/server/vino-upnp.h
==============================================================================
--- (empty file)
+++ trunk/server/vino-upnp.h	Wed Dec 17 17:01:10 2008
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 Jonh Wendell.
+ *
+ * This program 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Authors:
+ *      Jonh Wendell <wendell bani com br>
+ */
+
+#ifndef __VINO_UPNP_H__
+#define __VINO_IPNP_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define VINO_TYPE_UPNP         (vino_upnp_get_type ())
+#define VINO_UPNP(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), VINO_TYPE_UPNP, VinoUpnp))
+#define VINO_UPNP_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), VINO_TYPE_UPNP, VinoUpnpClass))
+#define VINO_IS_UPNP(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), VINO_TYPE_UPNP))
+#define VINO_IS_UPNP_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), VINO_TYPE_UPNP))
+#define VINO_UPNP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), VINO_TYPE_UPNP, VinoUpnpClass))
+
+typedef struct _VinoUpnp        VinoUpnp;
+typedef struct _VinoUpnpClass   VinoUpnpClass;
+typedef struct _VinoUpnpPrivate VinoUpnpPrivate;
+
+struct _VinoUpnp
+{
+  GObject          base;
+  VinoUpnpPrivate *priv;
+};
+
+struct _VinoUpnpClass
+{
+  GObjectClass base_class;
+};
+
+GType		vino_upnp_get_type		(void) G_GNUC_CONST;
+
+VinoUpnp	*vino_upnp_new			(void);
+gchar		*vino_upnp_get_external_ip	(VinoUpnp *upnp);
+int		vino_upnp_get_external_port	(VinoUpnp *upnp);
+int		vino_upnp_add_port		(VinoUpnp *upnp, int port);
+void		vino_upnp_remove_port		(VinoUpnp *upnp);
+
+G_END_DECLS
+
+#endif /* __VINO_UPNP_H__ */

Modified: trunk/server/vino-util.c
==============================================================================
--- trunk/server/vino-util.c	(original)
+++ trunk/server/vino-util.c	Wed Dec 17 17:01:10 2008
@@ -42,7 +42,8 @@
       { "mdns",    VINO_DEBUG_MDNS },
       { "prompt",  VINO_DEBUG_PROMPT },
       { "http",    VINO_DEBUG_HTTP },
-      { "dbus",    VINO_DEBUG_DBUS }
+      { "dbus",    VINO_DEBUG_DBUS },
+      { "upnp",    VINO_DEBUG_UPNP }
     };
   
   env_str = g_getenv ("VINO_SERVER_DEBUG");

Modified: trunk/server/vino-util.h
==============================================================================
--- trunk/server/vino-util.h	(original)
+++ trunk/server/vino-util.h	Wed Dec 17 17:01:10 2008
@@ -45,7 +45,8 @@
   VINO_DEBUG_MDNS    = 1 << 5,
   VINO_DEBUG_PROMPT  = 1 << 6,
   VINO_DEBUG_HTTP    = 1 << 7,
-  VINO_DEBUG_DBUS    = 1 << 8
+  VINO_DEBUG_DBUS    = 1 << 8,
+  VINO_DEBUG_UPNP    = 1 << 9
 } VinoDebugFlags;
 
 #ifdef G_ENABLE_DEBUG



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]