Commit c93a549b authored by Reinhard Tartler's avatar Reinhard Tartler

Imported Upstream version 2.2d

This diff is collapsed.
Copyright 2008-2009 Andrej Stepanchuk; Distributed under the GPL v2
Copyright 2009-2010 Howard Chu
Copyright 2009 The Flvstreamer Team
29 April 2010, v2.2d
- add RTMP_Alloc, RTMP_Free APIs
- add optional support for polarssl instead of OpenSSL
- add option to build crypto support without SSL/TLS
- tweak handshake offset checking
- add RTMP set_playlist command
- check for (and fix) broken timestamps in FLV packets
- fix tcUrl and playpath parsing in rtmpsrv and rtmpsuck
- change internal boolean flags to bitmasks
14 April 2010, v2.2c
- internal restructuring, fix #undef CRYPTO builds
- add RTMP_SetupURL, RTMP_SetOpt APIs
- add logging callback
22 March 2010, v2.2b
- fix v2.2a crashes in rtmpsrv/rtmpsuck
- fix v2.2a .swfinfo location on Windows
- fix typo for --auth parameter in manpages
- add FP10 handshake support for rtmpsrv/rtmpsuck
- avoid GNUMake vs BSDMake incompatibilities
- add pkgconfig file for librtmp
- more library cleanup
20 March 2010, v2.2a
- fix C++ compatibility for librtmp
- misc library restructuring
- add client support for tunneling: rtmpt, rtmpte, rtmps
- fix rtmpdump/rtmpgw FLV header dataType
- implement RTMP_Read() and RTMP_Write() to simplify library use
- fix SendPacket timestamps
- add optional support for GnuTLS/Gcrypt instead of OpenSSL
- use $HOMEPATH on Windows instead of $HOME for .swfinfo
4 March 2010, v2.2
- move RTMP code into library librtmp
- relicense RTMP code under LGPL v2.1
- add rtmpdump manpage
- fix AMF_LONG_STRING handling
- more FlashPlayer 10 handshake support
- in rtmpsrv fix Play Start/Stop messages
- rename "streams" program to "rtmpgw"
20 February 2010, v2.1d
- extend .swfinfo file format, add --swfAge rtmpdump parameter
old file should be replaced or manually updated: copy the
"date:" line and rename it to "ctim:"
- fix MacOSX builds - just use "make posix" now for all Unix-derived systems
- more explicit error checks in HTTP_get()
- in rtmpsrv spawn rtmpdump automatically
- fix bug in retry/resume of audio-only streams
- other minor misc. fixes
9 January 2010, v2.1c
- cleanup rtmpsrv output
- fix crash in 2.1b hashswf
- fix parseurl to url-decode PlayPath
- fix parseurl to recognize extensions followed by URL params
- fix Makefile, inadvertently dropped 'v' from version string
- in rtmpdump try Reconnect if ToggleStream doesn't work on timeouts
- in rtmpsuck use chunk-based I/O for better latency
- in rtmpsuck support lists of streams
- in rtmpsuck use raw client connect packet to workaround unsupported features
- support arbitrary AMF data appended to connect requests
4 January 2010, v2.1b
- fix url matching in .swfinfo lookup
- fix resume parsing in rtmpdump
- minor code cleanup (CRYPTO dependencies, logging)
- add getStreamLength recognition to rtmpsrv
- add close processing in rtmpsuck
1 January 2010, v2.1a
- fix socket receive timeouts for WIN32
- add streams description to README
29 December 2009, v2.1
- AMF cleanup: bounds checking for all encoders, moved AMF_EncodeNamed* from rtmp.c
- added SecureToken support
- added automatic SWF hash calculation
- added server-side handshake processing
- added rtmpsrv stub server example
- added rtmpsuck proxy server
- tweaks for logging
- renamed more functions to cleanup namespace for library use
- tweaks for server operation: objectEncoding, chunksize changes
16 December 2009, v2.0
- rewrote everything else in C, reorganized to make it usable again as a library
- fixed more portability bugs
- plugged memory leaks
2 December 2009, v1.9a
- fix auth string typo
- handle FCUnsubscribe message
- don't try retry on live streams
- SIGPIPE portability fix
- remove "not supported" comment for RTMPE
13 November 2009, v1.9
- Handle more signals to reduce risk of unresumable/corrupted partially streamed files
- Fixed >2GB file handling
- Added --hashes option for a hash progress bar instead of byte counter
- Fix to allow win32 to use binary mode on stdout.
- Added auto-unpause for buffer-limited streams
1 November 2009, v1.7
- added --subscribe option for subscribing to a stream
- added --start / --stop options for specifying endpoints of a stream
- added --debug / --quiet / --verbose options for controlling output
- added SOCKS4 support (by Monsieur Video)
- restructured to support auto-restart of timed-out streams
- rewritten byteswapping, works on all platforms
- fixed errors in command / result parsing
- support functions rewritten in C to avoid g++ compiler bugs on ARM
- support for 65600 channels instead of just 64
- fixed signature buffer overruns
17 May 2009, v1.6
- big endian alignment fix, should fix sparc64 and others
- moved timestamp handling into RTMP protocol innings, all packets have
absolute timestamps now, when seeking the stream will start with timestamp 0
even if seeked to a later position!
- fixed a timestamp bug (should fix async audio/video problems)
30 Apr 2009, v1.5a
- fixed host name resolution bug (caused unexpected crashes if DNS resolution
was not available)
- also using the hostname in tcUrl instead of the IP turns out to give much
better results
27 Apr 2009, v1.5
- RTMPE support (tested on Adobe 3.0.2,3.0.3,3.5.1, Wowza)
- SWFVerification (tested on Adobe 3.0.2,3.0.3,3.5.1)
- added AMF3 parsing support (experimental feauture, only some primitives, no references)
- added -o - option which allows the stream to be dumped to stdout
(debug/error messages go to stderr)
- added --live option to enable download of live streams
- added support for (Free)BSD and Mac (untested, so might need more fixing,
especially for PPC/sparc64)
- fixed a bug in url parsing
- added a useful application: streams, it will start a streaming server and
using a request like http://localhost/?r=rtmp://.... you can restream the
content to your player over http
11 Mar 2009, v1.4
- fixed resume bug: when the server switches between audio/video packets and FLV
chunk packets (why should a server want to do that? some actually do!) and rtmpdump
was invoked with --resume the keyframe check prevented rtmpdump from continuing
- fixed endianness
- added win32 and arm support (you can cross-compile it onto your Windows box
or even PDA)
- removed libboost dependency, written a small parser for rtmp urls, but it is
more of a heuristic one since the rtmp urls can be ambigous in some
circumstances. The best way is to supply all prameters using the override
options like --play, --app, etc.
- fixed stream ids (from XBMC tree)
19 Jan 2009, v1.3b
- fixed segfault on Mac OS/BSDdue to times(0)
- Makefile rewritten
16 Jan 2009, v1.3a
- fixed a bug introduced in v1.3 (wrong report bytes count), downloads won't
hang anymore
10 Jan 2009, v1.3
- fixed audio only streams (rtmpdump now recognizes the stream and writes a
correct tag, audio, video, audio+video)
- improved resume function to wait till a the seek is executed by the server.
The server might send playback data before seeking, so we ignore up to e.g. 50
frames and keep waiting for a keyframe with a timestamp of zero.
- nevertheless resuming does not always work since the server sometimes
doesn't resend the keyframe, seeking in flash is unreliable
02 Jan 2009, v1.2a
- fixed non-standard rtmp urls (including characters + < > ; )
- added small script get_hulu which can download streams (US only)
(many thanks to Richard Ablewhite for the help with
01 Jan 2009, v1.2:
- fixed FLV streams (support for resuming extended)
- fixed hanging download at the end
- several minor bugfixes
- changed parameter behaviour: not supplied parameters are omitted from the
connect packet, --auth is introduced (was automatically obtained from url
before, but it is possible to have an auth in the tcurl/rtmp url only without
an additional encoded string in the connect packet)
28 Dec 2008, v1.1a:
- fixed warnings, added -Wall to Makefile
28 Dec 2008, v1.1:
- fixed stucking downloads (the buffer time is set to the duration now,
so the server doesn't wait till the buffer is emptied
- added a --resume option to coninue incomplete downloads
- added support for AMF_DATE (experimental, no stream to test so far)
- fixed AMF parsing and several small bugs (works on 64bit platforms now)
24 Dec 2008, v1.0:
- First release
LIB_GNUTLS=-lgnutls -lgcrypt
LIB_OPENSSL=-lssl -lcrypto
INCRTMP=librtmp/rtmp_sys.h librtmp/rtmp.h librtmp/log.h librtmp/amf.h
@echo 'use "make posix" for a native Linux/Unix build, or'
@echo ' "make mingw" for a MinGW32 build'
@echo 'use commandline overrides if you want anything else'
progs: rtmpdump rtmpgw rtmpsrv rtmpsuck
posix linux unix osx:
@$(MAKE) CROSS_COMPILE=mingw32- LIBS="$(LIBS) -lws2_32 -lwinmm -lgdi32" THREADLIB= EXT=.exe $(MAKEFLAGS) progs
@$(MAKE) XCFLAGS=-static XLDFLAGS="-static-libgcc -static" EXT=.exe $(MAKEFLAGS) progs
@$(MAKE) CROSS_COMPILE=armv7a-angstrom-linux-gnueabi- INC=-I$(STAGING)/usr/include $(MAKEFLAGS) progs
rm -f *.o rtmpdump$(EXT) rtmpgw$(EXT) rtmpsrv$(EXT) rtmpsuck$(EXT)
@cd librtmp; $(MAKE) clean
@cd librtmp; $(MAKE) $(MF) all
# note: $^ is GNU Make's equivalent to BSD $>
# we use both since either make will ignore the one it doesn't recognize
rtmpdump: rtmpdump.o $(LIBRTMP)
$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(LIBS)
rtmpsrv: rtmpsrv.o thread.o $(LIBRTMP)
$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS)
rtmpsuck: rtmpsuck.o thread.o $(LIBRTMP)
$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS)
rtmpgw: rtmpgw.o thread.o $(LIBRTMP)
$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS)
rtmpgw.o: rtmpgw.c $(INCRTMP) Makefile
rtmpdump.o: rtmpdump.c $(INCRTMP) Makefile
rtmpsrv.o: rtmpsrv.c $(INCRTMP) Makefile
rtmpsuck.o: rtmpsuck.c $(INCRTMP) Makefile
thread.o: thread.c thread.h
RTMP Dump v2.2d
(C) 2009 Andrej Stepanchuk
(C) 2009-2010 Howard Chu
(C) 2010 2a665470ced7adb7156fcef47f8199a6371c117b8a79e399a2771e0b36384090
License: GPLv2
To compile type "make" with platform name, e.g.
$ make posix
for Linux, MacOSX, Unix, etc. or
$ make mingw
for Windows.
You can cross-compile for other platforms using
$ make cross
but you may need to override the CROSS_COMPILE and INC variables, e.g.
$ make arm CROSS_COMPILE=arm-none-linux- INC=-I/my/cross/includes
Please read the Makefile to see what other make variables are used.
This code also requires you to have OpenSSL and zlib installed. You may
optionally use GnuTLS or polarssl instead of OpenSSL if desired. You may
also build with just rtmpe support, and no rtmps/https support, by
specifying -DNO_SSL in the XDEF macro, e.g.
$ make posix XDEF=-DNO_SSL
You may also turn off all crypto support if desired
$ make posix CRYPTO=
Note that if using OpenSSL, you must have version 0.9.8 or newer.
Credit goes to team boxee for the XBMC RTMP code originally used in RTMPDumper.
The current code is based on the XBMC code but rewritten in C by Howard Chu.
SWF Verification
Note: these instructions for manually generating the SWFVerification
info are provided only for historical documentation. The software can now
generate this info automatically, so it is no longer necessary to
run the commands described here. Just use the -W (--swfVfy) option
to perform automatic SWFVerification.
Download the swf player you want to use for SWFVerification, unzip it using
$ flasm -x file.swf
It will show the decompressed filesize, use it for --swfsize
Now generate the hash
$ openssl sha -sha256 -hmac "Genuine Adobe Flash Player 001" file.swf
and use the --swfhash "01234..." option to pass it.
e.g. $ ./rtmpdump --swfhash "123456..." --swfsize 987...
Connect Parameters
Some servers expect additional custom parameters to be attached to the
RTMP connect request. The "--auth" option handles a specific case, where
a boolean TRUE followed by the given string are added to the request.
Other servers may require completely different parameters, so the new
"--conn" option has been added. This option can be set multiple times
on the command line, adding one parameter each time.
The argument to the option must take the form <type> : <value> where
type can be B for boolean, S for string, N for number, and O for object.
For booleans the value must be 0 or 1. Also, for objects the value must
be 1 to start a new object, or 0 to end the current object.
--conn B:0 --conn S:hello --conn N:3.14159
Named parameters can be specified by prefixing 'N' to the type. Then the
name should come next, and finally the value:
--conn NB:myflag:1 --conn NS:category:something --conn NN:pi:3.14159
Objects may be added sequentially:
-C O:1 -C NB:flag:1 -C NS:status:success -C O:0 -C O:1 -C NN:time:12.30 -C O:0
or nested:
-C O:1 -C NS:code:hello -C NO:extra:1 -C NS:data:stuff -C O:0 -C O:0
Building OpenSSL 0.9.8k
./Configure -DL_ENDIAN --prefix=`pwd`/armlibs linux-generic32
Then replace gcc, cc, ar, ranlib in Makefile and crypto/Makefile by arm-linux-* variants and use make && make install_sw
Try ./Configure mingw --prefix=`pwd`/win32libs -DL_ENDIAN -DOPENSSL_NO_HW
Replace gcc, cc, ... by mingw32-* variants in Makefile and crypto/Makefile
make && make install_sw
OpenSSL cross-compiling can be a difficult beast.
Precompiled OpenSSL binaries for Windows are available on
If you're just running a pre-built Windows rtmpdump binary, then all you
need is the "Light" installer. If you want to compile rtmpdump yourself,
you'll need the full installer.
Example Servers
Three different types of servers are also present in this distribution:
rtmpsrv - a stub server
rtmpsuck - a transparent proxy
rtmpgw - an RTMP to HTTP gateway
rtmpsrv - Note that this is very incomplete code, and I haven't yet decided
whether or not to finish it. It is useful for obtaining all the parameters
that a real Flash client would send to an RTMP server, so that they can be
used with rtmpdump. The current version now invokes rtmpdump automatically
after parsing a client request.
rtmpsuck - proxy server. See below...
All you need to do is redirect your Flash clients to the machine running this
server and it will dump out all the connect / play parameters that the Flash
client sent. The simplest way to cause the redirect is by editing /etc/hosts
when you know the hostname of the RTMP server, and point it to localhost while
running rtmpsrv on your machine. (This approach should work on any OS; on
Windows you would edit %SystemRoot%\system32\drivers\etc\hosts.)
On Linux you can also use iptables to redirect all outbound RTMP traffic. You
need to be running as root in order to use the iptables command.
In my original plan I would have the transparent proxy running as a special
user (e.g. user "proxy"), and regular Flash clients running as any other user.
In that case the proxy would make the connection to the real RTMP server. The
iptables rule would look like this:
iptables -t nat -A OUTPUT -p tcp --dport 1935 -m owner \! --uid-owner proxy \
A rule like the above will be needed to use rtmpsuck. Note that you should
replace "proxy" in the above command with an account that actually exists
on your machine.
Using it in this mode takes advantage of the Linux support for IP redirects;
in particular it uses a special getsockopt() call to retrieve the original
destination address of the connection. That way the proxy can create the
real outbound connection without any other help from the user. The equivalent
functionality may exist on other OSs but needs more investigation.
(Based on reading the BSD ipfw manpage, this rule ought to work on BSD:
ipfw add 40 fwd,1935 tcp from any to any 1935 not uid proxy
Some confirmation from any BSD users would be nice.)
(We have a solution for Windows based on a TDI driver; this is known to
work on Win2K and WinXP but is assumed to not work on Vista or Win7 as the
TDI is no longer used on those OS versions. Also, none of the known
solutions are available as freeware.)
The rtmpsuck command has only one option: "-z" to turn on debug logging.
It listens on port 1935 for RTMP sessions, but you can also redirect other
ports to it as needed (read the iptables docs). It first performs an RTMP
handshake with the client, then waits for the client to send a connect
request. It parses and prints the connect parameters, then makes an
outbound connection to the real RTMP server. It performs an RTMP handshake
with that server, forwards the connect request, and from that point on it
just relays packets back and forth between the two endpoints.
It also checks for a few packets that it treats specially: a play packet
from the client will get parsed so that the playpath can be displayed. It
also handles SWF Verification requests from the server, without forwarding
them to the client. (There would be no point, since the response is tied to
each session's handshake.)
Once the play command is processed, all subsequent audio/video data received
from the server will be written to a file, as well as being delivered back
to the client.
The point of all this, instead of just using a sniffer, is that since rtmpsuck
has performed real handshakes with both the client and the server, it can
negotiate whatever encryption keys are needed and so record the unencrypted
rtmpgw - HTTP gateway: this is an HTTP server that accepts requests that
consist of rtmpdump parameters. It then connects to the specified RTMP
server and returns the retrieved data in the HTTP response. The only valid
HTTP request is "GET /" but additional options can be provided in normal
URL-encoded fashion. E.g.
GET /?r=rtmp:%2f%2fserver%2fmyapp&y=somefile HTTP/1.0
is equivalent the rtmpdump parameters "-r rtmp://server/myapp -y somefile".
Note that only the shortform (single letter) rtmpdump options are supported.
This diff is collapsed.
all: librtmp.a
rm -f *.o *.a
librtmp.a: rtmp.o log.o amf.o hashswf.o parseurl.o
$(AR) rs $@ $?
log.o: log.c log.h Makefile
rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile
amf.o: amf.c amf.h bytes.h log.h Makefile
hashswf.o: hashswf.c http.h rtmp.h rtmp_sys.h Makefile
parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile
librtmp.pc: Makefile
sed -e "s;@prefix@;$(prefix);" -e "s;@VERSION@;$(VERSION);" \
-e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" > $@
install: librtmp.a librtmp.pc
-mkdir -p $(INCDIR) $(DESTDIR)$(prefix)/lib/pkgconfig
cp amf.h http.h log.h rtmp.h $(INCDIR)
cp librtmp.a $(DESTDIR)$(prefix)/lib
cp librtmp.pc $(DESTDIR)$(prefix)/lib/pkgconfig
This diff is collapsed.
#ifndef __AMF_H__
#define __AMF_H__
* Copyright (C) 2005-2008 Team XBMC
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
* This file is part of librtmp.
* librtmp is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1,
* or (at your option) any later version.
* librtmp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with librtmp see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
typedef enum
AMF_MOVIECLIP, /* reserved, not used */
AMF_RECORDSET, /* reserved, not used */
AMF_AVMPLUS, /* switch to AMF3 */
} AMFDataType;
typedef enum
} AMF3DataType;
typedef struct AVal
char *av_val;
int av_len;
} AVal;
#define AVC(str) {str,sizeof(str)-1}
#define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))
struct AMFObjectProperty;
typedef struct AMFObject
int o_num;
struct AMFObjectProperty *o_props;
} AMFObject;
typedef struct AMFObjectProperty
AVal p_name;
AMFDataType p_type;
double p_number;
AVal p_aval;
AMFObject p_object;
} p_vu;
int16_t p_UTCoffset;
} AMFObjectProperty;
char *AMF_EncodeString(char *output, char *outend, const AVal * str);
char *AMF_EncodeNumber(char *output, char *outend, double dVal);
char *AMF_EncodeInt16(char *output, char *outend, short nVal);
char *AMF_EncodeInt24(char *output, char *outend, int nVal);
char *AMF_EncodeInt32(char *output, char *outend, int nVal);
char *AMF_EncodeBoolean(char *output, char *outend, bool bVal);
/* Shortcuts for AMFProp_Encode */
char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, bool bVal);
unsigned short AMF_DecodeInt16(const char *data);
unsigned int AMF_DecodeInt24(const char *data);
unsigned int AMF_DecodeInt32(const char *data);
void AMF_DecodeString(const char *data, AVal * str);
void AMF_DecodeLongString(const char *data, AVal * str);
bool AMF_DecodeBoolean(const char *data);
double AMF_DecodeNumber(const char *data);
char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize,
bool bDecodeName);
int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,
int nArrayLen, bool bDecodeName);
int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize,
bool bDecodeName);
void AMF_Dump(AMFObject * obj);
void AMF_Reset(AMFObject * obj);
void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
int AMF_CountProp(AMFObject * obj);
AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name,
int nIndex);
AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
void AMFProp_SetNumber(AMFObjectProperty * prop, double dval);
void AMFProp_SetBoolean(AMFObjectProperty * prop, bool bflag);
void AMFProp_SetString(AMFObjectProperty * prop, AVal * str);
void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj);
void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
double AMFProp_GetNumber(AMFObjectProperty * prop);
bool AMFProp_GetBoolean(AMFObjectProperty * prop);
void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
bool AMFProp_IsValid(AMFObjectProperty * prop);
char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer,
int nSize, bool bDecodeName);
int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer,
int nSize, bool bDecodeName);
void AMFProp_Dump(AMFObjectProperty * prop);
void AMFProp_Reset(AMFObjectProperty * prop);
typedef struct AMF3ClassDef
AVal cd_name;
char cd_externalizable;
char cd_dynamic;
int cd_num;
AVal *cd_props;
} AMF3ClassDef;
void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop);
AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx);
#ifdef __cplusplus
#endif /* __AMF_H__ */
* Copyright (C) 2005-2008 Team XBMC
* Copyright (C) 2008-2009 Andrej Stepanchuk
* Copyright (C) 2009-2010 Howard Chu
* This file is part of librtmp.
* librtmp is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1,
* or (at your option) any later version.
* librtmp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License