diff --git a/glade/delugegtk.glade b/glade/delugegtk.glade index 6843b6f99..3bb57edef 100644 --- a/glade/delugegtk.glade +++ b/glade/delugegtk.glade @@ -14,605 +14,139 @@ 4 3 - + True + False - + True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - True - True - False - - - + Add Torrent + Add Torrent + True + gtk-add + - False + False - + True - True - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - - - True - 10 - 12 - 4 - - - - - - - - - - - - - - - - - - - - - True - - - 3 - 4 - 11 - 12 - - - - - True - 0 - <b>Estimated Time Remaining:</b> - True - - - 2 - 3 - 11 - 12 - - - - - True - 0 - - - 3 - 4 - 5 - 6 - - - - - True - 0 - - - 1 - 2 - 5 - 6 - - - - - True - 0 - <b>Peers:</b> - True - - - 2 - 3 - 5 - 6 - - - - - True - 0 - <b>Seeders:</b> - True - - - 5 - 6 - - - - - True - 0 - <b>Total Size:</b> - True - - - 2 - 3 - - - - - True - 0 - <b>Total Downloaded:</b> - True - - - 3 - 4 - - - - - True - 0 - <b>Downloaded this session:</b> - True - - - 7 - 8 - - - - - True - 0 - - - 1 - 2 - 2 - 3 - - - - - True - 0 - - - 1 - 2 - 3 - 4 - - - - - True - 0 - - - 1 - 2 - 7 - 8 - - - - - True - 0 - <b>Tracker:</b> - True - - - 8 - 9 - - - - - True - 0 - <b>Tracker Response:</b> - True - - - 9 - 10 - - - - - True - 0 - <b>Tracker Status:</b> - True - - - 10 - 11 - - - - - True - 0 - - - 1 - 2 - 10 - 11 - - - - - True - 0 - <b>Next Announce:</b> - True - - - 2 - 3 - 10 - 11 - - - - - True - 0 - - - 3 - 4 - 10 - 11 - - - - - True - 0 - <b>Pieces:</b> - True - - - 2 - 3 - 2 - 3 - - - - - True - 0 - <b>Total Uploaded:</b> - True - - - 2 - 3 - 3 - 4 - - - - - True - 0 - - - 3 - 4 - 2 - 3 - - - - - True - 0 - - - 3 - 4 - 3 - 4 - - - - - True - 0 - <b>Share Ratio:</b> - True - - - 2 - 3 - 6 - 7 - - - - - True - 0 - <b>Uploaded This Session:</b> - True - - - 2 - 3 - 7 - 8 - - - - - True - 0 - - - 3 - 4 - 6 - 7 - - - - - True - 0 - - - 3 - 4 - 7 - 8 - - - - - True - 0 - - - 1 - 4 - 8 - 9 - - - - - True - 0 - - - 1 - 4 - 9 - 10 - - - - - True - 0 - <b>Use compact storage allocation:</b> - True - - - 11 - 12 - - - - - True - 0 - - - 1 - 2 - 11 - 12 - - - - - True - 0 - <b>Download Rate:</b> - True - - - 4 - 5 - - - - - True - 0 - - - 1 - 2 - 4 - 5 - - - - - True - 0 - <b>Upload Rate:</b> - True - - - 2 - 3 - 4 - 5 - - - - - True - 0 - - - 3 - 4 - 4 - 5 - - - - - True - - - 4 - - - - - - - - - - False - - - - - True - Details - - - tab - False - False - - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - - - - - 1 - False - - - - - True - Peers - - - tab - 1 - False - False - - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - - - - - 2 - False - - - - - True - Files - - - tab - 2 - False - False - - + Remove Torrent + Remove Torrent + True + gtk-remove + - False - False + False + + + + + True + Clear Finished Torrents + Clear Finished + True + gtk-clear + + + + False + + + + + True + + + False + False + + + + + True + Start / Pause + Start / Pause + True + gtk-media-play + + + + False + + + + + True + Queue Torrent Up + Move Up + True + gtk-go-up + + + + False + + + + + True + Queue Torrent Down + Move Down + True + gtk-go-down + + + + False - 3 - 2 - 3 + 1 + 2 + + GTK_FILL - + True + GTK_TOOLBAR_BOTH_HORIZ + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + GTK_TOOLBAR_BOTH_HORIZ + False + + + 2 3 - 3 - 4 - + 1 + 2 + + GTK_FILL True - + True - _File + _Torrent True @@ -866,130 +400,597 @@ - + True - GTK_TOOLBAR_BOTH_HORIZ - False - 2 3 - 1 - 2 - - GTK_FILL + 3 + 4 + - + True - GTK_TOOLBAR_BOTH_HORIZ - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - True - False - + True - Add Torrent - Add Torrent - True - gtk-add - + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + True + True + False + + + - False + False + False - + True - Remove Torrent - Remove Torrent - True - gtk-remove - + True + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + + + True + 10 + 12 + 4 + + + + + + + + + + + + + + + + + + + + + True + + + 4 + + + + + + True + 0 + + + 3 + 4 + 4 + 5 + + + + + True + 0 + <b>Upload Rate:</b> + True + + + 2 + 3 + 4 + 5 + + + + + True + 0 + + + 1 + 2 + 4 + 5 + + + + + True + 0 + <b>Download Rate:</b> + True + + + 4 + 5 + + + + + True + 0 + + + 1 + 2 + 11 + 12 + + + + + True + 0 + <b>Use compact storage allocation:</b> + True + + + 11 + 12 + + + + + True + 0 + + + 1 + 4 + 9 + 10 + + + + + True + 0 + + + 1 + 4 + 8 + 9 + + + + + True + 0 + + + 3 + 4 + 7 + 8 + + + + + True + 0 + + + 3 + 4 + 6 + 7 + + + + + True + 0 + <b>Uploaded This Session:</b> + True + + + 2 + 3 + 7 + 8 + + + + + True + 0 + <b>Share Ratio:</b> + True + + + 2 + 3 + 6 + 7 + + + + + True + 0 + + + 3 + 4 + 3 + 4 + + + + + True + 0 + + + 3 + 4 + 2 + 3 + + + + + True + 0 + <b>Total Uploaded:</b> + True + + + 2 + 3 + 3 + 4 + + + + + True + 0 + <b>Pieces:</b> + True + + + 2 + 3 + 2 + 3 + + + + + True + 0 + + + 3 + 4 + 10 + 11 + + + + + True + 0 + <b>Next Announce:</b> + True + + + 2 + 3 + 10 + 11 + + + + + True + 0 + + + 1 + 2 + 10 + 11 + + + + + True + 0 + <b>Tracker Status:</b> + True + + + 10 + 11 + + + + + True + 0 + <b>Tracker Response:</b> + True + + + 9 + 10 + + + + + True + 0 + <b>Tracker:</b> + True + + + 8 + 9 + + + + + True + 0 + + + 1 + 2 + 7 + 8 + + + + + True + 0 + + + 1 + 2 + 3 + 4 + + + + + True + 0 + + + 1 + 2 + 2 + 3 + + + + + True + 0 + <b>Downloaded this session:</b> + True + + + 7 + 8 + + + + + True + 0 + <b>Total Downloaded:</b> + True + + + 3 + 4 + + + + + True + 0 + <b>Total Size:</b> + True + + + 2 + 3 + + + + + True + 0 + <b>Seeders:</b> + True + + + 5 + 6 + + + + + True + 0 + <b>Peers:</b> + True + + + 2 + 3 + 5 + 6 + + + + + True + 0 + + + 1 + 2 + 5 + 6 + + + + + True + 0 + + + 3 + 4 + 5 + 6 + + + + + True + 0 + <b>Estimated Time Remaining:</b> + True + + + 2 + 3 + 11 + 12 + + + + + True + + + 3 + 4 + 11 + 12 + + + + + + + + + False + + + + + True + Details + + + tab + False + False + + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + + + + + 1 + False + + + + + True + Peers + + + tab + 1 + False + False + + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + False + + + + + True + Files + + + tab + 2 + False + False + + - False - - - - - True - Clear Finished Torrents - Clear Finished - True - gtk-clear - - - - False - - - - - True - - - False - False - - - - - True - Start / Pause - Start / Pause - True - gtk-media-play - - - - False - - - - - True - Queue Torrent Up - Move Up - True - gtk-go-up - - - - False - - - - - True - Queue Torrent Down - Move Down - True - gtk-go-down - - - - False + False + False - 1 - 2 - - GTK_FILL + 3 + 2 + 3 diff --git a/miniupnpc/Changelog.txt b/miniupnpc/Changelog.txt new file mode 100644 index 000000000..f8efffacf --- /dev/null +++ b/miniupnpc/Changelog.txt @@ -0,0 +1,85 @@ +$Id: Changelog.txt,v 1.27 2007/03/01 22:29:48 nanard Exp $ +miniUPnP client Changelog. + +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 + diff --git a/miniupnpc/LICENCE b/miniupnpc/LICENCE new file mode 100644 index 000000000..80d8e0cb5 --- /dev/null +++ b/miniupnpc/LICENCE @@ -0,0 +1,26 @@ +Copyright (c) 2005-2006, 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. + diff --git a/miniupnpc/Makefile b/miniupnpc/Makefile new file mode 100644 index 000000000..c46569370 --- /dev/null +++ b/miniupnpc/Makefile @@ -0,0 +1,76 @@ +# $Id: Makefile,v 1.21 2007/03/02 10:45:52 nanard Exp $ +# MiniUPnP Project +# http://miniupnp.free.fr/ +# (c) 2005-2006 Thomas Bernard +CC = gcc +#AR = gar +CFLAGS = -O -Wall -g -DDEBUG +#CFLAGS = -O -Wall -DNDEBUG +INSTALL = install +#following libs are needed on Solaris +#LDLIBS=-lsocket -lnsl -lresolv + +SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \ + upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \ + minixmlvalid.c + +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +# HEADERS to install +HEADERS = miniupnpc.h miniwget.h upnpcommands.h igd_desc_parse.h \ + upnpreplyparse.h +LIBRARY = libminiupnpc.a +EXECUTABLES = upnpc testminixml minixmlvalid + +INSTALLPREFIX = /usr +INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc +INSTALLDIRLIB = $(INSTALLPREFIX)/lib + +.PHONY: install clean depend all + +all: validateminixml $(LIBRARY) $(EXECUTABLES) + +validateminixml: minixmlvalid + @echo "minixml validation test" + ./minixmlvalid + touch $@ + +clean: + $(RM) $(LIBRARY) $(EXECUTABLES) $(OBJS) + +install: $(LIBRARY) + $(INSTALL) -d $(INSTALLDIRINC) + $(INSTALL) --mode=644 $(HEADERS) $(INSTALLDIRINC) + $(INSTALL) --mode=644 $(LIBRARY) $(INSTALLDIRLIB) + +cleaninstall: + $(RM) -r $(INSTALLDIRINC) + $(RM) $(INSTALLDIRLIB)/$(LIBRARY) + +depend: + makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null + +libminiupnpc.a: miniwget.o minixml.o igd_desc_parse.o minisoap.o miniupnpc.o upnpreplyparse.o upnpcommands.o + $(AR) crs $@ $? + +upnpc: upnpc.o libminiupnpc.a + +testminixml: minixml.o igd_desc_parse.o testminixml.o + +minixmlvalid: minixml.o minixmlvalid.o + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +igd_desc_parse.o: igd_desc_parse.h +miniupnpc.o: miniupnpc.h declspec.h igd_desc_parse.h miniwget.h minisoap.h +miniupnpc.o: minixml.h upnpcommands.h upnpreplyparse.h +minixml.o: minixml.h +minisoap.o: minisoap.h +miniwget.o: miniupnpc.h declspec.h igd_desc_parse.h +upnpc.o: miniwget.h declspec.h miniupnpc.h igd_desc_parse.h upnpcommands.h +upnpc.o: upnpreplyparse.h +upnpcommands.o: upnpcommands.h upnpreplyparse.h miniupnpc.h declspec.h +upnpcommands.o: igd_desc_parse.h +upnpreplyparse.o: upnpreplyparse.h minixml.h +testminixml.o: minixml.h igd_desc_parse.h +minixmlvalid.o: minixml.h diff --git a/miniupnpc/Makefile.mingw b/miniupnpc/Makefile.mingw new file mode 100644 index 000000000..c87924213 --- /dev/null +++ b/miniupnpc/Makefile.mingw @@ -0,0 +1,35 @@ +# $Id: Makefile.mingw,v 1.4 2006/11/08 22:07:47 nanard Exp $ +CC = gcc +#CFLAGS = -Wall -g -DDEBUG +CFLAGS = -Wall -Os -DNDEBUG +LDLIBS = -lws2_32 +# -lwsock32 + +all: upnpc testminixml libminiupnpc.a + +clean: + del upnpc testminixml *.o + +libminiupnpc.a: miniwget.o minixml.o igd_desc_parse.o minisoap.o miniupnpc.o upnpreplyparse.o upnpcommands.o + $(AR) cr $@ $? + +upnpc: upnpc.o libminiupnpc.a + +minixml.o: minixml.c minixml.h + +upnpc.o: upnpc.c miniwget.h minisoap.h miniupnpc.h igd_desc_parse.h upnpreplyparse.h upnpcommands.h + +miniwget.o: miniwget.c miniwget.h + +minisoap.o: minisoap.c minisoap.h + +miniupnpc.o: miniupnpc.c miniupnpc.h minisoap.h miniwget.h minixml.h + +igd_desc_parse.o: igd_desc_parse.c igd_desc_parse.h + +testminixml: minixml.o igd_desc_parse.o testminixml.c + +upnpreplyparse.o: upnpreplyparse.c upnpreplyparse.h minixml.h + +upnpcommands.o: upnpcommands.c upnpcommands.h upnpreplyparse.h miniupnpc.h + diff --git a/miniupnpc/README b/miniupnpc/README new file mode 100644 index 000000000..7956e056b --- /dev/null +++ b/miniupnpc/README @@ -0,0 +1,42 @@ +Project: miniupnp +Project web page: 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 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 + +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 etc... for the includes +and -lminiupnpc for the link + +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 ! + + diff --git a/miniupnpc/bsdqueue.h b/miniupnpc/bsdqueue.h new file mode 100644 index 000000000..1fe0599f5 --- /dev/null +++ b/miniupnpc/bsdqueue.h @@ -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_ */ diff --git a/miniupnpc/declspec.h b/miniupnpc/declspec.h new file mode 100644 index 000000000..113339c5d --- /dev/null +++ b/miniupnpc/declspec.h @@ -0,0 +1,15 @@ +#ifndef __DECLSPEC_H__ +#define __DECLSPEC_H__ + +#ifdef WIN32 + #ifdef MINIUPNP_EXPORTS + #define LIBSPEC __declspec(dllexport) + #else + #define LIBSPEC __declspec(dllimport) + #endif +#else + #define LIBSPEC +#endif + +#endif + diff --git a/miniupnpc/igd_desc_parse.c b/miniupnpc/igd_desc_parse.c new file mode 100644 index 000000000..5b06facee --- /dev/null +++ b/miniupnpc/igd_desc_parse.c @@ -0,0 +1,115 @@ +/* $Id: igd_desc_parse.c,v 1.7 2006/11/19 22:32:33 nanard Exp $ */ +/* Project : miniupnp + * 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. + * */ +#include "igd_desc_parse.h" +#include +#include + +/* 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++; +} + +/* 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) ) + { + /*datas->state++; */ + /* + 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_CIF, + "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) + datas->state = 2; + if(0==strcmp(datas->servicetype, + "urn:schemas-upnp-org:service:WANIPConnection:1") ) + datas->state = 3; +/* if(0==strcmp(datas->servicetype, + "urn:schemas-upnp-org:service:WANPPPConnection:1") ) + datas->state = 4; */ + } +} + +/* 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(datas->state<=1) + { + if( !strcmp(datas->cureltname, "serviceType") ) + dstmember = datas->servicetype_CIF; + else if( !strcmp(datas->cureltname, "controlURL") ) + dstmember = datas->controlurl_CIF; + else if( !strcmp(datas->cureltname, "eventSubURL") ) + dstmember = datas->eventsuburl_CIF; + else if( !strcmp(datas->cureltname, "SCPDURL") ) + dstmember = datas->scpdurl_CIF; + else if( !strcmp(datas->cureltname, "deviceType") ) + dstmember = datas->devicetype_CIF; + } + else if(datas->state==2) + { + if( !strcmp(datas->cureltname, "serviceType") ) + dstmember = datas->servicetype; + else if( !strcmp(datas->cureltname, "controlURL") ) + dstmember = datas->controlurl; + else if( !strcmp(datas->cureltname, "eventSubURL") ) + dstmember = datas->eventsuburl; + else if( !strcmp(datas->cureltname, "SCPDURL") ) + dstmember = datas->scpdurl; + else if( !strcmp(datas->cureltname, "deviceType") ) + dstmember = datas->devicetype; + } + 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 :\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); +} + + diff --git a/miniupnpc/igd_desc_parse.h b/miniupnpc/igd_desc_parse.h new file mode 100644 index 000000000..474dc7b49 --- /dev/null +++ b/miniupnpc/igd_desc_parse.h @@ -0,0 +1,38 @@ +/* $Id: igd_desc_parse.h,v 1.4 2006/07/06 00:05:11 nanard Exp $ */ +/* Project : miniupnp + * 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 __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 (64) +struct IGDdatas { + char cureltname[MINIUPNPC_URL_MAXSIZE]; + char urlbase[MINIUPNPC_URL_MAXSIZE]; + int level; + int state; + 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]; + 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]; +}; + +void IGDstartelt(void *, const char *, int); +void IGDendelt(void *, const char *, int); +void IGDdata(void *, const char *, int); +void printIGD(struct IGDdatas *); + +#endif + diff --git a/miniupnpc/mingw32make.bat b/miniupnpc/mingw32make.bat new file mode 100644 index 000000000..94ec0b932 --- /dev/null +++ b/miniupnpc/mingw32make.bat @@ -0,0 +1,5 @@ +mingw32-make -f Makefile.mingw +if errorlevel 1 goto end +strip upnpc.exe +upx --best upnpc.exe +:end diff --git a/miniupnpc/minisoap.c b/miniupnpc/minisoap.c new file mode 100644 index 000000000..7267af649 --- /dev/null +++ b/miniupnpc/minisoap.c @@ -0,0 +1,73 @@ +/* $Id: minisoap.c,v 1.10 2007/01/29 20:05: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 +#include +#ifdef WIN32 +#include +#include +#define snprintf _snprintf +#else +#include +#include +#include +#endif +#include "minisoap.h" + +/* only for malloc */ +#include + +/* 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; + p = malloc(headerssize+bodysize); + memcpy(p, headers, headerssize); + memcpy(p+headerssize, body, bodysize); + /*n = write(fd, p, headerssize+bodysize);*/ + n = send(fd, p, headerssize+bodysize, 0); + shutdown(fd, SHUT_WR); /*SD_SEND*/ + 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[1024]; + int headerssize; + bodysize = (int)strlen(body); + headerssize = snprintf(headerbuf, sizeof(headerbuf), + "POST %s HTTP/1.1\r\n" + "HOST: %s:%d\r\n" + "Content-length: %d\r\n" + "Content-Type: text/xml\r\n" + "SOAPAction: \"%s\"\r\n" + "Connection: Close\r\n" + "\r\n", + url, host, port, bodysize, action); + return httpWrite(fd, body, bodysize, headerbuf, headerssize); +} + + diff --git a/miniupnpc/minisoap.h b/miniupnpc/minisoap.h new file mode 100644 index 000000000..9fa297fd3 --- /dev/null +++ b/miniupnpc/minisoap.h @@ -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 + diff --git a/miniupnpc/miniupnpc.c b/miniupnpc/miniupnpc.c new file mode 100644 index 000000000..fc4623733 --- /dev/null +++ b/miniupnpc/miniupnpc.c @@ -0,0 +1,659 @@ +/* $Id: miniupnpc.c,v 1.38 2007/03/01 22:29:49 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#define snprintf _snprintf +#define strncasecmp memicmp +#define MAXHOSTNAMELEN 64 +#else +#include +#include +#include +#include +#include +#include +#include +#include +#define closesocket close +#endif +#include "miniupnpc.h" +#include "miniwget.h" +#include "minisoap.h" +#include "minixml.h" +#include "upnpcommands.h" + +/* Uncomment the following to transmit the msearch from the same port + * as the UPnP multicast port. With WinXP this seems to result in the + * responses to the msearch being lost, thus if things dont work then + * comment this out. */ +/* #define TX_FROM_UPNP_PORT */ + +#ifdef WIN32 +#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); +#else +#define PRINT_SOCKET_ERROR(x) perror(x) +#endif + +/* 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 ! + * */ +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]; + int soapbodylen; + 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), + "\r\n" + "" + "" + "" + "" + "\r\n", action, service); + } + else + { + char * p; + const char * pe, * pv; + soapbodylen = snprintf(soapbody, sizeof(soapbody), + "\r\n" + "" + "" + "", + 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, ">\r\n", + soapbody + sizeof(soapbody) - p); + } + if(!parseURL(url, hostname, &port, &path)) return -1; + if(s<0) + { + s = socket(PF_INET, SOCK_STREAM, 0); + 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"); + *bufsize = 0; + return -1; + } + } + + n = soapPostSubmit(s, path, hostname, port, soapact, soapbody); + + 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("n=%d bufsize=%d ContLen=%d HeadLen=%d\n", + n, *bufsize, contentlen, headerlen); +#endif + if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen) + break; + } + + closesocket(s); + return -1; +} + +/* 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(ipNext = 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 + { + if(src[0] != '/') + strncat(dst, "/", n-1); + strncat(dst, src, n-1); + } +} + +/* 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++; + 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); + if(n < 0) + { + PRINT_SOCKET_ERROR("poll"); + 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); + 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 */ + if((state >= 3) || strstr(dev->st, "InternetGatewayDevice")) + { + 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; + 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; +} + + + diff --git a/miniupnpc/miniupnpc.h b/miniupnpc/miniupnpc.h new file mode 100644 index 000000000..939c286a8 --- /dev/null +++ b/miniupnpc/miniupnpc.h @@ -0,0 +1,81 @@ +/* $Id: miniupnpc.h,v 1.13 2007/01/29 20:27:23 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 + +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]; +}; + +/* discover UPnP devices on the network */ +LIBSPEC struct UPNPDev * upnpDiscover(int); +/* free returned list from above function */ +LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); + +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); + +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 + diff --git a/miniupnpc/miniwget.c b/miniupnpc/miniwget.c new file mode 100644 index 000000000..b8295984c --- /dev/null +++ b/miniupnpc/miniwget.c @@ -0,0 +1,212 @@ +/* $Id: miniwget.c,v 1.17 2006/12/03 17:22:09 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 +#include +#include +#include "miniupnpc.h" +#ifdef WIN32 +#include +#include +#define MAXHOSTNAMELEN 64 +#define MIN(x,y) (((x)<(y))?(x):(y)) +#define snprintf _snprintf +#define herror +#define socklen_t int +#else +#include +#include +#include +#include +#include +#include +#define closesocket close +#endif +/* for MIN() macro : */ +#if defined(__sun) || defined(sun) +#include +#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); + memset(dest.sin_zero, 0, sizeof(dest.sin_zero)); + s = socket(PF_INET, SOCK_STREAM, 0); + dest.sin_family = AF_INET; + dest.sin_port = htons(port); + if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0) + { + perror("connect"); + 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, 2048, + "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(ip3)) + { + 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); +} + diff --git a/miniupnpc/miniwget.h b/miniupnpc/miniwget.h new file mode 100644 index 000000000..12c062c6d --- /dev/null +++ b/miniupnpc/miniwget.h @@ -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 + diff --git a/miniupnpc/minixml.c b/miniupnpc/minixml.c new file mode 100644 index 000000000..dd92f5806 --- /dev/null +++ b/miniupnpc/minixml.c @@ -0,0 +1,185 @@ +/* $Id: minixml.c,v 1.5 2007/01/27 14:20:00 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-2006, 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; + } + 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); +} + + diff --git a/miniupnpc/minixml.h b/miniupnpc/minixml.h new file mode 100644 index 000000000..857c70ee9 --- /dev/null +++ b/miniupnpc/minixml.h @@ -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 + diff --git a/miniupnpc/minixmlvalid.c b/miniupnpc/minixmlvalid.c new file mode 100644 index 000000000..e7967befa --- /dev/null +++ b/miniupnpc/minixmlvalid.c @@ -0,0 +1,149 @@ +/* $Id: minixmlvalid.c,v 1.2 2006/11/30 11:31:55 nanard Exp $ */ +/* MiniUPnP Project + * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ + * minixmlvalid.c : + * validation program for the minixml parser + * + * (c) 2006 Thomas Bernard */ + +#include +#include +#include +#include "minixml.h" + +/* xml event structure */ +struct event { + enum { ELTSTART, ELTEND, ATT, CHARDATA } type; + const char * data; + int len; +}; + +struct eventlist { + int n; + struct event * events; +}; + +/* compare 2 xml event lists + * return 0 if the two lists are equals */ +int evtlistcmp(struct eventlist * a, struct eventlist * b) +{ + int i; + struct event * ae, * be; + if(a->n != b->n) + return 1; + for(i=0; in; i++) + { + ae = a->events + i; + be = b->events + i; + if( (ae->type != be->type) + ||(ae->len != be->len) + ||memcmp(ae->data, be->data, ae->len)) + { + printf("Found a difference : %d '%.*s' != %d '%.*s'\n", + ae->type, ae->len, ae->data, + be->type, be->len, be->data); + return 1; + } + } + return 0; +} + +/* Test data */ +static const char xmldata[] = +"\n" +" " +"character data" +" \n \t" +"" +" \tchardata1chardata2" +""; + +static const struct event evtref[] = +{ + {ELTSTART, "xmlroot", 7}, + {ELTSTART, "elt1", 4}, + /* attributes */ + {CHARDATA, "character data", 14}, + {ELTEND, "elt1", 4}, + {ELTSTART, "elt1b", 5}, + {ELTSTART, "elt2a", 5}, + {ELTSTART, "elt2b", 5}, + {CHARDATA, "chardata1", 9}, + {ELTEND, "elt2b", 5}, + {ELTSTART, "elt2b", 5}, + {CHARDATA, "chardata2", 9}, + {ELTEND, "elt2b", 5}, + {ELTEND, "elt2a", 5}, + {ELTEND, "xmlroot", 7} +}; + +void startelt(void * data, const char * p, int l) +{ + struct eventlist * evtlist = data; + struct event * evt; + evt = evtlist->events + evtlist->n; + /*printf("startelt : %.*s\n", l, p);*/ + evt->type = ELTSTART; + evt->data = p; + evt->len = l; + evtlist->n++; +} + +void endelt(void * data, const char * p, int l) +{ + struct eventlist * evtlist = data; + struct event * evt; + evt = evtlist->events + evtlist->n; + /*printf("endelt : %.*s\n", l, p);*/ + evt->type = ELTEND; + evt->data = p; + evt->len = l; + evtlist->n++; +} + +void chardata(void * data, const char * p, int l) +{ + struct eventlist * evtlist = data; + struct event * evt; + evt = evtlist->events + evtlist->n; + /*printf("chardata : '%.*s'\n", l, p);*/ + evt->type = CHARDATA; + evt->data = p; + evt->len = l; + evtlist->n++; +} + +int testxmlparser(const char * xml, int size) +{ + int r; + struct eventlist evtlist; + struct eventlist evtlistref; + struct xmlparser parser; + evtlist.n = 0; + evtlist.events = malloc(sizeof(struct event)*100); + memset(&parser, 0, sizeof(parser)); + parser.xmlstart = xml; + parser.xmlsize = size; + parser.data = &evtlist; + parser.starteltfunc = startelt; + parser.endeltfunc = endelt; + parser.datafunc = chardata; + parsexml(&parser); + printf("%d events\n", evtlist.n); + /* compare */ + evtlistref.n = sizeof(evtref)/sizeof(struct event); + evtlistref.events = (struct event *)evtref; + r = evtlistcmp(&evtlistref, &evtlist); + free(evtlist.events); + return r; +} + +int main(int argc, char * * argv) +{ + int r; + r = testxmlparser(xmldata, sizeof(xmldata)-1); + if(r) + printf("minixml validation test failed\n"); + return r; +} + diff --git a/miniupnpc/testminixml.c b/miniupnpc/testminixml.c new file mode 100644 index 000000000..3d82527b7 --- /dev/null +++ b/miniupnpc/testminixml.c @@ -0,0 +1,88 @@ +/* $Id: testminixml.c,v 1.6 2006/11/19 22:32:35 nanard Exp $ + * testminixml.c + * test program for the "minixml" functions. + * Author : Thomas Bernard. + */ +#include +#include +#include +#include "minixml.h" +#include "igd_desc_parse.h" + +#ifdef WIN32 +#define NO_BZERO +#endif + +#ifdef NO_BZERO +#define bzero(p, n) memset(p, 0, n) +#endif + +/* ---------------------------------------------------------------------- */ +void printeltname1(void * d, const char * name, int l) +{ + int i; + printf("element "); + for(i=0;i +#include +#include +#ifdef WIN32 +#include +#define snprintf _snprintf +#endif +#include "miniwget.h" +#include "miniupnpc.h" +#include "upnpcommands.h" + +/* protofix() checks if protocol is "UDP" or "TCP" + * returns NULL if not */ +const char * protofix(const char * proto) +{ + static const char proto_tcp[4] = { 'T', 'C', 'P', 0}; + static const char proto_udp[4] = { 'U', 'D', 'P', 0}; + int i, b; + for(i=0, b=1; i<4; i++) + b = b && ( (proto[i] == proto_tcp[i]) + || (proto[i] == (proto_tcp[i] | 32)) ); + if(b) + return proto_tcp; + for(i=0, b=1; i<4; i++) + b = b && ( (proto[i] == proto_udp[i]) + || (proto[i] == (proto_udp[i] | 32)) ); + if(b) + return proto_udp; + return 0; +} + +static void DisplayInfos(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + char connectionType[64]; + char status[64]; + unsigned int uptime; + unsigned int brUp, brDown; + UPNP_GetConnectionTypeInfo(urls->controlURL, + data->servicetype, + connectionType); + if(connectionType[0]) + printf("Connection Type : %s\n", connectionType); + else + printf("GetConnectionTypeInfo failed.\n"); + UPNP_GetStatusInfo(urls->controlURL, data->servicetype, status, &uptime); + printf("Status : %s, uptime=%u\n", status, uptime); + UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->servicetype_CIF, + &brDown, &brUp); + printf("MaxBitRateDown : %u bps MaxBitRateUp %u bps\n", brDown, brUp); +} + +static void GetConnectionStatus(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + unsigned int bytessent, bytesreceived, packetsreceived, packetssent; + DisplayInfos(urls, data); + bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->servicetype_CIF); + bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->servicetype_CIF); + packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->servicetype_CIF); + packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->servicetype_CIF); + printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived); + printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived); +} + +static void ListRedirections(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + int r; + int i = 0; + char index[6]; + char intClient[16]; + char intPort[6]; + char extPort[6]; + char protocol[4]; + char desc[80]; + char enabled[6]; + char rHost[64]; + char duration[16]; + /*unsigned int num=0; + UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num); + printf("PortMappingNumberOfEntries : %u\n", num);*/ + do { + snprintf(index, 6, "%d", i); + rHost[0] = '\0'; enabled[0] = '\0'; + duration[0] = '\0'; desc[0] = '\0'; + extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0'; + r = UPNP_GetGenericPortMappingEntry(urls->controlURL, data->servicetype, + index, + extPort, intClient, intPort, + protocol, desc, enabled, + rHost, duration); + if(r==0) + printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n" + " desc='%s' rHost='%s'\n", + i, protocol, extPort, intClient, intPort, + enabled, duration, + desc, rHost); + i++; + } while(r==0); +} + +/* Test function + * 1 - get connection type + * 2 - get extenal ip address + * 3 - Add port mapping + * 4 - get this port mapping from the IGD */ +static void SetRedirectAndTest(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * iaddr, + const char * iport, + const char * eport, + const char * proto) +{ + char externalIPAddress[16]; + char intClient[16]; + char intPort[6]; + int r; + + if(!iaddr || !iport || !eport || !proto) + { + fprintf(stderr, "Wrong arguments\n"); + return; + } + proto = protofix(proto); + if(!proto) + { + fprintf(stderr, "invalid protocol\n"); + return; + } + + UPNP_GetExternalIPAddress(urls->controlURL, + data->servicetype, + externalIPAddress); + if(externalIPAddress[0]) + printf("ExternalIPAddress = %s\n", externalIPAddress); + else + printf("GetExternalIPAddress failed.\n"); + + r = UPNP_AddPortMapping(urls->controlURL, data->servicetype, + eport, iport, iaddr, 0, proto); + if(r==0) + printf("AddPortMapping(%s, %s, %s) failed\n", eport, iport, iaddr); + + UPNP_GetSpecificPortMappingEntry(urls->controlURL, + data->servicetype, + eport, proto, + intClient, intPort); + if(intClient[0]) + printf("InternalIP:Port = %s:%s\n", intClient, intPort); + else + printf("GetSpecificPortMappingEntry failed.\n"); + + printf("external %s:%s is redirected to internal %s:%s\n", + externalIPAddress, eport, intClient, intPort); +} + +static void +RemoveRedirect(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * eport, + const char * proto) +{ + if(!proto || !eport) + { + fprintf(stderr, "invalid arguments\n"); + return; + } + proto = protofix(proto); + if(!proto) + { + fprintf(stderr, "protocol invalid\n"); + return; + } + UPNP_DeletePortMapping(urls->controlURL, data->servicetype, eport, proto); +} + + +/* sample upnp client program */ +int main(int argc, char ** argv) +{ + char command = 0; + struct UPNPDev * devlist; + char lanaddr[16]; /* my ip address on the LAN */ + int i; + +#ifdef WIN32 + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if(nResult != NO_ERROR) + { + fprintf(stderr, "WSAStartup() failed.\n"); + return -1; + } +#endif + printf("upnpc : miniupnp test client. (c) 2006 Thomas Bernard\n"); + printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n" + "for more information.\n"); + if(argc>=2 && (argv[1][0] == '-')) + command = argv[1][1]; + + if(!command || argc<2 || (command == 'a' && argc<6) + || (command == 'd' && argc<4) + || (command == 'r' && argc<4)) + { + fprintf(stderr, "Usage :\t%s -a ip port external_port protocol\tAdd port redirection\n", argv[0]); + fprintf(stderr, " \t%s -d external_port protocol\tDelete port redirection\n", argv[0]); + fprintf(stderr, " \t%s -s\t\t\t\tGet Connection status\n", argv[0]); + fprintf(stderr, " \t%s -l\t\t\t\tList redirections\n", argv[0]); + fprintf(stderr, " \t%s -r port1 protocol1 [port2 protocol2] [...]\n\t\t\t\tAdd all redirections to the current host\n", argv[0]); + fprintf(stderr, "\nprotocol is UDP or TCP\n"); + return 1; + } + + devlist = upnpDiscover(2000); + if(devlist) + { + struct UPNPDev * device; + struct UPNPUrls urls; + struct IGDdatas data; + printf("List of UPNP devices found on the network :\n"); + for(device = devlist; device; device = device->pNext) + { + printf("\n desc: %s\n st: %s\n", + device->descURL, device->st); + } + putchar('\n'); + if(UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))) + { + printf("Found valid IGD : %s\n", urls.controlURL); + printf("Local LAN ip address : %s\n", lanaddr); + #if 0 + printf("getting \"%s\"\n", urls.ipcondescURL); + descXML = miniwget(urls.ipcondescURL, &descXMLsize); + if(descXML) + { + /*fwrite(descXML, 1, descXMLsize, stdout);*/ + free(descXML); descXML = NULL; + } + #endif + + switch(command) + { + case 'l': + DisplayInfos(&urls, &data); + ListRedirections(&urls, &data); + break; + case 'a': + SetRedirectAndTest(&urls, &data, argv[2], argv[3], argv[4], argv[5]); + break; + case 'd': + RemoveRedirect(&urls, &data, argv[2], argv[3]); + break; + case 's': + GetConnectionStatus(&urls, &data); + break; + case 'r': + for(i=2; i +#include +#include +#include "upnpcommands.h" +#include "miniupnpc.h" + +static unsigned int +my_atoui(const char * s) +{ + return (unsigned int)strtoul(s, NULL, 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"); + if(p) + 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"); + if(p) + 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"); + if(p) + 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"); + if(p) + r = my_atoui(p); + ClearNameValueList(&pdata); + return r; +} + +/* UPNP_GetStatusInfo() call the corresponding UPNP method + * returns the current status and uptime */ +void UPNP_GetStatusInfo(const char * controlURL, + const char * servicetype, + char * status, + unsigned int * uptime) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char * p; + char* up; + + if(!status && !uptime) + return; + + simpleUPnPcommand(-1, controlURL, servicetype, "GetStatusInfo", 0, buffer, &bufsize); + ParseNameValue(buffer, bufsize, &pdata); + /*DisplayNameValueList(buffer, bufsize);*/ + up = GetValueFromNameValueList(&pdata, "NewUptime"); + p = GetValueFromNameValueList(&pdata, "NewConnectionStatus"); + + if(status) + { + if(p){ + strncpy(status, p, 64 ); + status[63] = '\0'; + }else + status[0]= '\0'; + } + + if(uptime){ + if(p) + sscanf(up,"%u",uptime); + else + uptime = 0; + } + + ClearNameValueList(&pdata); +} + +/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method + * returns the connection type */ +void UPNP_GetConnectionTypeInfo(const char * controlURL, + const char * servicetype, + char * connectionType) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char * p; + + if(!connectionType) + return; + + + 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(connectionType) + { + if(p){ + strncpy(connectionType, p, 64 ); + connectionType[63] = '\0'; + } else + connectionType[0] = '\0'; + } + ClearNameValueList(&pdata); +} + +/* 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 */ +void UPNP_GetLinkLayerMaxBitRates(const char * controlURL, const char * servicetype, unsigned int * bitrateDown, unsigned int* bitrateUp) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char * down; + char* up; + + if(!bitrateDown && !bitrateUp) + return; + + /* 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(bitrateDown) + { + if(down) + sscanf(down,"%u",bitrateDown); + else + *bitrateDown = 0; + } + + if(bitrateUp) + { + if(up) + sscanf(up,"%u",bitrateUp); + else + *bitrateUp = 0; + } + ClearNameValueList(&pdata); +} + + +/* 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 */ +void UPNP_GetExternalIPAddress(const char * controlURL, const char * servicetype, char * extIpAdd) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char * p; + + if(!extIpAdd) + return; + + simpleUPnPcommand(-1, controlURL, servicetype, "GetExternalIPAddress", 0, buffer, &bufsize); + /*fd = simpleUPnPcommand(fd, controlURL, data.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'; + }else + extIpAdd[0] = '\0'; + + ClearNameValueList(&pdata); +} + +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) + return 0; + + 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); + /*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "AddPortMapping", AddPortMappingArgs, buffer, &bufsize);*/ + /*DisplayNameValueList(buffer, bufsize);*/ + /*buffer[bufsize] = '\0';*/ + /*puts(buffer);*/ + ParseNameValue(buffer, bufsize, &pdata); + resVal = GetValueFromNameValueList(&pdata, "errorCode"); + ret = resVal?0:1; + /* Do something with resVal if not null ! */ + /*printf("AddPortMapping errorCode = '%s'\n", resVal); */ + free(AddPortMappingArgs); + return ret; +} + +void 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; + + if(!extPort) + return; + + 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);*/ + free(DeletePortMappingArgs); +} + +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 = -1; + 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 = 0; + } + 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'; + } + ClearNameValueList(&pdata); + free(GetPortMappingArgs); + return r; +} + +void UPNP_GetPortMappingNumberOfEntries(const char * controlURL, const char * servicetype, unsigned int * numEntries) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char* p; + 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) + { + sscanf(p,"%u",numEntries); + } + ClearNameValueList(&pdata); +} + +/* 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 */ +void +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; + + if(!intPort && !intClient && !extPort) + return; + + 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(intClient) + { + if(p){ + strncpy(intClient, p, 16); + intClient[15] = '\0'; + }else + intClient[0] = '\0'; + } + + p = GetValueFromNameValueList(&pdata, "NewInternalPort"); + if(intPort) + { + if(p){ + strncpy(intPort, p, 6); + intPort[5] = '\0'; + }else + intPort[0] = '\0'; + } + + ClearNameValueList(&pdata); + free(GetPortMappingArgs); +} + + diff --git a/miniupnpc/upnpcommands.h b/miniupnpc/upnpcommands.h new file mode 100644 index 000000000..ae980a0a3 --- /dev/null +++ b/miniupnpc/upnpcommands.h @@ -0,0 +1,104 @@ +/* $Id: upnpcommands.h,v 1.10 2007/01/29 20:27:24 nanard Exp $ */ +/* Miniupnp project : http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005-2006 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" + +#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); + +LIBSPEC void +UPNP_GetStatusInfo(const char * controlURL, + const char * servicetype, + char * status, + unsigned int * uptime); + +LIBSPEC void +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 */ +LIBSPEC void +UPNP_GetExternalIPAddress(const char * controlURL, + const char * servicetype, + char * extIpAdd); + +LIBSPEC void +UPNP_GetLinkLayerMaxBitRates(const char* controlURL, + const char* servicetype, + unsigned int * bitrateDown, + unsigned int * bitrateUp); + +/* Returns zero if unable to add the port mapping, otherwise non-zero + * to indicate success */ +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); + +LIBSPEC void +UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, + const char * extPort, const char * proto); + +LIBSPEC void +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 */ +LIBSPEC void +UPNP_GetSpecificPortMappingEntry(const char * controlURL, + const char * servicetype, + const char * extPort, + const char * proto, + char * intClient, + char * intPort); + +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 + diff --git a/miniupnpc/upnpreplyparse.c b/miniupnpc/upnpreplyparse.c new file mode 100644 index 000000000..9edc15808 --- /dev/null +++ b/miniupnpc/upnpreplyparse.c @@ -0,0 +1,122 @@ +/* $Id: upnpreplyparse.c,v 1.8 2007/02/07 22:38:09 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 +#include +#include + +#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; +} + +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; +} + +/* debug all-in-one function + * do parsing then display to stdout */ +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); +} + diff --git a/miniupnpc/upnpreplyparse.h b/miniupnpc/upnpreplyparse.h new file mode 100644 index 000000000..beb1686b5 --- /dev/null +++ b/miniupnpc/upnpreplyparse.h @@ -0,0 +1,60 @@ +/* $Id: upnpreplyparse.h,v 1.6 2007/02/07 22:38:09 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(sun) || defined(__sun) || defined(WIN32) +#include "bsdqueue.h" +#else +#include +#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() */ +void +DisplayNameValueList(char * buffer, int bufsize); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/miniupnpc/validateminixml b/miniupnpc/validateminixml new file mode 100644 index 000000000..e69de29bb diff --git a/setup.py b/setup.py index 9ba328dd0..d9c408498 100644 --- a/setup.py +++ b/setup.py @@ -124,6 +124,18 @@ deluge_core = Extension('deluge_core', 'libtorrent/src/kademlia/rpc_manager.cpp', 'libtorrent/src/kademlia/traversal_algorithm.cpp']) +upnp = Extension('upnp', + include_dirs = ['./miniupnpc'], + sources=['src/python-upnp.c', + 'miniupnpc/igd_desc_parse.c', + 'miniupnpc/minisoap.c', + 'miniupnpc/miniupnpc.c', + 'miniupnpc/miniwget.c', + 'miniupnpc/minixml.c', + 'miniupnpc/minixmlvalid.c', + 'miniupnpc/upnpcommands.c', + 'miniupnpc/upnpreplyparse.c']) + # Thanks to Iain Nicol for code to save the location for installed prefix # At runtime, we need to know where we installed the data to. @@ -245,6 +257,6 @@ setup(name=NAME, fullname=FULLNAME, version=VERSION, package_dir = {'deluge': 'src'}, data_files=data, ext_package='deluge', - ext_modules=[deluge_core], + ext_modules=[deluge_core, upnp], cmdclass=cmdclass ) diff --git a/src/python-upnp.c b/src/python-upnp.c new file mode 100644 index 000000000..81b53bd9b --- /dev/null +++ b/src/python-upnp.c @@ -0,0 +1,113 @@ +/* python-miniupnp + * + * Copyright (C) Zach Tibbitts 2007 + * + * + * You may 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. + * + * python-miniupnp 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 python-miniupnp. If not, write to: + * The Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +// upnpDiscover() +static PyObject* +_upnpDiscover(PyObject* self, PyObject* args) +{ + int delay, i, k; + if (!PyArg_ParseTuple(args, "i", &delay)) + return NULL; + struct UPNPDev* discover = upnpDiscover(delay); + struct UPNPDev* next = discover; + // First, we have to find out how many elements are in the list + i = 0; + while(next) + { + i++; + next = next->pNext; + } + // Turn the linked list into a python object + PyObject* list = PyList_New(i); + for(k=0;kdescURL, + "st", discover->st, + "buffer", discover->buffer); + PyList_SET_ITEM(list, k, tmp); + } + return list; +} + +static PyObject* +_miniwget(PyObject* self, PyObject* args) +{ + char* url; + int* size; + if (!PyArg_ParseTuple(args, "si", &url, &size)) + return NULL; + miniwget(url, size); + return NULL; +} + +static PyObject* +_parserootdesc(PyObject* self, PyObject* args) +{ + return NULL; +} + +static PyObject* +_GetUPNPUrls(PyObject* self, PyObject* args) +{ + return NULL; +} + +static PyObject* +_simpleUPnPcommand(PyObject* self, PyObject* args) +{ + int s; + const char * url; + const char * service; + const char * action; + struct UPNParg * arguments; + char * buffer; + int * bufsize; + if (!PyArg_ParseTuple(args, "isssOsi", &s, &url, &service, &action, + &arguments, &buffer, &bufsize)) + return NULL; + int result; + result = simpleUPnPcommand(s, url, service, action, arguments, buffer, bufsize); + return Py_BuildValue("i", result); +} + +static PyMethodDef upnp_methods[] = { + {"simple_upnp_command", (PyCFunction)_simpleUPnPcommand, METH_VARARGS, "."}, + {"discover", (PyCFunction)_upnpDiscover, METH_VARARGS, "."}, + {"miniwget", (PyCFunction)_miniwget, METH_VARARGS, "."}, + {"parse_root_desc", (PyCFunction)_parserootdesc, METH_VARARGS, "."}, + {"get_upnp_urls", (PyCFunction)_GetUPNPUrls, METH_VARARGS, "."}, + {NULL} +}; + +void +initupnp(void) +{ + Py_InitModule("upnp", upnp_methods); +}