diff options
Diffstat (limited to 'kopete/protocols/jabber/jingle/libjingle')
283 files changed, 45768 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jingle/libjingle/AUTHORS b/kopete/protocols/jabber/jingle/libjingle/AUTHORS new file mode 100644 index 00000000..e491a9e7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/AUTHORS @@ -0,0 +1 @@ +Google Inc. diff --git a/kopete/protocols/jabber/jingle/libjingle/COPYING b/kopete/protocols/jabber/jingle/libjingle/COPYING new file mode 100644 index 00000000..d58182b1 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/COPYING @@ -0,0 +1,25 @@ +Copyright (c) 2004--2005, Google Inc. +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/kopete/protocols/jabber/jingle/libjingle/ChangeLog b/kopete/protocols/jabber/jingle/libjingle/ChangeLog new file mode 100644 index 00000000..6d15ac52 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/ChangeLog @@ -0,0 +1,4 @@ +Libjingle + +0.1.0 - Dec 15 2005 + - Initial release diff --git a/kopete/protocols/jabber/jingle/libjingle/INSTALL b/kopete/protocols/jabber/jingle/libjingle/INSTALL new file mode 100644 index 00000000..a4b34144 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/INSTALL @@ -0,0 +1,229 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/kopete/protocols/jabber/jingle/libjingle/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/Makefile.am new file mode 100644 index 00000000..164f7058 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS=talk + +dist-hook: + sed -i -f talk/sanitize.sed `find $(distdir) -type f`
\ No newline at end of file diff --git a/kopete/protocols/jabber/jingle/libjingle/NEWS b/kopete/protocols/jabber/jingle/libjingle/NEWS new file mode 100644 index 00000000..1694c754 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/NEWS @@ -0,0 +1 @@ +* Initial source release diff --git a/kopete/protocols/jabber/jingle/libjingle/README b/kopete/protocols/jabber/jingle/libjingle/README new file mode 100644 index 00000000..ec130b36 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/README @@ -0,0 +1,59 @@ +Libjingle
+
+Libjingle is a set of components provided by Google to interoperate with Google
+Talk's peer-to-peer and voice capabilities. This package will create several
+static libraries you may link to your project as needed.
+
+-talk - No source files in talk/, just these subdirectories
+|-base - Contains basic low-level portable utility functions for
+| things like threads and sockets
+|-p2p - The P2P stack
+ |-base - Base p2p functionality
+ |-client - Hooks to tie it into XMPP
+|-session - Signaling
+ |-phone - Signaling code specific to making phone calls
+|-third_party - Components that aren't ours
+ |-mediastreamer - Media components for dealing with sound hardware and
+ | voice codecs
+|-xmllite - XML parser
+|-xmpp - XMPP engine
+
+In addition, this package contains two examples in talk/examples which
+illustrate the basic concepts of how the provided classes work.
+
+The xmllite component of libjingle depends on expat. You can download expat
+from http://expat.sourceforge.net/.
+
+mediastreamer, the media components used by the example applications depend on
+the oRTP and iLBC components from linphone, which can be found at
+http://www.linphone.org. Linphone, in turn depends on GLib, which can be found
+at http://www.gtk.org. This GLib dependency should be removed in future
+releases.
+
+Building Libjingle
+
+Once the dependencies are installed, run ./configure. ./configure will return
+an error if it failed to locate the proper dependencies. If ./configure
+succeeds, run 'make' to build the components and examples.
+
+When the build is complete, you can run the call example from
+talk/examples/call. This will ask you for your GMail username and your GMail
+auth cookie. Your GMail auth cookie is the GX cookie from mail.google.com
+found in your web browser.
+
+Relay Server
+
+Libjingle will also build a relay server that may be used to relay traffic
+when a direct peer-to-peer connection could not be established. The relay
+server will build in talk/p2p/base/relayserver and will listen on UDP
+ports 5000 and 5001. See the Libjingle Developer Guide at
+http://code.google.com/apis/talk/index.html for information about configuring
+a client to use this relay server.
+
+STUN Server
+
+Lastly, Libjingle builds a STUN server which implements the STUN protocol for
+Simple Traversal of UDP over NAT. The STUN server is built as
+talk/p2p/base/stunserver and listens on UDP port 7000. See the Libjingle
+Developer Guide at http://code.google.com/apis/talk/index.html for information
+about configuring a client to use this STUN server.
diff --git a/kopete/protocols/jabber/jingle/libjingle/libjingle.pro b/kopete/protocols/jabber/jingle/libjingle/libjingle.pro new file mode 100644 index 00000000..53c8e293 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/libjingle.pro @@ -0,0 +1,142 @@ +TEMPLATE = lib +CONFIG += staticlib +CONFIG += debug + +target.extra = true + +exists(../../conf.pri) { + include(../../conf.pri) +} + +JINGLE_CPP = . +INCLUDEPATH += $$JINGLE_CPP $$JINGLE_CPP/talk/third_party/mediastreamer +DEFINES += POSIX +OBJECTS_DIR = $$JINGLE_CPP/.obj + +# Base +SOURCES += \ + $$JINGLE_CPP/talk/base/asyncpacketsocket.cc \ + $$JINGLE_CPP/talk/base/asynctcpsocket.cc \ + $$JINGLE_CPP/talk/base/asyncudpsocket.cc \ + $$JINGLE_CPP/talk/base/base64.cc \ + $$JINGLE_CPP/talk/base/bytebuffer.cc \ + $$JINGLE_CPP/talk/base/md5c.c \ + $$JINGLE_CPP/talk/base/messagequeue.cc \ + $$JINGLE_CPP/talk/base/network.cc \ + $$JINGLE_CPP/talk/base/physicalsocketserver.cc \ + $$JINGLE_CPP/talk/base/socketadapters.cc \ + $$JINGLE_CPP/talk/base/socketaddress.cc \ + $$JINGLE_CPP/talk/base/task.cc \ + $$JINGLE_CPP/talk/base/taskrunner.cc \ + $$JINGLE_CPP/talk/base/thread.cc \ + $$JINGLE_CPP/talk/base/time.cc + +# Not needed ? +#$$JINGLE_CPP/talk/base/socketaddresspair.cc \ +#$$JINGLE_CPP/talk/base/host.cc \ + +# P2P Base +SOURCES += \ + $$JINGLE_CPP/talk/p2p/base/helpers.cc \ + $$JINGLE_CPP/talk/p2p/base/p2psocket.cc \ + $$JINGLE_CPP/talk/p2p/base/port.cc \ + $$JINGLE_CPP/talk/p2p/base/relayport.cc \ + $$JINGLE_CPP/talk/p2p/base/session.cc \ + $$JINGLE_CPP/talk/p2p/base/sessionmanager.cc \ + $$JINGLE_CPP/talk/p2p/base/socketmanager.cc \ + $$JINGLE_CPP/talk/p2p/base/stun.cc \ + $$JINGLE_CPP/talk/p2p/base/stunport.cc \ + $$JINGLE_CPP/talk/p2p/base/stunrequest.cc \ + $$JINGLE_CPP/talk/p2p/base/tcpport.cc \ + $$JINGLE_CPP/talk/p2p/base/udpport.cc + +# P2P Client +SOURCES += \ + $$JINGLE_CPP/talk/p2p/client/basicportallocator.cc \ + $$JINGLE_CPP/talk/p2p/client/sessionclient.cc \ + $$JINGLE_CPP/talk/p2p/client/socketmonitor.cc + + +# XMLLite +SOURCES += \ + $$JINGLE_CPP/talk/xmllite/qname.cc \ + $$JINGLE_CPP/talk/xmllite/xmlbuilder.cc \ + $$JINGLE_CPP/talk/xmllite/xmlconstants.cc \ + $$JINGLE_CPP/talk/xmllite/xmlelement.cc \ + $$JINGLE_CPP/talk/xmllite/xmlnsstack.cc \ + $$JINGLE_CPP/talk/xmllite/xmlparser.cc \ + $$JINGLE_CPP/talk/xmllite/xmlprinter.cc + +# XMPP +SOURCES += \ + $$JINGLE_CPP/talk/xmpp/constants.cc \ + $$JINGLE_CPP/talk/xmpp/jid.cc \ + $$JINGLE_CPP/talk/xmpp/saslmechanism.cc \ + $$JINGLE_CPP/talk/xmpp/xmppclient.cc \ + $$JINGLE_CPP/talk/xmpp/xmppengineimpl.cc \ + $$JINGLE_CPP/talk/xmpp/xmppengineimpl_iq.cc \ + $$JINGLE_CPP/talk/xmpp/xmpplogintask.cc \ + $$JINGLE_CPP/talk/xmpp/xmppstanzaparser.cc \ + $$JINGLE_CPP/talk/xmpp/xmpptask.cc + +# Session +SOURCES += \ + $$JINGLE_CPP/talk/session/phone/call.cc \ + $$JINGLE_CPP/talk/session/phone/audiomonitor.cc \ + $$JINGLE_CPP/talk/session/phone/phonesessionclient.cc \ + $$JINGLE_CPP/talk/session/phone/channelmanager.cc \ + $$JINGLE_CPP/talk/session/phone/linphonemediaengine.cc \ + $$JINGLE_CPP/talk/session/phone/voicechannel.cc + +#contains(DEFINES, HAVE_PORTAUDIO) { +# SOURCES += \ +# $$JINGLE_CPP/talk/session/phone/portaudiomediaengine.cc +#} + + +# Mediastreamer +SOURCES += \ + $$JINGLE_CPP/talk/third_party/mediastreamer/audiostream.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/ms.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msAlawdec.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msAlawenc.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msbuffer.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mscodec.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mscopy.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msfdispatcher.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msfifo.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msfilter.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msilbcdec.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msilbcenc.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msMUlawdec.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msMUlawenc.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msnosync.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msossread.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msosswrite.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msqdispatcher.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msqueue.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msread.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msringplayer.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msrtprecv.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msrtpsend.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mssoundread.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mssoundwrite.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msspeexdec.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/msspeexenc.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mssync.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mstimer.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/mswrite.c \ + $$JINGLE_CPP/talk/third_party/mediastreamer/sndcard.c + +contains(DEFINES, HAVE_ALSA_ASOUNDLIB_H) { + SOURCES += $$JINGLE_CPP/talk/third_party/mediastreamer/alsacard.c +} + +contains(DEFINES, HAVE_PORTAUDIO) { + SOURCES += $$JINGLE_CPP/talk/third_party/mediastreamer/portaudiocard.c +} + +#$$JINGLE_CPP/talk/third_party/mediastreamer/osscard.c \ +#$$JINGLE_CPP/talk/third_party/mediastreamer/jackcard.c \ +#$$JINGLE_CPP/talk/third_party/mediastreamer/hpuxsndcard.c \ + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am new file mode 100644 index 00000000..2a845dc0 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=base p2p xmllite xmpp session third_party diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am new file mode 100644 index 00000000..2921049a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am @@ -0,0 +1,62 @@ +## Does not compile with final +KDE_OPTIONS = nofinal + +libcricketbase_la_SOURCES = socketaddress.cc \ + jtime.cc \ + asyncudpsocket.cc \ + messagequeue.cc \ + thread.cc \ + physicalsocketserver.cc \ + bytebuffer.cc \ + asyncpacketsocket.cc \ + network.cc \ + asynctcpsocket.cc \ + socketadapters.cc \ + md5c.c \ + base64.cc \ + task.cc \ + taskrunner.cc \ + host.cc \ + socketaddresspair.cc + +noinst_HEADERS = asyncfile.h \ + common.h \ + asyncpacketsocket.h \ + socketfactory.h \ + asyncsocket.h \ + socket.h \ + asynctcpsocket.h \ + linked_ptr.h \ + asyncudpsocket.h \ + logging.h \ + socketserver.h \ + base64.h \ + md5.h \ + stl_decl.h \ + basicdefs.h \ + messagequeue.h \ + basictypes.h \ + stringutils.h \ + bytebuffer.h \ + task.h \ + byteorder.h \ + taskrunner.h \ + criticalsection.h \ + network.h \ + thread.h \ + jtime.h \ + physicalsocketserver.h \ + proxyinfo.h \ + host.h \ + scoped_ptr.h \ + sigslot.h \ + winping.h \ + socketadapters.h \ + socketaddress.h \ + host.h \ + socketaddresspair.h + +AM_CPPFLAGS = -DPOSIX -I$(srcdir)/../.. -I$(top_builddir) $(all_includes) +noinst_LTLIBRARIES = libcricketbase.la +DEFAULT_INCLUDES = -I$(srcdir)/../.. + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h new file mode 100644 index 00000000..0faac9ea --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h @@ -0,0 +1,56 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CRICKET_BASE_ASYNCFILEH_H__ +#define CRICKET_BASE_ASYNCFILEH_H__ + +#include "talk/base/sigslot.h" + +namespace cricket { + +// Provides the ability to perform file I/O asynchronously. +// TODO: Create a common base class with AsyncSocket. +class AsyncFile { +public: + virtual ~AsyncFile() {} + + // Determines whether the file will receive read events. + virtual bool readable() = 0; + virtual void set_readable(bool value) = 0; + + // Determines whether the file will receive write events. + virtual bool writable() = 0; + virtual void set_writable(bool value) = 0; + + sigslot::signal1<AsyncFile*> SignalReadEvent; + sigslot::signal1<AsyncFile*> SignalWriteEvent; + sigslot::signal2<AsyncFile*,int> SignalCloseEvent; +}; + +} // namespace cricket + +#endif // CRICKET_BASE_ASYNCFILEH_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc new file mode 100644 index 00000000..10cfa617 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc @@ -0,0 +1,83 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asyncpacketsocket.h" + +namespace cricket { + +AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) { +} + +AsyncPacketSocket::~AsyncPacketSocket() { + delete socket_; +} + +SocketAddress AsyncPacketSocket::GetLocalAddress() const { + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncPacketSocket::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + +int AsyncPacketSocket::Bind(const SocketAddress& addr) { + return socket_->Bind(addr); +} + +int AsyncPacketSocket::Connect(const SocketAddress& addr) { + return socket_->Connect(addr); +} + +int AsyncPacketSocket::Send(const void *pv, size_t cb) { + return socket_->Send(pv, cb); +} + +int AsyncPacketSocket::SendTo( + const void *pv, size_t cb, const SocketAddress& addr) { + return socket_->SendTo(pv, cb, addr); +} + +int AsyncPacketSocket::Close() { + return socket_->Close(); +} + +int AsyncPacketSocket::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncPacketSocket::GetError() const { + return socket_->GetError(); +} + +void AsyncPacketSocket::SetError(int error) { + return socket_->SetError(error); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h new file mode 100644 index 00000000..b5119c8d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ASYNCPACKETSOCKET_H__ +#define __ASYNCPACKETSOCKET_H__ + +#include "talk/base/asyncsocket.h" + +namespace cricket { + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncPacketSocket : public sigslot::has_slots<> { +public: + AsyncPacketSocket(AsyncSocket* socket); + virtual ~AsyncPacketSocket(); + + // Relevant socket methods: + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Bind(const SocketAddress& addr); + virtual int Connect(const SocketAddress& addr); + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Close(); + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); + + // Emitted each time a packet is read. + sigslot::signal4<const char*, size_t, const SocketAddress&, AsyncPacketSocket*> SignalReadPacket; + +protected: + AsyncSocket* socket_; +}; + +} // namespace cricket + +#endif // __ASYNCPACKETSOCKET_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h new file mode 100644 index 00000000..d6404232 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h @@ -0,0 +1,91 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ASYNCSOCKET_H__ +#define __ASYNCSOCKET_H__ + +#include "talk/base/sigslot.h" +#include "talk/base/socket.h" + +namespace cricket { + +// Provides the ability to perform socket I/O asynchronously. +class AsyncSocket : public Socket, public sigslot::has_slots<> { +public: + virtual ~AsyncSocket() {} + + sigslot::signal1<AsyncSocket*> SignalReadEvent; // ready to read + sigslot::signal1<AsyncSocket*> SignalWriteEvent; // ready to write + sigslot::signal1<AsyncSocket*> SignalConnectEvent; // connected + sigslot::signal2<AsyncSocket*,int> SignalCloseEvent; // closed + // TODO: error +}; + +class AsyncSocketAdapter : public AsyncSocket { +public: + AsyncSocketAdapter(Socket * socket) : socket_(socket) { + } + AsyncSocketAdapter(AsyncSocket * socket) : socket_(socket) { + socket->SignalConnectEvent.connect(this, &AsyncSocketAdapter::OnConnectEvent); + socket->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent); + socket->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent); + socket->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent); + } + virtual ~AsyncSocketAdapter() { delete socket_; } + + virtual SocketAddress GetLocalAddress() const { return socket_->GetLocalAddress(); } + virtual SocketAddress GetRemoteAddress() const { return socket_->GetRemoteAddress(); } + + virtual int Bind(const SocketAddress& addr) { return socket_->Bind(addr); } + virtual int Connect(const SocketAddress& addr) { return socket_->Connect(addr); } + virtual int Send(const void *pv, size_t cb) { return socket_->Send(pv, cb); } + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { return socket_->SendTo(pv, cb, addr); } + virtual int Recv(void *pv, size_t cb) { return socket_->Recv(pv, cb); } + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { return socket_->RecvFrom(pv, cb, paddr); } + virtual int Listen(int backlog) { return socket_->Listen(backlog); } + virtual Socket *Accept(SocketAddress *paddr) { return socket_->Accept(paddr); } + virtual int Close() { return socket_->Close(); } + virtual int GetError() const { return socket_->GetError(); } + virtual void SetError(int error) { return socket_->SetError(error); } + + virtual ConnState GetState() const { return socket_->GetState(); } + + virtual int EstimateMTU(uint16* mtu) { return socket_->EstimateMTU(mtu); } + virtual int SetOption(Option opt, int value) { return socket_->SetOption(opt, value); } + +protected: + virtual void OnConnectEvent(AsyncSocket * socket) { SignalConnectEvent(this); } + virtual void OnReadEvent(AsyncSocket * socket) { SignalReadEvent(this); } + virtual void OnWriteEvent(AsyncSocket * socket) { SignalWriteEvent(this); } + virtual void OnCloseEvent(AsyncSocket * socket, int err) { SignalCloseEvent(this, err); } + + Socket * socket_; +}; + +} // namespace cricket + +#endif // __ASYNCSOCKET_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc new file mode 100644 index 00000000..6d4697a6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc @@ -0,0 +1,197 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asynctcpsocket.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +const size_t MAX_PACKET_SIZE = 64 * 1024; + +typedef uint16 PacketLength; +const size_t PKT_LEN_SIZE = sizeof(PacketLength); + +const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE; + +AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), outpos_(0) { + inbuf_ = new char[insize_]; + outbuf_ = new char[outsize_]; + + ASSERT(socket_ != NULL); + socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent); +} + +AsyncTCPSocket::~AsyncTCPSocket() { + delete [] inbuf_; + delete [] outbuf_; +} + +int AsyncTCPSocket::Send(const void *pv, size_t cb) { + if (cb > MAX_PACKET_SIZE) { + socket_->SetError(EMSGSIZE); + return -1; + } + + // If we are blocking on send, then silently drop this packet + if (outpos_) + return static_cast<int>(cb); + + PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb)); + memcpy(outbuf_, &pkt_len, PKT_LEN_SIZE); + memcpy(outbuf_ + PKT_LEN_SIZE, pv, cb); + outpos_ = PKT_LEN_SIZE + cb; + + int res = Flush(); + if (res <= 0) { + // drop packet if we made no progress + outpos_ = 0; + return res; + } + + // We claim to have sent the whole thing, even if we only sent partial + return static_cast<int>(cb); +} + +int AsyncTCPSocket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + if (addr == GetRemoteAddress()) + return Send(pv, cb); + + ASSERT(false); + socket_->SetError(ENOTCONN); + return -1; +} + +int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) { + if (outpos_ + cb > outsize_) { + socket_->SetError(EMSGSIZE); + return -1; + } + + memcpy(outbuf_ + outpos_, pv, cb); + outpos_ += cb; + + return Flush(); +} + +void AsyncTCPSocket::ProcessInput(char * data, size_t& len) { + SocketAddress remote_addr(GetRemoteAddress()); + + while (true) { + if (len < PKT_LEN_SIZE) + return; + + PacketLength pkt_len; + memcpy(&pkt_len, data, PKT_LEN_SIZE); + pkt_len = NetworkToHost16(pkt_len); + + if (len < PKT_LEN_SIZE + pkt_len) + return; + + SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this); + + len -= PKT_LEN_SIZE + pkt_len; + if (len > 0) { + memmove(data, data + PKT_LEN_SIZE + pkt_len, len); + } + } +} + +int AsyncTCPSocket::Flush() { + int res = socket_->Send(outbuf_, outpos_); + if (res <= 0) { + return res; + } + if (static_cast<size_t>(res) <= outpos_) { + outpos_ -= res; + } else { + ASSERT(false); + return -1; + } + if (outpos_ > 0) { + memmove(outbuf_, outbuf_ + res, outpos_); + } + return res; +} + +void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) { + SignalConnect(this); +} + +void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + + int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "recvfrom: " << errno << " " << std::strerror(errno); + return; + } + + inpos_ += len; + + ProcessInput(inbuf_, inpos_); + + if (inpos_ >= insize_) { + LOG(INFO) << "input buffer overflow"; + ASSERT(false); + inpos_ = 0; + } +} + +void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + + if (outpos_ > 0) { + Flush(); + } +} + +void AsyncTCPSocket::OnCloseEvent(AsyncSocket* socket, int error) { + SignalClose(this, error); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h new file mode 100644 index 00000000..e93e5e7d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h @@ -0,0 +1,68 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ASYNCTCPSOCKET_H__ +#define __ASYNCTCPSOCKET_H__ + +#include "talk/base/asyncpacketsocket.h" + +namespace cricket { + +// Simulates UDP semantics over TCP. Send and Recv packet sizes +// are preserved, and drops packets silently on Send, rather than +// buffer them in user space. +class AsyncTCPSocket : public AsyncPacketSocket { +public: + AsyncTCPSocket(AsyncSocket* socket); + virtual ~AsyncTCPSocket(); + + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + + sigslot::signal1<AsyncTCPSocket*> SignalConnect; + sigslot::signal2<AsyncTCPSocket*,int> SignalClose; + +protected: + int SendRaw(const void * pv, size_t cb); + virtual void ProcessInput(char * data, size_t& len); + +private: + char* inbuf_, * outbuf_; + size_t insize_, inpos_, outsize_, outpos_; + + int Flush(); + + // Called by the underlying socket + void OnConnectEvent(AsyncSocket* socket); + void OnReadEvent(AsyncSocket* socket); + void OnWriteEvent(AsyncSocket* socket); + void OnCloseEvent(AsyncSocket* socket, int error); +}; + +} // namespace cricket + +#endif // __ASYNCSTCPOCKET_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc new file mode 100644 index 00000000..5b8c2466 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc @@ -0,0 +1,83 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asyncudpsocket.h" +#include "talk/base/logging.h" +#include <cassert> +#include <cstring> +#include <iostream> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +const int BUF_SIZE = 64 * 1024; + +AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket) { + size_ = BUF_SIZE; + buf_ = new char[size_]; + + assert(socket_); + // The socket should start out readable but not writable. + socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent); +} + +AsyncUDPSocket::~AsyncUDPSocket() { + delete [] buf_; +} + +void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { + assert(socket == socket_); + + SocketAddress remote_addr; + int len = socket_->RecvFrom(buf_, size_, &remote_addr); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + PLOG(LS_ERROR, socket_->GetError()) << "recvfrom"; + return; + } + + // TODO: Make sure that we got all of the packet. If we did not, then we + // should resize our buffer to be large enough. + + SignalReadPacket(buf_, (size_t)len, remote_addr, this); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h new file mode 100644 index 00000000..7fac7713 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ASYNCUDPSOCKET_H__ +#define __ASYNCUDPSOCKET_H__ + +#include "talk/base/asyncpacketsocket.h" +#include "talk/base/socketfactory.h" + +namespace cricket { + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncUDPSocket : public AsyncPacketSocket { +public: + AsyncUDPSocket(AsyncSocket* socket); + virtual ~AsyncUDPSocket(); + +private: + char* buf_; + size_t size_; + + // Called when the underlying socket is ready to be read from. + void OnReadEvent(AsyncSocket* socket); +}; + +// Creates a new socket for sending asynchronous UDP packets using an +// asynchronous socket from the given factory. +inline AsyncUDPSocket* CreateAsyncUDPSocket(SocketFactory* factory) { + return new AsyncUDPSocket(factory->CreateAsyncSocket(SOCK_DGRAM)); +} + +} // namespace cricket + +#endif // __ASYNCSUDPOCKET_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc new file mode 100644 index 00000000..e0ec1b90 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc @@ -0,0 +1,194 @@ + +//********************************************************************* +//* Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//* +//* Enhancements by Stanley Yamane: +//* o reverse lookup table for the decode function +//* o reserve string buffer space in advance +//* +//********************************************************************* + +#include "talk/base/base64.h" + +using namespace std; + +static const char fillchar = '='; +static const string::size_type np = string::npos; + +const string Base64::Base64Table( + // 0000000000111111111122222222223333333333444444444455555555556666 + // 0123456789012345678901234567890123456789012345678901234567890123 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + +// Decode Table gives the index of any valid base64 character in the Base64 table] +// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == / + + // 0 1 2 3 4 5 6 7 8 9 +const string::size_type Base64::DecodeTable[] = { + np,np,np,np,np,np,np,np,np,np, // 0 - 9 + np,np,np,np,np,np,np,np,np,np, //10 -19 + np,np,np,np,np,np,np,np,np,np, //20 -29 + np,np,np,np,np,np,np,np,np,np, //30 -39 + np,np,np,62,np,np,np,63,52,53, //40 -49 + 54,55,56,57,58,59,60,61,np,np, //50 -59 + np,np,np,np,np, 0, 1, 2, 3, 4, //60 -69 + 5, 6, 7, 8, 9,10,11,12,13,14, //70 -79 + 15,16,17,18,19,20,21,22,23,24, //80 -89 + 25,np,np,np,np,np,np,26,27,28, //90 -99 + 29,30,31,32,33,34,35,36,37,38, //100 -109 + 39,40,41,42,43,44,45,46,47,48, //110 -119 + 49,50,51,np,np,np,np,np,np,np, //120 -129 + np,np,np,np,np,np,np,np,np,np, //130 -139 + np,np,np,np,np,np,np,np,np,np, //140 -149 + np,np,np,np,np,np,np,np,np,np, //150 -159 + np,np,np,np,np,np,np,np,np,np, //160 -169 + np,np,np,np,np,np,np,np,np,np, //170 -179 + np,np,np,np,np,np,np,np,np,np, //180 -189 + np,np,np,np,np,np,np,np,np,np, //190 -199 + np,np,np,np,np,np,np,np,np,np, //200 -209 + np,np,np,np,np,np,np,np,np,np, //210 -219 + np,np,np,np,np,np,np,np,np,np, //220 -229 + np,np,np,np,np,np,np,np,np,np, //230 -239 + np,np,np,np,np,np,np,np,np,np, //240 -249 + np,np,np,np,np,np //250 -256 +}; + +string Base64::encodeFromArray(const char * data, size_t len) { + size_t i; + char c; + string ret; + + ret.reserve(len * 2); + + for (i = 0; i < len; ++i) + { + c = (data[i] >> 2) & 0x3f; + ret.append(1, Base64Table[c]); + c = (data[i] << 4) & 0x3f; + if (++i < len) + c |= (data[i] >> 4) & 0x0f; + + ret.append(1, Base64Table[c]); + if (i < len) + { + c = (data[i] << 2) & 0x3f; + if (++i < len) + c |= (data[i] >> 6) & 0x03; + + ret.append(1, Base64Table[c]); + } + else + { + ++i; + ret.append(1, fillchar); + } + + if (i < len) + { + c = data[i] & 0x3f; + ret.append(1, Base64Table[c]); + } + else + { + ret.append(1, fillchar); + } + } + + return(ret); +} + + +string Base64::encode(const string& data) +{ + string::size_type i; + char c; + string::size_type len = data.length(); + string ret; + + ret.reserve(len * 2); + + for (i = 0; i < len; ++i) + { + c = (data[i] >> 2) & 0x3f; + ret.append(1, Base64Table[c]); + c = (data[i] << 4) & 0x3f; + if (++i < len) + c |= (data[i] >> 4) & 0x0f; + + ret.append(1, Base64Table[c]); + if (i < len) + { + c = (data[i] << 2) & 0x3f; + if (++i < len) + c |= (data[i] >> 6) & 0x03; + + ret.append(1, Base64Table[c]); + } + else + { + ++i; + ret.append(1, fillchar); + } + + if (i < len) + { + c = data[i] & 0x3f; + ret.append(1, Base64Table[c]); + } + else + { + ret.append(1, fillchar); + } + } + + return(ret); +} + +string Base64::decode(const string& data) +{ + string::size_type i; + char c; + char c1; + string::size_type len = data.length(); + string ret; + + ret.reserve(len); + + for (i = 0; i < len; ++i) + { + c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + ++i; + c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + c = (c << 2) | ((c1 >> 4) & 0x3); + ret.append(1, c); + if (++i < len) + { + c = data[i]; + if (fillchar == c) + break; + + c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); + ret.append(1, c1); + } + + if (++i < len) + { + c1 = data[i]; + if (fillchar == c1) + break; + + c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + c = ((c << 6) & 0xc0) | c1; + ret.append(1, c); + } + } + + return(ret); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h new file mode 100644 index 00000000..4622b329 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h @@ -0,0 +1,29 @@ + +//********************************************************************* +//* C_Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//********************************************************************* + +#ifndef Base64_H +#define Base64_H + +#include <string> +using std::string; // comment if your compiler doesn't use namespaces + +class Base64 +{ +public: + static string encode(const string & data); + static string decode(const string & data); + static string encodeFromArray(const char * data, size_t len); +private: + static const string Base64Table; + static const string::size_type DecodeTable[]; +}; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h new file mode 100644 index 00000000..171bc9f9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h @@ -0,0 +1,53 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BASICDEFS_H__ +#define __BASICDEFS_H__ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + + +// Used in GUI when referring to the product name (& Version Resource Product Name) +#define PRODUCT_NAME "Google Talk" + +// Used in GUI when referring to the publisher of the product +#define COMPANY_NAME "Google" + +// Used in filenames, directories, registry key names, etc to refer to the product +#define DIRECTORY_NAME "Google Talk" + +// Used in URLs, registry values, etc, where we prefer not to use a space +#define PRODUCT_SIGNATURE "googletalk" + +// Used whenever we do HTTP +#define USERAGENT_STRING "Google Talk" + +#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0])))) + +#endif // __BASICDEFS_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h new file mode 100644 index 00000000..ea393c09 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BASICTYPES_H__ +#define __BASICTYPES_H__ + +#ifdef COMPILER_MSVC +typedef __int64 int64; +#else +typedef long long int64; +#endif /* COMPILER_MSVC */ +typedef long int32; +typedef short int16; +typedef char int8; + +#ifdef COMPILER_MSVC +typedef unsigned __int64 uint64; +typedef __int64 int64; +#else +typedef unsigned long long uint64; +typedef long long int64; +#endif /* COMPILER_MSVC */ +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +#ifdef WIN32 +typedef int socklen_t; +#endif + +namespace cricket { + template<class T> inline T _min(T a, T b) { return (a > b) ? b : a; } + template<class T> inline T _max(T a, T b) { return (a < b) ? b : a; } +} + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_EVIL_CONSTRUCTORS(TypeName) + +#ifndef UNUSED +#define UNUSED(x) Unused(static_cast<const void *>(&x)) +#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)) +#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)) +#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)) +#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b)) +inline void Unused(const void *) { } +#endif // UNUSED + +#endif // __BASICTYPES_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc new file mode 100644 index 00000000..067f50ed --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc @@ -0,0 +1,165 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/basictypes.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/byteorder.h" +#include <algorithm> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcpy; +} +#endif + +namespace cricket { + +const int DEFAULT_SIZE = 4096; + +ByteBuffer::ByteBuffer() { + start_ = 0; + end_ = 0; + size_ = DEFAULT_SIZE; + bytes_ = new char[size_]; +} + +ByteBuffer::ByteBuffer(const char* bytes, size_t len) { + start_ = 0; + end_ = len; + size_ = len; + bytes_ = new char[size_]; + memcpy(bytes_, bytes, end_); +} + +ByteBuffer::ByteBuffer(const char* bytes) { + start_ = 0; + end_ = strlen(bytes); + size_ = end_; + bytes_ = new char[size_]; + memcpy(bytes_, bytes, end_); +} + +ByteBuffer::~ByteBuffer() { + delete bytes_; +} + +bool ByteBuffer::ReadUInt8(uint8& val) { + return ReadBytes(reinterpret_cast<char*>(&val), 1); +} + +bool ByteBuffer::ReadUInt16(uint16& val) { + uint16 v; + if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) { + return false; + } else { + val = NetworkToHost16(v); + return true; + } +} + +bool ByteBuffer::ReadUInt32(uint32& val) { + uint32 v; + if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) { + return false; + } else { + val = NetworkToHost32(v); + return true; + } +} + +bool ByteBuffer::ReadString(std::string& val, size_t len) { + if (len > Length()) { + return false; + } else { + val.append(bytes_ + start_, len); + start_ += len; + return true; + } +} + +bool ByteBuffer::ReadBytes(char* val, size_t len) { + if (len > Length()) { + return false; + } else { + memcpy(val, bytes_ + start_, len); + start_ += len; + return true; + } +} + +void ByteBuffer::WriteUInt8(uint8 val) { + WriteBytes(reinterpret_cast<const char*>(&val), 1); +} + +void ByteBuffer::WriteUInt16(uint16 val) { + uint16 v = HostToNetwork16(val); + WriteBytes(reinterpret_cast<const char*>(&v), 2); +} + +void ByteBuffer::WriteUInt32(uint32 val) { + uint32 v = HostToNetwork32(val); + WriteBytes(reinterpret_cast<const char*>(&v), 4); +} + +void ByteBuffer::WriteString(const std::string& val) { + WriteBytes(val.c_str(), val.size()); +} + +void ByteBuffer::WriteBytes(const char* val, size_t len) { + if (Length() + len > Capacity()) + Resize(Length() + len); + + memcpy(bytes_ + end_, val, len); + end_ += len; +} + +void ByteBuffer::Resize(size_t size) { + if (size > size_) + size = _max(size, 3 * size_ / 2); + + size_t len = _min(end_ - start_, size); + char* new_bytes = new char[size]; + memcpy(new_bytes, bytes_ + start_, len); + delete [] bytes_; + + start_ = 0; + end_ = len; + size_ = size; + bytes_ = new_bytes; +} + +void ByteBuffer::Shift(size_t size) { + if (size > Length()) + return; + + end_ = Length() - size; + memmove(bytes_, bytes_ + start_ + size, end_); + start_ = 0; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h new file mode 100644 index 00000000..b0a52344 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h @@ -0,0 +1,71 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BYTEBUFFER_H__ +#define __BYTEBUFFER_H__ + +#include "talk/base/basictypes.h" +#include <string> + +namespace cricket { + +class ByteBuffer { +public: + ByteBuffer(); + ByteBuffer(const char* bytes, size_t len); + ByteBuffer(const char* bytes); // uses strlen + ~ByteBuffer(); + + const char* Data() const { return bytes_ + start_; } + size_t Length() { return end_ - start_; } + size_t Capacity() { return size_ - start_; } + + bool ReadUInt8(uint8& val); + bool ReadUInt16(uint16& val); + bool ReadUInt32(uint32& val); + bool ReadString(std::string& val, size_t len); // append to val + bool ReadBytes(char* val, size_t len); + + void WriteUInt8(uint8 val); + void WriteUInt16(uint16 val); + void WriteUInt32(uint32 val); + void WriteString(const std::string& val); + void WriteBytes(const char* val, size_t len); + + void Resize(size_t size); + void Shift(size_t size); + +private: + char* bytes_; + size_t size_; + size_t start_; + size_t end_; +}; + +} // namespace cricket + +#endif // __BYTEBUFFER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h new file mode 100644 index 00000000..4b70f47e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BYTEORDER_H__ +#define __BYTEORDER_H__ + +#include "talk/base/basictypes.h" + +#ifdef POSIX +extern "C" { +#include <arpa/inet.h> +} +#endif + +#ifdef WIN32 +#include <winsock2.h> +#endif + +namespace cricket { + +inline uint16 HostToNetwork16(uint16 n) { + return htons(n); +} + +inline uint32 HostToNetwork32(uint32 n) { + return htonl(n); +} + +inline uint16 NetworkToHost16(uint16 n) { + return ntohs(n); +} + +inline uint32 NetworkToHost32(uint32 n) { + return ntohl(n); +} + +} // namespace cricket + +#endif // __BYTEORDER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h new file mode 100644 index 00000000..b21be2f1 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h @@ -0,0 +1,231 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _common_h_ +#define _common_h_ + +#if defined(_MSC_VER) +// warning C4355: 'this' : used in base member initializer list +#pragma warning(disable:4355) +#endif + +#if defined(ENABLE_DEBUG_MALLOC) && !defined(ENABLE_DEBUG) +#define ENABLE_DEBUG 1 +#endif + +////////////////////////////////////////////////////////////////////// +// General Utilities +////////////////////////////////////////////////////////////////////// + +#ifndef UNUSED +#define UNUSED(x) Unused(static_cast<const void *>(&x)) +#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)) +#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)) +#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)) +#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b)) +inline void Unused(const void *) { } +#endif // UNUSED + +#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0])))) + +///////////////////////////////////////////////////////////////////////////// +// std:min/std:max on msvc +///////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0 + +#undef min +#undef max + +namespace std { + + + /** + * @brief This does what you think it does. + * @param a A thing of arbitrary type. + * @param b Another thing of arbitrary type. + * @return The lesser of the parameters. + * + * This is the simple classic generic implementation. It will work on + * temporary expressions, since they are only evaluated once, unlike a + * preprocessor macro. + */ + template<typename _Tp> + inline const _Tp& + min(const _Tp& __a, const _Tp& __b) + { + //return __b < __a ? __b : __a; + if (__b < __a) return __b; return __a; + } + + /** + * @brief This does what you think it does. + * @param a A thing of arbitrary type. + * @param b Another thing of arbitrary type. + * @return The greater of the parameters. + * + * This is the simple classic generic implementation. It will work on + * temporary expressions, since they are only evaluated once, unlike a + * preprocessor macro. + */ + template<typename _Tp> + inline const _Tp& + max(const _Tp& __a, const _Tp& __b) + { + //return __a < __b ? __b : __a; + if (__a < __b) return __b; return __a; + } + + /** + * @brief This does what you think it does. + * @param a A thing of arbitrary type. + * @param b Another thing of arbitrary type. + * @param comp A @link s20_3_3_comparisons comparison functor@endlink. + * @return The lesser of the parameters. + * + * This will work on temporary expressions, since they are only evaluated + * once, unlike a preprocessor macro. + */ + template<typename _Tp, typename _Compare> + inline const _Tp& + min(const _Tp& __a, const _Tp& __b, _Compare __comp) + { + //return __comp(__b, __a) ? __b : __a; + if (__comp(__b, __a)) return __b; return __a; + } + + /** + * @brief This does what you think it does. + * @param a A thing of arbitrary type. + * @param b Another thing of arbitrary type. + * @param comp A @link s20_3_3_comparisons comparison functor@endlink. + * @return The greater of the parameters. + * + * This will work on temporary expressions, since they are only evaluated + * once, unlike a preprocessor macro. + */ + template<typename _Tp, typename _Compare> + inline const _Tp& + max(const _Tp& __a, const _Tp& __b, _Compare __comp) + { + //return __comp(__a, __b) ? __b : __a; + if (__comp(__a, __b)) return __b; return __a; + } + +} + +#endif // _MSC_VER <= 1200 + + +///////////////////////////////////////////////////////////////////////////// +// Assertions +///////////////////////////////////////////////////////////////////////////// + +#ifdef ENABLE_DEBUG + +namespace buzz { + +// Break causes the debugger to stop executing, or the program to abort +void Break(); + +// LogAssert writes information about an assertion to the log +void LogAssert(const char * function, const char * file, int line, const char * expression); + +inline void Assert(bool result, const char * function, const char * file, int line, const char * expression) { + if (!result) { + LogAssert(function, file, line, expression); + Break(); + } +} + +}; // namespace buzz + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define __FUNCTION__ "" +#endif + +#define ASSERT(x) buzz::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x) +#define VERIFY(x) buzz::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x) + +#else // !ENABLE_DEBUG + +#define ASSERT(x) (void)0 +#define VERIFY(x) (void)(x) + +#endif // !ENABLE_DEBUG + +#define COMPILE_TIME_ASSERT(expr) char CTA_UNIQUE_NAME[expr] +#define CTA_UNIQUE_NAME MAKE_NAME(__LINE__) +#define CTA_MAKE_NAME(line) MAKE_NAME2(line) +#define CTA_MAKE_NAME2(line) constraint_ ## line + +////////////////////////////////////////////////////////////////////// +// Memory leak tracking +////////////////////////////////////////////////////////////////////// + +#include <sys/types.h> + +#ifdef ENABLE_DEBUG_MALLOC + +namespace buzz { + +void * DebugAllocate(size_t size, const char * fname = 0, int line = 0); +void DebugDeallocate(void * ptr, const char * fname = 0, int line = 0); +bool LeakCheck(); +bool LeakCheckU(); +void LeakMarkBaseline(); +void LeakClearBaseline(); + +}; // namespace buzz + +inline void * operator new(size_t size, const char * fname, int line) { return buzz::DebugAllocate(size, fname, line); } +inline void operator delete(void * ptr, const char * fname, int line) { buzz::DebugDeallocate(ptr, fname, line); } + +#if !(defined(TRACK_ARRAY_ALLOC_PROBLEM) && \ + defined(_MSC_VER) && _MSC_VER <= 1200) // 1200 == VC++ 6.0 + +inline void * operator new[](size_t size, const char * fname, int line) { return buzz::DebugAllocate(size, fname, line); } +inline void operator delete[](void * ptr, const char * fname, int line) { buzz::DebugDeallocate(ptr, fname, line); } + +#endif // TRACK_ARRAY_ALLOC_PROBLEM + + +// If you put "#define new TRACK_NEW" in your .cc file after all includes, it should track the calling function name + +#define TRACK_NEW new(__FILE__,__LINE__) +#define TRACK_DEL delete(__FILE__,__LINE__) + +#else // !ENABLE_DEBUG_MALLOC + +#define TRACK_NEW new +#define TRACK_DEL delete + +#endif // !ENABLE_DEBUG_MALLOC + +////////////////////////////////////////////////////////////////////// + +#endif // _common_h_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h new file mode 100644 index 00000000..b75ad5c7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h @@ -0,0 +1,120 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _criticalsection_h_ +#define _criticalsection_h_ + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +#ifdef POSIX +#include <pthread.h> +#endif + +#ifdef _DEBUG +#define CS_TRACK_OWNER 1 +#endif // _DEBUG + +#if CS_TRACK_OWNER +#define TRACK_OWNER(x) x +#else // !CS_TRACK_OWNER +#define TRACK_OWNER(x) +#endif // !CS_TRACK_OWNER + +namespace cricket { + +#ifdef WIN32 +class CriticalSection { +public: + CriticalSection() { + InitializeCriticalSection(&crit_); + // Windows docs say 0 is not a valid thread id + TRACK_OWNER(thread_ = 0); + } + ~CriticalSection() { + DeleteCriticalSection(&crit_); + } + void Enter() { + EnterCriticalSection(&crit_); + TRACK_OWNER(thread_ = GetCurrentThreadId()); + } + void Leave() { + TRACK_OWNER(thread_ = 0); + LeaveCriticalSection(&crit_); + } + +#if CS_TRACK_OWNER + bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); } +#endif // CS_TRACK_OWNER + +private: + CRITICAL_SECTION crit_; + TRACK_OWNER(DWORD thread_); // The section's owning thread id +}; +#endif // WIN32 + +#ifdef POSIX +class CriticalSection { +public: + CriticalSection() { + pthread_mutexattr_t mutex_attribute; + pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &mutex_attribute); + } + ~CriticalSection() { + pthread_mutex_destroy(&mutex_); + } + void Enter() { + pthread_mutex_lock(&mutex_); + } + void Leave() { + pthread_mutex_unlock(&mutex_); + } +private: + pthread_mutex_t mutex_; +}; +#endif // POSIX + +// CritScope, for serializing exection through a scope + +class CritScope { +public: + CritScope(CriticalSection *pcrit) { + pcrit_ = pcrit; + pcrit_->Enter(); + } + ~CritScope() { + pcrit_->Leave(); + } +private: + CriticalSection *pcrit_; +}; + +} // namespace cricket + +#endif // _criticalsection_h_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc new file mode 100644 index 00000000..7b7490d9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc @@ -0,0 +1,99 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/base/network.h" +#include "talk/base/socket.h" +#include <string> +#include <iostream> +#include <cassert> +#include <errno.h> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; + using ::exit; +} +#endif + +#ifdef POSIX +extern "C" { +#include <sys/utsname.h> +} +#endif // POSIX + +namespace { + +void FatalError(const std::string& name, int err) { + PLOG(LERROR, err) << name; + std::exit(1); +} + +} + +namespace cricket { + +#ifdef POSIX +std::string GetHostName() { + struct utsname nm; + if (uname(&nm) < 0) + FatalError("uname", errno); + return std::string(nm.nodename); +} +#endif + +#ifdef WIN32 +std::string GetHostName() { + // TODO: fix this + return "cricket"; +} +#endif + +// Records information about the local host. +Host* gLocalHost = 0; + +const Host& LocalHost() { + if (!gLocalHost) { + std::vector<Network*>* networks = new std::vector<Network*>; + NetworkManager::CreateNetworks(*networks); +#ifdef WIN32 + // This is sort of problematic... one part of the code (the unittests) wants + // 127.0.0.1 to be present and another part (port allocators) don't. Right + // now, they use different APIs, so we can have different behavior. But + // there is something wrong with this. + networks->push_back(new Network("localhost", + SocketAddress::StringToIP("127.0.0.1"))); +#endif + gLocalHost = new Host(GetHostName(), networks); + assert(gLocalHost->networks().size() > 0); + } + + return *gLocalHost; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.h new file mode 100644 index 00000000..16f31a78 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HOST_H__ +#define __HOST_H__ + +#include "talk/base/network.h" +#include <string> +#include <vector> + +namespace cricket { + +// Provides information about a host in the network. +class Host { +public: + Host(const std::string& name, std::vector<Network*>* networks) + : name_(name), networks_(networks) { } + + const std::string& name() const { return name_; } + const std::vector<Network*>& networks() const { return *networks_; } + +private: + std::string name_; + std::vector<Network*>* networks_; +}; + +// Returns a reference to the description of the local host. +const Host& LocalHost(); + +// Returns the name of the local host. +std::string GetHostName(); + +} // namespace cricket + +#endif // __HOST_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc new file mode 100644 index 00000000..5befe9fd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc @@ -0,0 +1,77 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/jtime.h" +#include <iostream> +#include <cstdlib> +#include <cstring> + +namespace cricket { + +#ifdef POSIX +#include <sys/time.h> +uint32 Time() { + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} +#endif + +#ifdef WIN32 +#include <windows.h> +uint32 Time() { + return GetTickCount(); +} +#endif + +bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier) { + if (earlier <= later) { + return ((earlier <= middle) && (middle <= later)); + } else { + return !((later < middle) && (middle < earlier)); + } +} + +int32 TimeDiff(uint32 later, uint32 earlier) { + uint32 LAST = 0xFFFFFFFF; + uint32 HALF = 0x80000000; + if (TimeIsBetween(earlier + HALF, later, earlier)) { + if (earlier <= later) { + return static_cast<long>(later - earlier); + } else { + return static_cast<long>(later + (LAST - earlier) + 1); + } + } else { + if (later <= earlier) { + return -static_cast<long>(earlier - later); + } else { + return -static_cast<long>(earlier + (LAST - later) + 1); + } + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h new file mode 100644 index 00000000..d7dff0fb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h @@ -0,0 +1,47 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __JTIME_H__ +#define __JTIME_H__ + +#include "talk/base/basictypes.h" + +namespace cricket { + +// Returns the current time in milliseconds. +uint32 Time(); + +// TODO: Delete this old version. +#define GetMillisecondCount Time + +// Comparisons between time values, which can wrap around. +bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier); +int32 TimeDiff(uint32 later, uint32 earlier); + +} // namespace cricket + +#endif // __TIME_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h new file mode 100644 index 00000000..94b62ad3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h @@ -0,0 +1,138 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +/* + * linked_ptr - simple reference linked pointer + * (like reference counting, just using a linked list of the references + * instead of their count.) + * + * The implementation stores three pointers for every linked_ptr, but + * does not allocate anything on the free store. + */ + +#ifndef _LINKED_PTR_H_ +#define _LINKED_PTR_H_ + +/* For ANSI-challenged compilers, you may want to #define + * NO_MEMBER_TEMPLATES, explicit or mutable */ +#define NO_MEMBER_TEMPLATES + +template <class X> class linked_ptr +{ +public: + +#ifndef NO_MEMBER_TEMPLATES +# define TEMPLATE_FUNCTION template <class Y> + TEMPLATE_FUNCTION friend class linked_ptr<Y>; +#else +# define TEMPLATE_FUNCTION + typedef X Y; +#endif + + typedef X element_type; + + explicit linked_ptr(X* p = 0) throw() + : itsPtr(p) {itsPrev = itsNext = this;} + ~linked_ptr() + {release();} + linked_ptr(const linked_ptr& r) throw() + {acquire(r);} + linked_ptr& operator=(const linked_ptr& r) + { + if (this != &r) { + release(); + acquire(r); + } + return *this; + } + +#ifndef NO_MEMBER_TEMPLATES + template <class Y> friend class linked_ptr<Y>; + template <class Y> linked_ptr(const linked_ptr<Y>& r) throw() + {acquire(r);} + template <class Y> linked_ptr& operator=(const linked_ptr<Y>& r) + { + if (this != &r) { + release(); + acquire(r); + } + return *this; + } +#endif // NO_MEMBER_TEMPLATES + + X& operator*() const throw() {return *itsPtr;} + X* operator->() const throw() {return itsPtr;} + X* get() const throw() {return itsPtr;} + bool unique() const throw() {return itsPrev ? itsPrev==this : true;} + +private: + X* itsPtr; + mutable const linked_ptr* itsPrev; + mutable const linked_ptr* itsNext; + + void acquire(const linked_ptr& r) throw() + { // insert this to the list + itsPtr = r.itsPtr; + itsNext = r.itsNext; + itsNext->itsPrev = this; + itsPrev = &r; +#ifndef mutable + r.itsNext = this; +#else // for ANSI-challenged compilers + (const_cast<linked_ptr<X>*>(&r))->itsNext = this; +#endif + } + +#ifndef NO_MEMBER_TEMPLATES + template <class Y> void acquire(const linked_ptr<Y>& r) throw() + { // insert this to the list + itsPtr = r.itsPtr; + itsNext = r.itsNext; + itsNext->itsPrev = this; + itsPrev = &r; +#ifndef mutable + r.itsNext = this; +#else // for ANSI-challenged compilers + (const_cast<linked_ptr<X>*>(&r))->itsNext = this; +#endif + } +#endif // NO_MEMBER_TEMPLATES + + void release() + { // erase this from the list, delete if unique + if (unique()) delete itsPtr; + else { + itsPrev->itsNext = itsNext; + itsNext->itsPrev = itsPrev; + itsPrev = itsNext = 0; + } + itsPtr = 0; + } +}; + +#endif // LINKED_PTR_H + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h new file mode 100644 index 00000000..500386ed --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h @@ -0,0 +1,222 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +// LOG(...) an ostream target that can be used to send formatted +// output to a variety of logging targets, such as debugger console, stderr, +// file, or any StreamInterface. +// The severity level passed as the first argument to the the LOGging +// functions is used as a filter, to limit the verbosity of the logging. +// Static members of LogMessage documented below are used to control the +// verbosity and target of the output. +// There are several variations on the LOG macro which facilitate logging +// of common error conditions, detailed below. + +#ifndef TALK_BASE_LOGGING_H__ +#define TALK_BASE_LOGGING_H__ + +#include <sstream> +#include "talk/base/basictypes.h" +class StreamInterface; + +/////////////////////////////////////////////////////////////////////////////// +// ConstantLabel can be used to easily generate string names from constant +// values. This can be useful for logging descriptive names of error messages. +// Usage: +// const ConstantLabel LIBRARY_ERRORS[] = { +// KLABEL(SOME_ERROR), +// KLABEL(SOME_OTHER_ERROR), +// ... +// LASTLABEL +// } +// +// int err = LibraryFunc(); +// LOG(LS_ERROR) << "LibraryFunc returned: " +// << ErrorName(err, LIBRARY_ERRORS); + +struct ConstantLabel { int value; const char * label; }; +#define KLABEL(x) { x, #x } +#define TLABEL(x,y) { x, y } +#define LASTLABEL { 0, 0 } + +const char * FindLabel(int value, const ConstantLabel entries[]); +std::string ErrorName(int err, const ConstantLabel * err_table); + +////////////////////////////////////////////////////////////////////// + +// Note that the non-standard LoggingSeverity aliases exist because they are +// still in broad use. The meanings of the levels are: +// LS_SENSITIVE: Information which should only be logged with the consent +// of the user, due to privacy concerns. +// LS_VERBOSE: This level is for data which we do not want to appear in the +// normal debug log, but should appear in diagnostic logs. +// LS_INFO: Chatty level used in debugging for all sorts of things, the default +// in debug builds. +// LS_WARNING: Something that may warrant investigation. +// LS_ERROR: Something that should not have occurred. +enum LoggingSeverity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR, + INFO = LS_INFO, + WARNING = LS_WARNING, + LERROR = LS_ERROR }; + +// LogErrorContext assists in interpreting the meaning of an error value. +// ERRCTX_ERRNO: the value was read from global 'errno' +// ERRCTX_HRESULT: the value is a Windows HRESULT +enum LogErrorContext { ERRCTX_NONE, ERRCTX_ERRNO, ERRCTX_HRESULT }; + +// If LOGGING is not explicitly defined, default to enabled in debug mode +#if !defined(LOGGING) +#if defined(_DEBUG) && !defined(NDEBUG) +#define LOGGING 1 +#else +#define LOGGING 0 +#endif +#endif // !defined(LOGGING) + +#if LOGGING + +#define LOG(sev) \ + if (LogMessage::Loggable(sev)) \ + LogMessage(__FILE__, __LINE__, sev).stream() + +// PLOG and LOG_ERR attempt to provide a string description of an errno derived +// error. LOG_ERR reads errno directly, so care must be taken to call it before +// errno is reset. +#define PLOG(sev, err) \ + if (LogMessage::Loggable(sev)) \ + LogMessage(__FILE__, __LINE__, sev, ERRCTX_ERRNO, err).stream() +#define LOG_ERR(sev) \ + if (LogMessage::Loggable(sev)) \ + LogMessage(__FILE__, __LINE__, sev, ERRCTX_ERRNO, errno).stream() + +// LOG_GLE(M) attempt to provide a string description of the HRESULT returned +// by GetLastError. The second variant allows searching of a dll's string +// table for the error description. +#ifdef WIN32 +#define LOG_GLE(sev) \ + if (LogMessage::Loggable(sev)) \ + LogMessage(__FILE__, __LINE__, sev, ERRCTX_HRESULT, GetLastError()).stream() +#define LOG_GLEM(sev, mod) \ + if (LogMessage::Loggable(sev)) \ + LogMessage(__FILE__, __LINE__, sev, ERRCTX_HRESULT, GetLastError(), mod) \ + .stream() +#endif // WIN32 + +// TODO: Add an "assert" wrapper that logs in the same manner. + +#else // !LOGGING + +// Hopefully, the compiler will optimize away some of this code. +// Note: syntax of "1 ? (void)0 : LogMessage" was causing errors in g++, +// converted to "while (false)" +#define LOG(sev) \ + while (false) LogMessage(NULL, 0, sev).stream() +#define PLOG(sev, err) \ + while (false) LogMessage(NULL, 0, sev, ERRCTX_ERRNO, 0).stream() +#define LOG_ERR(sev) \ + while (false) LogMessage(NULL, 0, sev, ERRCTX_ERRNO, 0).stream() +#ifdef WIN32 +#define LOG_GLE(sev) \ + while (false) LogMessage(NULL, 0, sev, ERRCTX_HRESULT, 0).stream() +#define LOG_GLEM(sev, mod) \ + while (false) LogMessage(NULL, 0, sev, ERRCTX_HRESULT, 0).stream() +#endif // WIN32 + +#endif // !LOGGING + +class LogMessage { + public: + LogMessage(const char* file, int line, LoggingSeverity sev, + LogErrorContext err_ctx = ERRCTX_NONE, int err = 0, + const char* module = NULL); + ~LogMessage(); + + static inline bool Loggable(LoggingSeverity sev) { return (sev >= min_sev_); } + std::ostream& stream() { return print_stream_; } + + enum { NO_LOGGING = LS_ERROR + 1 }; + + // These are attributes which apply to all logging channels + // LogContext: Display the file and line number of the message + static void LogContext(int min_sev); + // LogThreads: Display the thread identifier of the current thread + static void LogThreads(bool on = true); + // LogTimestamps: Display the elapsed time of the program + static void LogTimestamps(bool on = true); + + // Timestamps begin with program execution, but can be reset with this + // function for measuring the duration of an activity, or to synchronize + // timestamps between multiple instances. + static void ResetTimestamps(); + + // These are the available logging channels + // Debug: Debug console on Windows, otherwise stderr + static void LogToDebug(int min_sev); + static int GetLogToDebug() { return dbg_sev_; } + // Stream: Any non-blocking stream interface. LogMessage takes ownership of + // the stream. + static void LogToStream(StreamInterface* stream, int min_sev); + static int GetLogToStream() { return stream_sev_; } + + // Testing against MinLogSeverity allows code to avoid potentially expensive + // logging operations by pre-checking the logging level. + static int GetMinLogSeverity() { return min_sev_; } + + private: + // These assist in formatting some parts of the debug output. + static const char* Describe(LoggingSeverity sev); + static const char* DescribeFile(const char* file); + + // The ostream that buffers the formatted message before output + std::ostringstream print_stream_; + + // The severity level of this message + LoggingSeverity severity_; + + // String data generated in the constructor, that should be appended to + // the message before output. + std::string extra_; + + // dbg_sev_ and stream_sev_ are the thresholds for those output targets + // min_sev_ is the minimum (most verbose) of those levels, and is used + // as a short-circuit in the logging macros to identify messages that won't + // be logged. + // ctx_sev_ is the minimum level at which file context is displayed + static int min_sev_, dbg_sev_, stream_sev_, ctx_sev_; + + // The output stream, if any + static StreamInterface * stream_; + + // Flags for formatting options + static bool thread_, timestamp_; + + // The timestamp at which logging started. + static uint32 start_; + + DISALLOW_EVIL_CONSTRUCTORS(LogMessage); +}; + +#endif // TALK_BASE_LOGGING_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h new file mode 100644 index 00000000..c2e22cc5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h @@ -0,0 +1,45 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +#ifndef MD5_H +#define MD5_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long unsigned int uint32; +typedef struct MD5Context MD5_CTX; + +#define md5byte unsigned char + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + uint32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32 buf[4], uint32 const in[16]); + +#ifdef __cplusplus +}; +#endif + +#endif /* !MD5_H */ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c new file mode 100644 index 00000000..eb2c034d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c @@ -0,0 +1,256 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +#include <string.h> /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | + ((unsigned)buf[1]<<8 | buf[0]); + *(uint32 *)buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = (unsigned char *)ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = (unsigned char*)(ctx->in) + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; + ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse((unsigned char *)ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc new file mode 100644 index 00000000..f10489f7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc @@ -0,0 +1,321 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/messagequeue.h" +#include "talk/base/physicalsocketserver.h" + +#ifdef POSIX +extern "C" { +#include <sys/time.h> +} +#endif + +namespace cricket { + +//------------------------------------------------------------------ +// MessageQueueManager + +MessageQueueManager* MessageQueueManager::instance_; + +MessageQueueManager* MessageQueueManager::Instance() { + // Note: This is not thread safe, but it is first called before threads are + // spawned. + if (!instance_) + instance_ = new MessageQueueManager; + return instance_; +} + +MessageQueueManager::MessageQueueManager() { +} + +MessageQueueManager::~MessageQueueManager() { +} + +void MessageQueueManager::Add(MessageQueue *message_queue) { + CritScope cs(&crit_); + message_queues_.push_back(message_queue); +} + +void MessageQueueManager::Remove(MessageQueue *message_queue) { + CritScope cs(&crit_); + std::vector<MessageQueue *>::iterator iter; + iter = std::find(message_queues_.begin(), message_queues_.end(), message_queue); + if (iter != message_queues_.end()) + message_queues_.erase(iter); +} + +void MessageQueueManager::Clear(MessageHandler *handler) { + CritScope cs(&crit_); + std::vector<MessageQueue *>::iterator iter; + for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++) + (*iter)->Clear(handler); +} + +//------------------------------------------------------------------ +// MessageQueue + +MessageQueue::MessageQueue(SocketServer* ss) + : ss_(ss), new_ss(false), fStop_(false), fPeekKeep_(false) { + if (!ss_) { + new_ss = true; + ss_ = new PhysicalSocketServer(); + } + MessageQueueManager::Instance()->Add(this); +} + +MessageQueue::~MessageQueue() { + Clear(NULL); + if (new_ss) + delete ss_; + MessageQueueManager::Instance()->Remove(this); +} + +void MessageQueue::set_socketserver(SocketServer* ss) { + if (new_ss) + delete ss_; + new_ss = false; + ss_ = ss; +} + +void MessageQueue::Stop() { + fStop_ = true; + ss_->WakeUp(); +} + +bool MessageQueue::IsStopping() { + return fStop_; +} + +void MessageQueue::Restart() { + fStop_ = false; +} + +bool MessageQueue::Peek(Message *pmsg, int cmsWait) { + if (fStop_) + return false; + if (fPeekKeep_) { + *pmsg = msgPeek_; + return true; + } + if (!Get(pmsg, cmsWait)) + return false; + msgPeek_ = *pmsg; + fPeekKeep_ = true; + return true; +} + +bool MessageQueue::Get(Message *pmsg, int cmsWait) { + // Force stopping + + if (fStop_) + return false; + + // Return and clear peek if present + // Always return the peek if it exists so there is Peek/Get symmetry + + if (fPeekKeep_) { + *pmsg = msgPeek_; + fPeekKeep_ = false; + return true; + } + + // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch + + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = GetMillisecondCount(); + uint32 msCurrent = msStart; + while (!fStop_) { + // Check for sent messages + + ReceiveSends(); + + // Check queues + + int cmsDelayNext = -1; + { + CritScope cs(&crit_); + + // Check for delayed messages that have been triggered + // Calc the next trigger too + + while (!dmsgq_.empty()) { + if (msCurrent < dmsgq_.top().msTrigger_) { + cmsDelayNext = dmsgq_.top().msTrigger_ - msCurrent; + break; + } + msgq_.push(dmsgq_.top().msg_); + dmsgq_.pop(); + } + + // Check for posted events + + if (!msgq_.empty()) { + *pmsg = msgq_.front(); + msgq_.pop(); + return true; + } + } + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == -1) { + cmsNext = cmsDelayNext; + } else { + cmsNext = cmsTotal - cmsElapsed; + if (cmsNext < 0) + cmsNext = 0; + if (cmsDelayNext != -1 && cmsDelayNext < cmsNext) + cmsNext = cmsDelayNext; + } + + // Wait and multiplex in the meantime + ss_->Wait(cmsNext, true); + + // If the specified timeout expired, return + + msCurrent = GetMillisecondCount(); + cmsElapsed = msCurrent - msStart; + if (cmsWait != -1) { + if (cmsElapsed >= cmsWait) + return false; + } + } + return false; +} + +void MessageQueue::ReceiveSends() { +} + +void MessageQueue::Post(MessageHandler *phandler, uint32 id, + MessageData *pdata) { + // Keep thread safe + // Add the message to the end of the queue + // Signal for the multiplexer to return + + CritScope cs(&crit_); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + msgq_.push(msg); + ss_->WakeUp(); +} + +void MessageQueue::PostDelayed(int cmsDelay, MessageHandler *phandler, + uint32 id, MessageData *pdata) { + // Keep thread safe + // Add to the priority queue. Gets sorted soonest first. + // Signal for the multiplexer to return. + + CritScope cs(&crit_); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + dmsgq_.push(DelayedMessage(cmsDelay, &msg)); + ss_->WakeUp(); +} + +int MessageQueue::GetDelay() { + CritScope cs(&crit_); + + if (!msgq_.empty()) + return 0; + + if (!dmsgq_.empty()) { + int delay = dmsgq_.top().msTrigger_ - GetMillisecondCount(); + if (delay < 0) + delay = 0; + return delay; + } + + return -1; +} + +void MessageQueue::Clear(MessageHandler *phandler, uint32 id) { + CritScope cs(&crit_); + + // Remove messages with phandler + + if (fPeekKeep_) { + if (phandler == NULL || msgPeek_.phandler == phandler) { + if (id == (uint32)-1 || msgPeek_.message_id == id) { + delete msgPeek_.pdata; + fPeekKeep_ = false; + } + } + } + + // Remove from ordered message queue + + size_t c = msgq_.size(); + while (c-- != 0) { + Message msg = msgq_.front(); + msgq_.pop(); + if (phandler != NULL && msg.phandler != phandler) { + msgq_.push(msg); + } else { + if (id == (uint32)-1 || msg.message_id == id) { + delete msg.pdata; + } else { + msgq_.push(msg); + } + } + } + + // Remove from priority queue. Not directly iterable, so use this approach + + std::queue<DelayedMessage> dmsgs; + while (!dmsgq_.empty()) { + DelayedMessage dmsg = dmsgq_.top(); + dmsgq_.pop(); + if (phandler != NULL && dmsg.msg_.phandler != phandler) { + dmsgs.push(dmsg); + } else { + if (id == (uint32)-1 || dmsg.msg_.message_id == id) { + delete dmsg.msg_.pdata; + } else { + dmsgs.push(dmsg); + } + } + } + while (!dmsgs.empty()) { + dmsgq_.push(dmsgs.front()); + dmsgs.pop(); + } +} + +void MessageQueue::Dispatch(Message *pmsg) { + pmsg->phandler->OnMessage(pmsg); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h new file mode 100644 index 00000000..2a9cbed6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h @@ -0,0 +1,164 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MESSAGEQUEUE_H__ +#define __MESSAGEQUEUE_H__ + +#include "talk/base/basictypes.h" +#include "talk/base/criticalsection.h" +#include "talk/base/socketserver.h" +#include "talk/base/jtime.h" +#include <vector> +#include <queue> +#include <algorithm> + +namespace cricket { + +struct Message; +class MessageQueue; +class MessageHandler; + +// MessageQueueManager does cleanup of of message queues + +class MessageQueueManager { +public: + static MessageQueueManager* Instance(); + + void Add(MessageQueue *message_queue); + void Remove(MessageQueue *message_queue); + void Clear(MessageHandler *handler); + +private: + MessageQueueManager(); + ~MessageQueueManager(); + + static MessageQueueManager* instance_; + std::vector<MessageQueue *> message_queues_; + CriticalSection crit_; +}; + +// Messages get dispatched to a MessageHandler + +class MessageHandler { +public: + virtual ~MessageHandler() { + MessageQueueManager::Instance()->Clear(this); + } + + virtual void OnMessage(Message *pmsg) = 0; +}; + +// Derive from this for specialized data +// App manages lifetime, except when messages are purged + +class MessageData { +public: + MessageData() {} + virtual ~MessageData() {} +}; + +template <class arg1_type> +class TypedMessageData : public MessageData { +public: + TypedMessageData(arg1_type data) { + data_ = data; + } + arg1_type data() { + return data_; + } +private: + arg1_type data_; +}; + +// No destructor + +struct Message { + Message() { + memset(this, 0, sizeof(*this)); + } + MessageHandler *phandler; + uint32 message_id; + MessageData *pdata; +}; + +// DelayedMessage goes into a priority queue, sorted by trigger time + +class DelayedMessage { +public: + DelayedMessage(int cmsDelay, Message *pmsg) { + cmsDelay_ = cmsDelay; + msTrigger_ = GetMillisecondCount() + cmsDelay; + msg_ = *pmsg; + } + + bool operator< (const DelayedMessage& dmsg) const { + return dmsg.msTrigger_ < msTrigger_; + } + + int cmsDelay_; // for debugging + uint32 msTrigger_; + Message msg_; +}; + +class MessageQueue { +public: + MessageQueue(SocketServer* ss = 0); + virtual ~MessageQueue(); + + SocketServer* socketserver() { return ss_; } + void set_socketserver(SocketServer* ss); + + // Once the queue is stopped, all calls to Get/Peek will return false. + virtual void Stop(); + virtual bool IsStopping(); + virtual void Restart(); + + virtual bool Get(Message *pmsg, int cmsWait = -1); + virtual bool Peek(Message *pmsg, int cmsWait = 0); + virtual void Post(MessageHandler *phandler, uint32 id = 0, + MessageData *pdata = NULL); + virtual void PostDelayed(int cmsDelay, MessageHandler *phandler, + uint32 id = 0, MessageData *pdata = NULL); + virtual void Clear(MessageHandler *phandler, uint32 id = (uint32)-1); + virtual void Dispatch(Message *pmsg); + virtual void ReceiveSends(); + virtual int GetDelay(); + +protected: + SocketServer* ss_; + bool new_ss; + bool fStop_; + bool fPeekKeep_; + Message msgPeek_; + std::queue<Message> msgq_; + std::priority_queue<DelayedMessage> dmsgq_; + CriticalSection crit_; +}; + +} // namespace cricket + +#endif // __MESSAGEQUEUE_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc new file mode 100644 index 00000000..21b3a08f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc @@ -0,0 +1,382 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/base/network.h" +#include "talk/base/socket.h" // this includes something that makes windows happy +#include "talk/base/jtime.h" +#include "talk/base/basicdefs.h" + +#include <algorithm> +#include <cassert> +#include <cfloat> +#include <cmath> +#include <sstream> + +#ifdef POSIX +extern "C" { +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <unistd.h> +#include <errno.h> +} +#endif // POSIX + +#ifdef WIN32 +#include <Iphlpapi.h> +#endif + +namespace { + +const double kAlpha = 0.5; // weight for data infinitely far in the past +const double kHalfLife = 2000; // half life of exponential decay (in ms) +const double kLog2 = 0.693147180559945309417; +const double kLambda = kLog2 / kHalfLife; + +// assume so-so quality unless data says otherwise +const double kDefaultQuality = cricket::QUALITY_FAIR; + +typedef std::map<std::string,std::string> StrMap; + +void BuildMap(const StrMap& map, std::string& str) { + str.append("{"); + bool first = true; + for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) { + if (!first) str.append(","); + str.append(i->first); + str.append("="); + str.append(i->second); + first = false; + } + str.append("}"); +} + +void ParseCheck(std::istringstream& ist, char ch) { + if (ist.get() != ch) + LOG(LERROR) << "Expecting '" << ch << "'"; +} + +std::string ParseString(std::istringstream& ist) { + std::string str; + int count = 0; + while (ist) { + char ch = ist.peek(); + if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) { + break; + } else if (ch == '{') { + count += 1; + } else if (ch == '}') { + count -= 1; + if (count < 0) + LOG(LERROR) << "mismatched '{' and '}'"; + } + str.append(1, static_cast<char>(ist.get())); + } + return str; +} + +void ParseMap(const std::string& str, StrMap& map) { + if (str.size() == 0) + return; + std::istringstream ist(str); + ParseCheck(ist, '{'); + for (;;) { + std::string key = ParseString(ist); + ParseCheck(ist, '='); + std::string val = ParseString(ist); + map[key] = val; + if (ist.peek() == ',') + ist.get(); + else + break; + } + ParseCheck(ist, '}'); + if (ist.rdbuf()->in_avail() != 0) + LOG(LERROR) << "Unexpected characters at end"; +} + +#if 0 +const std::string TEST_MAP0_IN = ""; +const std::string TEST_MAP0_OUT = "{}"; +const std::string TEST_MAP1 = "{a=12345}"; +const std::string TEST_MAP2 = "{a=12345,b=67890}"; +const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}"; +const std::string TEST_MAP4 = "{a={d=12345,e=67890}}"; +const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}"; +const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}"; +const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}"; + +class MyTest { +public: + MyTest() { + test(TEST_MAP0_IN, TEST_MAP0_OUT); + test(TEST_MAP1, TEST_MAP1); + test(TEST_MAP2, TEST_MAP2); + test(TEST_MAP3, TEST_MAP3); + test(TEST_MAP4, TEST_MAP4); + test(TEST_MAP5, TEST_MAP5); + test(TEST_MAP6, TEST_MAP6); + test(TEST_MAP7, TEST_MAP7); + } + void test(const std::string& input, const std::string& exp_output) { + StrMap map; + ParseMap(input, map); + std::string output; + BuildMap(map, output); + LOG(INFO) << " ******** " << (output == exp_output); + } +}; + +static MyTest myTest; +#endif + +template <typename T> +std::string ToString(T val) { + std::ostringstream ost; + ost << val; + return ost.str(); +} + +template <typename T> +T FromString(std::string str) { + std::istringstream ist(str); + T val; + ist >> val; + return val; +} + +} + +namespace cricket { + +#ifdef POSIX +void NetworkManager::CreateNetworks(std::vector<Network*>& networks) { + int fd; + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + PLOG(LERROR, errno) << "socket"; + return; + } + + struct ifconf ifc; + ifc.ifc_len = 64 * sizeof(struct ifreq); + ifc.ifc_buf = new char[ifc.ifc_len]; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + PLOG(LERROR, errno) << "ioctl"; + return; + } + assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq))); + + struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf); + struct ifreq* end = + reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len); + + while (ptr < end) { + struct sockaddr_in* inaddr = + reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr); + if (inaddr->sin_family == AF_INET) { + uint32 ip = ntohl(inaddr->sin_addr.s_addr); + networks.push_back(new Network(std::string(ptr->ifr_name), ip)); + } +#ifdef _SIZEOF_ADDR_IFREQ + ptr = reinterpret_cast<struct ifreq*>( + reinterpret_cast<char*>(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); +#else + ptr++; +#endif + } + + delete [] ifc.ifc_buf; + close(fd); +} +#endif + +#ifdef WIN32 +void NetworkManager::CreateNetworks(std::vector<Network*>& networks) { + IP_ADAPTER_INFO info_temp; + ULONG len = 0; + + if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW) + return; + IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len]; + if (GetAdaptersInfo(infos, &len) != NO_ERROR) + return; + + int count = 0; + for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) { + if (info->Type == MIB_IF_TYPE_LOOPBACK) + continue; + if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0) + continue; + + // In production, don't transmit the network name because of + // privacy concerns. Transmit a number instead. + + std::string name; +#if defined(PRODUCTION) + std::ostringstream ost; + ost << count; + name = ost.str(); + count++; +#else + name = info->Description; +#endif + + networks.push_back(new Network(name, + SocketAddress::StringToIP(info->IpAddressList.IpAddress.String))); + } + + delete infos; +} +#endif + +void NetworkManager::GetNetworks(std::vector<Network*>& result) { + std::vector<Network*> list; + CreateNetworks(list); + + for (uint32 i = 0; i < list.size(); ++i) { + NetworkMap::iterator iter = networks_.find(list[i]->name()); + + Network* network; + if (iter == networks_.end()) { + network = list[i]; + } else { + network = iter->second; + network->set_ip(list[i]->ip()); + delete list[i]; + } + + networks_[network->name()] = network; + result.push_back(network); + } +} + +std::string NetworkManager::GetState() { + StrMap map; + for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i) + map[i->first] = i->second->GetState(); + + std::string str; + BuildMap(map, str); + return str; +} + +void NetworkManager::SetState(std::string str) { + StrMap map; + ParseMap(str, map); + + for (StrMap::iterator i = map.begin(); i != map.end(); ++i) { + std::string name = i->first; + std::string state = i->second; + + Network* network = new Network(name, 0); + network->SetState(state); + networks_[name] = network; + } +} + +Network::Network(const std::string& name, uint32 ip) + : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0), + exponential_numerator_(0), exponential_denominator_(0), + quality_(kDefaultQuality) { + + last_data_time_ = Time(); + + // TODO: seed the historical data with one data point based on the link speed + // metric from XP (4.0 if < 50, 3.0 otherwise). +} + +void Network::StartSession(NetworkSession* session) { + assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end()); + sessions_.push_back(session); +} + +void Network::StopSession(NetworkSession* session) { + SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session); + if (iter != sessions_.end()) + sessions_.erase(iter); +} + +void Network::EstimateQuality() { + uint32 now = Time(); + + // Add new data points for the current time. + for (uint32 i = 0; i < sessions_.size(); ++i) { + if (sessions_[i]->HasQuality()) + AddDataPoint(now, sessions_[i]->GetCurrentQuality()); + } + + // Construct the weighted average using both uniform and exponential weights. + + double exp_shift = exp(-kLambda * (now - last_data_time_)); + double numerator = uniform_numerator_ + exp_shift * exponential_numerator_; + double denominator = uniform_denominator_ + exp_shift * exponential_denominator_; + + if (denominator < DBL_EPSILON) + quality_ = kDefaultQuality; + else + quality_ = numerator / denominator; +} + +void Network::AddDataPoint(uint32 time, double quality) { + uniform_numerator_ += kAlpha * quality; + uniform_denominator_ += kAlpha; + + double exp_shift = exp(-kLambda * (time - last_data_time_)); + exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_; + exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_; + + last_data_time_ = time; +} + +std::string Network::GetState() { + StrMap map; + map["lt"] = ToString<uint32>(last_data_time_); + map["un"] = ToString<double>(uniform_numerator_); + map["ud"] = ToString<double>(uniform_denominator_); + map["en"] = ToString<double>(exponential_numerator_); + map["ed"] = ToString<double>(exponential_denominator_); + + std::string str; + BuildMap(map, str); + return str; +} + +void Network::SetState(std::string str) { + StrMap map; + ParseMap(str, map); + + last_data_time_ = FromString<uint32>(map["lt"]); + uniform_numerator_ = FromString<double>(map["un"]); + uniform_denominator_ = FromString<double>(map["ud"]); + exponential_numerator_ = FromString<double>(map["en"]); + exponential_denominator_ = FromString<double>(map["ed"]); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.h new file mode 100644 index 00000000..2cc9128a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.h @@ -0,0 +1,136 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __NETWORK_H__ +#define __NETWORK_H__ + +#include "talk/base/basictypes.h" + +#include <deque> +#include <map> +#include <string> +#include <vector> + +namespace cricket { + +class Network; +class NetworkSession; + +// Keeps track of the available network interfaces over time so that quality +// information can be aggregated and recorded. +class NetworkManager { +public: + + // Updates and returns the current list of networks available on this machine. + // This version will make sure that repeated calls return the same object for + // a given network, so that quality is tracked appropriately. + void GetNetworks(std::vector<Network*>& networks); + + // Reads and writes the state of the quality database in a string format. + std::string GetState(); + void SetState(std::string str); + + // Creates a network object for each network available on the machine. + static void CreateNetworks(std::vector<Network*>& networks); + +private: + typedef std::map<std::string,Network*> NetworkMap; + + NetworkMap networks_; +}; + +// Represents a Unix-type network interface, with a name and single address. +// It also includes the ability to track and estimate quality. +class Network { +public: + Network(const std::string& name, uint32 ip); + + // Returns the OS name of this network. This is considered the primary key + // that identifies each network. + const std::string& name() const { return name_; } + + // Identifies the current IP address used by this network. + uint32 ip() const { return ip_; } + void set_ip(uint32 ip) { ip_ = ip; } + + // Updates the list of sessions that are ongoing. + void StartSession(NetworkSession* session); + void StopSession(NetworkSession* session); + + // Re-computes the estimate of near-future quality based on the information + // as of this exact moment. + void EstimateQuality(); + + // Returns the current estimate of the near-future quality of connections + // that use this local interface. + double quality() { return quality_; } + +private: + typedef std::vector<NetworkSession*> SessionList; + + std::string name_; + uint32 ip_; + SessionList sessions_; + double uniform_numerator_; + double uniform_denominator_; + double exponential_numerator_; + double exponential_denominator_; + uint32 last_data_time_; + double quality_; + + // Updates the statistics maintained to include the given estimate. + void AddDataPoint(uint32 time, double quality); + + // Converts the internal state to and from a string. This is used to record + // quality information into a permanent store. + void SetState(std::string str); + std::string GetState(); + + friend class NetworkManager; +}; + +// Represents a session that is in progress using a particular network and can +// provide data about the quality of the network at any given moment. +class NetworkSession { +public: + // Determines whether this session has an estimate at this moment. We will + // only call GetCurrentQuality when this returns true. + virtual bool HasQuality() = 0; + + // Returns an estimate of the quality at this exact moment. The result should + // be a MOS (mean opinion score) value. + virtual float GetCurrentQuality() = 0; + +}; + +const double QUALITY_BAD = 3.0; +const double QUALITY_FAIR = 3.35; +const double QUALITY_GOOD = 3.7; + +} // namespace cricket + +#endif // __NETWORK_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc new file mode 100644 index 00000000..91d2daad --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc @@ -0,0 +1,1117 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <cassert> + +#ifdef POSIX +extern "C" { +#include <errno.h> +#include <fcntl.h> +#include <sys/time.h> +#include <unistd.h> +} +#endif + +#include "talk/base/basictypes.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/jtime.h" +#include "talk/base/winping.h" + +#ifdef __linux +#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h +#endif // __linux + +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#define _WINSOCKAPI_ +#include <windows.h> +#undef SetPort + +#include <algorithm> +#include <iostream> + +class WinsockInitializer { +public: + WinsockInitializer() { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(1, 0); + err_ = WSAStartup(wVersionRequested, &wsaData); + } + ~WinsockInitializer() { + WSACleanup(); + } + int error() { + return err_; + } +private: + int err_; +}; +WinsockInitializer g_winsockinit; +#endif + +namespace cricket { + +const int kfRead = 0x0001; +const int kfWrite = 0x0002; +const int kfConnect = 0x0004; +const int kfClose = 0x0008; + + +// Standard MTUs +const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + //4464, // IEEE 802.5 (4Mb max) + 4352, // FDDI + //2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + //1536, // Expermental Ethernet Networks + //1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + //576, // X.25 Networks + //544, // DEC IP Portal + //512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + 68, // Official minimum + 0, // End of list marker +}; + +const uint32 IP_HEADER_SIZE = 20; +const uint32 ICMP_HEADER_SIZE = 8; + +class PhysicalSocket : public AsyncSocket { +public: + PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET) + : ss_(ss), s_(s), enabled_events_(0), error_(0), + state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED) { + if (s != INVALID_SOCKET) + enabled_events_ = kfRead | kfWrite; + } + + virtual ~PhysicalSocket() { + Close(); + } + + // Creates the underlying OS socket (same as the "socket" function). + virtual bool Create(int type) { + Close(); + s_ = ::socket(AF_INET, type, 0); + UpdateLastError(); + enabled_events_ = kfRead | kfWrite; + return s_ != INVALID_SOCKET; + } + + SocketAddress GetLocalAddress() const { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(s_, (struct sockaddr*)&addr, &addrlen); + assert(addrlen == sizeof(addr)); + if (result >= 0) { + return SocketAddress(NetworkToHost32(addr.sin_addr.s_addr), + NetworkToHost16(addr.sin_port)); + } else { + return SocketAddress(); + } + } + + SocketAddress GetRemoteAddress() const { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(s_, (struct sockaddr*)&addr, &addrlen); + assert(addrlen == sizeof(addr)); + if (result >= 0) { + return SocketAddress( + NetworkToHost32(addr.sin_addr.s_addr), + NetworkToHost16(addr.sin_port)); + } else { + assert(errno == ENOTCONN); + return SocketAddress(); + } + } + + int Bind(const SocketAddress& addr) { + struct sockaddr_in saddr; + IP2SA(&addr, &saddr); + int err = ::bind(s_, (struct sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + return err; + } + + int Connect(const SocketAddress& addr) { + // TODO: Implicit creation is required to reconnect... + // ...but should we make it more explicit? + if ((s_ == INVALID_SOCKET) && !Create(SOCK_STREAM)) + return SOCKET_ERROR; + SocketAddress addr2(addr); + if (addr2.IsUnresolved()) { + LOG(INFO) << "Resolving addr in PhysicalSocket::Connect"; + addr2.Resolve(); // TODO: Do this async later? + } + struct sockaddr_in saddr; + IP2SA(&addr2, &saddr); + int err = ::connect(s_, (struct sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_; + if (err == 0) { + state_ = CS_CONNECTED; + } else if (IsBlockingError(error_)) { + state_ = CS_CONNECTING; + enabled_events_ |= kfConnect; + } + return err; + } + + int GetError() const { + return error_; + } + + void SetError(int error) { + error_ = error; + } + + ConnState GetState() const { + return state_; + } + + int SetOption(Option opt, int value) { + assert(opt == OPT_DONTFRAGMENT); +#ifdef WIN32 + value = (value == 0) ? 0 : 1; + return ::setsockopt( + s_, IPPROTO_IP, IP_DONTFRAGMENT, reinterpret_cast<char*>(&value), + sizeof(value)); +#endif +#ifdef __linux + value = (value == 0) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO; + return ::setsockopt( + s_, IPPROTO_IP, IP_MTU_DISCOVER, &value, sizeof(value)); +#endif +#ifdef OSX + // This is not possible on OSX. + return -1; +#endif + } + + int Send(const void *pv, size_t cb) { + int sent = ::send(s_, reinterpret_cast<const char *>(pv), (int)cb, 0); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_; + ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false + if ((sent < 0) && IsBlockingError(error_)) { + enabled_events_ |= kfWrite; + } + return sent; + } + + int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + struct sockaddr_in saddr; + IP2SA(&addr, &saddr); + int sent = ::sendto( + s_, (const char *)pv, (int)cb, 0, (struct sockaddr*)&saddr, + sizeof(saddr)); + UpdateLastError(); + ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false + if ((sent < 0) && IsBlockingError(error_)) { + enabled_events_ |= kfWrite; + } + return sent; + } + + int Recv(void *pv, size_t cb) { + int received = ::recv(s_, (char *)pv, (int)cb, 0); + UpdateLastError(); + if ((received >= 0) || IsBlockingError(error_)) { + enabled_events_ |= kfRead; + } + return received; + } + + int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + struct sockaddr saddr; + socklen_t cbAddr = sizeof(saddr); + int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, &saddr, &cbAddr); + UpdateLastError(); + if ((received >= 0) && (paddr != NULL)) + SA2IP(&saddr, paddr); + if ((received >= 0) || IsBlockingError(error_)) { + enabled_events_ |= kfRead; + } + return received; + } + + int Listen(int backlog) { + int err = ::listen(s_, backlog); + UpdateLastError(); + if (err == 0) + state_ = CS_CONNECTING; + return err; + } + + Socket* Accept(SocketAddress *paddr) { + struct sockaddr saddr; + socklen_t cbAddr = sizeof(saddr); + SOCKET s = ::accept(s_, &saddr, &cbAddr); + UpdateLastError(); + if (s == INVALID_SOCKET) + return NULL; + if (paddr != NULL) + SA2IP(&saddr, paddr); + return ss_->WrapSocket(s); + } + + int Close() { + if (s_ == INVALID_SOCKET) + return 0; + int err = ::closesocket(s_); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Close() Ret: " << err << " Error: " << error_; + s_ = INVALID_SOCKET; + state_ = CS_CLOSED; + enabled_events_ = 0; + return err; + } + + int EstimateMTU(uint16* mtu) { + SocketAddress addr = GetRemoteAddress(); + if (addr.IsAny()) { + error_ = ENOTCONN; + return -1; + } + +#ifdef WIN32 + + WinPing ping; + if (!ping.IsValid()) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + + for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { + int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE; + if (ping.Ping(addr.ip(), size, 0, 1, false) != WinPing::PING_TOO_LARGE) { + *mtu = PACKET_MAXIMUMS[level]; + return 0; + } + } + + assert(false); + return 0; + +#endif // WIN32 + +#ifdef __linux + + int value; + socklen_t vlen = sizeof(value); + int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen); + if (err < 0) { + UpdateLastError(); + return err; + } + + assert((0 <= value) && (value <= 65536)); + *mtu = uint16(value); + return 0; + +#endif // __linux + + // TODO: OSX support + } + + SocketServer* socketserver() { return ss_; } + +protected: + PhysicalSocketServer* ss_; + SOCKET s_; + uint32 enabled_events_; + int error_; + ConnState state_; + + void UpdateLastError() { +#ifdef WIN32 + error_ = WSAGetLastError(); +#endif +#ifdef POSIX + error_ = errno; +#endif + } + + void IP2SA(const SocketAddress *paddr, struct sockaddr_in *psaddr) { + memset(psaddr, 0, sizeof(*psaddr)); + psaddr->sin_family = AF_INET; + psaddr->sin_port = HostToNetwork16(paddr->port()); + if (paddr->ip() == 0) + psaddr->sin_addr.s_addr = INADDR_ANY; + else + psaddr->sin_addr.s_addr = HostToNetwork32(paddr->ip()); + } + + void SA2IP(const struct sockaddr *psaddr, SocketAddress *paddr) { + const struct sockaddr_in *psaddr_in = + reinterpret_cast<const struct sockaddr_in*>(psaddr); + paddr->SetIP(NetworkToHost32(psaddr_in->sin_addr.s_addr)); + paddr->SetPort(NetworkToHost16(psaddr_in->sin_port)); + } +}; + +#ifdef POSIX +class Dispatcher { +public: + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; + virtual int GetDescriptor() = 0; +}; + +class EventDispatcher : public Dispatcher { +public: + EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) { + if (pipe(afd_) < 0) + LOG(LERROR) << "pipe failed"; + ss_->Add(this); + } + + virtual ~EventDispatcher() { + ss_->Remove(this); + close(afd_[0]); + close(afd_[1]); + } + + virtual void Signal() { + CritScope cs(&crit_); + if (!fSignaled_) { + uint8 b = 0; + if (write(afd_[1], &b, sizeof(b)) < 0) + LOG(LERROR) << "write failed"; + fSignaled_ = true; + } + } + + virtual uint32 GetRequestedEvents() { + return kfRead; + } + + virtual void OnPreEvent(uint32 ff) { + // It is not possible to perfectly emulate an auto-resetting event with + // pipes. This simulates it by resetting before the event is handled. + + CritScope cs(&crit_); + if (fSignaled_) { + uint8 b; + read(afd_[0], &b, sizeof(b)); + fSignaled_ = false; + } + } + + virtual void OnEvent(uint32 ff, int err) { + assert(false); + } + + virtual int GetDescriptor() { + return afd_[0]; + } + +private: + PhysicalSocketServer *ss_; + int afd_[2]; + bool fSignaled_; + CriticalSection crit_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { +public: + SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) { + ss_->Add(this); + } + SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) { + ss_->Add(this); + } + + virtual ~SocketDispatcher() { + ss_->Remove(this); + } + + bool Initialize() { + fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK); + return true; + } + + virtual bool Create(int type) { + // Change the socket to be non-blocking. + if (!PhysicalSocket::Create(type)) + return false; + + return Initialize(); + } + + virtual int GetDescriptor() { + return s_; + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & kfRead) != 0) { + enabled_events_ &= ~kfRead; + SignalReadEvent(this); + } + if ((ff & kfWrite) != 0) { + enabled_events_ &= ~kfWrite; + SignalWriteEvent(this); + } + if ((ff & kfConnect) != 0) { + enabled_events_ &= ~kfConnect; + SignalConnectEvent(this); + } + if ((ff & kfClose) != 0) + SignalCloseEvent(this, err); + } +}; + +class FileDispatcher: public Dispatcher, public AsyncFile { +public: + FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) { + set_readable(true); + + ss_->Add(this); + + fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK); + } + + virtual ~FileDispatcher() { + ss_->Remove(this); + } + + SocketServer* socketserver() { return ss_; } + + virtual int GetDescriptor() { + return fd_; + } + + virtual uint32 GetRequestedEvents() { + return flags_; + } + + virtual void OnPreEvent(uint32 ff) { + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & kfRead) != 0) + SignalReadEvent(this); + if ((ff & kfWrite) != 0) + SignalWriteEvent(this); + if ((ff & kfClose) != 0) + SignalCloseEvent(this, err); + } + + virtual bool readable() { + return (flags_ & kfRead) != 0; + } + + virtual void set_readable(bool value) { + flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead); + } + + virtual bool writable() { + return (flags_ & kfWrite) != 0; + } + + virtual void set_writable(bool value) { + flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite); + } + +private: + PhysicalSocketServer* ss_; + int fd_; + int flags_; +}; + +AsyncFile* PhysicalSocketServer::CreateFile(int fd) { + return new FileDispatcher(fd, this); +} + +#endif // POSIX + +#ifdef WIN32 +class Dispatcher { +public: + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; + virtual WSAEVENT GetWSAEvent() = 0; + virtual SOCKET GetSocket() = 0; + virtual bool CheckSignalClose() = 0; +}; + +uint32 FlagsToEvents(uint32 events) { + uint32 ffFD = FD_CLOSE | FD_ACCEPT; + if (events & kfRead) + ffFD |= FD_READ; + if (events & kfWrite) + ffFD |= FD_WRITE; + if (events & kfConnect) + ffFD |= FD_CONNECT; + return ffFD; +} + +class EventDispatcher : public Dispatcher { +public: + EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) { + if (hev_ = WSACreateEvent()) { + ss_->Add(this); + } + } + + ~EventDispatcher() { + if (hev_ != NULL) { + ss_->Remove(this); + WSACloseEvent(hev_); + hev_ = NULL; + } + } + + virtual void Signal() { + if (hev_ != NULL) + WSASetEvent(hev_); + } + + virtual uint32 GetRequestedEvents() { + return 0; + } + + virtual void OnPreEvent(uint32 ff) { + WSAResetEvent(hev_); + } + + virtual void OnEvent(uint32 ff, int err) { + } + + virtual WSAEVENT GetWSAEvent() { + return hev_; + } + + virtual SOCKET GetSocket() { + return INVALID_SOCKET; + } + + virtual bool CheckSignalClose() { return false; } + +private: + PhysicalSocketServer* ss_; + WSAEVENT hev_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { +public: + static int next_id_; + int id_; + bool signal_close_; + int signal_err_; + + SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) { + } + SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) { + } + + virtual ~SocketDispatcher() { + Close(); + } + + bool Initialize() { + assert(s_ != INVALID_SOCKET); + // Must be a non-blocking + u_long argp = 1; + ioctlsocket(s_, FIONBIO, &argp); + ss_->Add(this); + return true; + } + + virtual bool Create(int type) { + // Create socket + if (!PhysicalSocket::Create(type)) + return false; + + if (!Initialize()) + return false; + + do { id_ = ++next_id_; } while (id_ == 0); + return true; + } + + virtual int Close() { + if (s_ == INVALID_SOCKET) + return 0; + + id_ = 0; + signal_close_ = false; + ss_->Remove(this); + return PhysicalSocket::Close(); + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + if ((ff & kfConnect) != 0) + state_ = CS_CONNECTED; + } + + virtual void OnEvent(uint32 ff, int err) { + int cache_id = id_; + if ((ff & kfRead) != 0) { + enabled_events_ &= ~kfRead; + SignalReadEvent(this); + } + if (((ff & kfWrite) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~kfWrite; + SignalWriteEvent(this); + } + if (((ff & kfConnect) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~kfConnect; + SignalConnectEvent(this); + } + if (((ff & kfClose) != 0) && (id_ == cache_id)) { + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] OnClose() Error: " << err; + signal_close_ = true; + signal_err_ = err; + } + } + + virtual WSAEVENT GetWSAEvent() { + return WSA_INVALID_EVENT; + } + + virtual SOCKET GetSocket() { + return s_; + } + + virtual bool CheckSignalClose() { + if (!signal_close_) + return false; + + char ch; + if (recv(s_, &ch, 1, MSG_PEEK) > 0) + return false; + + signal_close_ = false; + SignalCloseEvent(this, signal_err_); + return true; + } +}; + +int SocketDispatcher::next_id_ = 0; + +#endif // WIN32 + +// Sets the value of a boolean value to false when signaled. +class Signaler : public EventDispatcher { +public: + Signaler(PhysicalSocketServer* ss, bool* pf) + : EventDispatcher(ss), pf_(pf) { + } + virtual ~Signaler() { } + + void OnEvent(uint32 ff, int err) { + if (pf_) + *pf_ = false; + } + +private: + bool *pf_; +}; + +PhysicalSocketServer::PhysicalSocketServer() : fWait_(false), + last_tick_tracked_(0), last_tick_dispatch_count_(0) { + signal_wakeup_ = new Signaler(this, &fWait_); +} + +PhysicalSocketServer::~PhysicalSocketServer() { + delete signal_wakeup_; +} + +void PhysicalSocketServer::WakeUp() { + signal_wakeup_->Signal(); +} + +Socket* PhysicalSocketServer::CreateSocket(int type) { + PhysicalSocket* socket = new PhysicalSocket(this); + if (socket->Create(type)) { + return socket; + } else { + delete socket; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) { + SocketDispatcher* dispatcher = new SocketDispatcher(this); + if (dispatcher->Create(type)) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) { + SocketDispatcher* dispatcher = new SocketDispatcher(s, this); + if (dispatcher->Initialize()) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +void PhysicalSocketServer::Add(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + dispatchers_.push_back(pdispatcher); +} + +void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + dispatchers_.erase(std::remove(dispatchers_.begin(), dispatchers_.end(), pdispatcher), dispatchers_.end()); +} + +#ifdef POSIX +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { + // Calculate timing information + + struct timeval *ptvWait = NULL; + struct timeval tvWait; + struct timeval tvStop; + if (cmsWait != -1) { + // Calculate wait timeval + tvWait.tv_sec = cmsWait / 1000; + tvWait.tv_usec = (cmsWait % 1000) * 1000; + ptvWait = &tvWait; + + // Calculate when to return in a timeval + gettimeofday(&tvStop, NULL); + tvStop.tv_sec += tvWait.tv_sec; + tvStop.tv_usec += tvWait.tv_usec; + if (tvStop.tv_usec >= 1000000) { + tvStop.tv_usec -= 1000000; + tvStop.tv_sec += 1; + } + } + + // Zero all fd_sets. Don't need to do this inside the loop since + // select() zeros the descriptors not signaled + + fd_set fdsRead; + FD_ZERO(&fdsRead); + fd_set fdsWrite; + FD_ZERO(&fdsWrite); + + fWait_ = true; + + while (fWait_) { + int fdmax = -1; + { + CritScope cr(&crit_); + for (unsigned i = 0; i < dispatchers_.size(); i++) { + // Query dispatchers for read and write wait state + + Dispatcher *pdispatcher = dispatchers_[i]; + assert(pdispatcher); + if (!process_io && (pdispatcher != signal_wakeup_)) + continue; + int fd = pdispatcher->GetDescriptor(); + if (fd > fdmax) + fdmax = fd; + uint32 ff = pdispatcher->GetRequestedEvents(); + if (ff & kfRead) + FD_SET(fd, &fdsRead); + if (ff & (kfWrite | kfConnect)) + FD_SET(fd, &fdsWrite); + } + } + + // Wait then call handlers as appropriate + // < 0 means error + // 0 means timeout + // > 0 means count of descriptors ready + int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); + + // If error, return error + // todo: do something intelligent + + if (n < 0) + return false; + + // If timeout, return success + + if (n == 0) + return true; + + // We have signaled descriptors + + { + CritScope cr(&crit_); + for (unsigned i = 0; i < dispatchers_.size(); i++) { + Dispatcher *pdispatcher = dispatchers_[i]; + int fd = pdispatcher->GetDescriptor(); + uint32 ff = 0; + if (FD_ISSET(fd, &fdsRead)) { + FD_CLR(fd, &fdsRead); + ff |= kfRead; + } + if (FD_ISSET(fd, &fdsWrite)) { + FD_CLR(fd, &fdsWrite); + if (pdispatcher->GetRequestedEvents() & kfConnect) { + ff |= kfConnect; + } else { + ff |= kfWrite; + } + } + if (ff != 0) { + pdispatcher->OnPreEvent(ff); + pdispatcher->OnEvent(ff, 0); + } + } + } + + // Recalc the time remaining to wait. Doing it here means it doesn't get + // calced twice the first time through the loop + + if (cmsWait != -1) { + ptvWait->tv_sec = 0; + ptvWait->tv_usec = 0; + struct timeval tvT; + gettimeofday(&tvT, NULL); + if (tvStop.tv_sec >= tvT.tv_sec) { + ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; + ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; + if (ptvWait->tv_usec < 0) { + ptvWait->tv_usec += 1000000; + ptvWait->tv_sec -= 1; + } + } + } + } + + return true; +} +#endif // POSIX + +#ifdef WIN32 +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) +{ + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = GetMillisecondCount(); + +#if LOGGING + if (last_tick_dispatch_count_ == 0) { + last_tick_tracked_ = msStart; + } +#endif + + WSAEVENT socket_ev = WSACreateEvent(); + + fWait_ = true; + while (fWait_) { + std::vector<WSAEVENT> events; + std::vector<Dispatcher *> event_owners; + + events.push_back(socket_ev); + + { + CritScope cr(&crit_); + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher * disp = dispatchers_[i]; + if (!process_io && (disp != signal_wakeup_)) + continue; + SOCKET s = disp->GetSocket(); + if (disp->CheckSignalClose()) { + // We just signalled close, don't poll this socket + } else if (s != INVALID_SOCKET) { + WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents())); + } else { + events.push_back(disp->GetWSAEvent()); + event_owners.push_back(disp); + } + } + } + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == -1) { + cmsNext = cmsWait; + } else { + cmsNext = cmsTotal - cmsElapsed; + if (cmsNext < 0) + cmsNext = 0; + } + + // Wait for one of the events to signal + DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()), &events[0], false, cmsNext, false); + +#if 0 // LOGGING + // we track this information purely for logging purposes. + last_tick_dispatch_count_++; + if (last_tick_dispatch_count_ >= 1000) { + uint32 now = GetMillisecondCount(); + LOG(INFO) << "PhysicalSocketServer took " << TimeDiff(now, last_tick_tracked_) << "ms for 1000 events"; + + // If we get more than 1000 events in a second, we are spinning badly + // (normally it should take about 8-20 seconds). + assert(TimeDiff(now, last_tick_tracked_) > 1000); + + last_tick_tracked_ = now; + last_tick_dispatch_count_ = 0; + } +#endif + + // Failed? + // todo: need a better strategy than this! + + if (dw == WSA_WAIT_FAILED) { + int error = WSAGetLastError(); + assert(false); + WSACloseEvent(socket_ev); + return false; + } + + // Timeout? + + if (dw == WSA_WAIT_TIMEOUT) { + WSACloseEvent(socket_ev); + return true; + } + + // Figure out which one it is and call it + + { + CritScope cr(&crit_); + int index = dw - WSA_WAIT_EVENT_0; + if (index > 0) { + --index; // The first event is the socket event + event_owners[index]->OnPreEvent(0); + event_owners[index]->OnEvent(0, 0); + } else if (process_io) { + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher * disp = dispatchers_[i]; + SOCKET s = disp->GetSocket(); + if (s == INVALID_SOCKET) + continue; + + WSANETWORKEVENTS wsaEvents; + int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents); + if (err == 0) { + +#if LOGGING + { + if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + } +#endif + uint32 ff = 0; + int errcode = 0; + if (wsaEvents.lNetworkEvents & FD_READ) + ff |= kfRead; + if (wsaEvents.lNetworkEvents & FD_WRITE) + ff |= kfWrite; + if (wsaEvents.lNetworkEvents & FD_CONNECT) { + if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) { + ff |= kfConnect; + } else { + // TODO: Decide whether we want to signal connect, but with an error code + ff |= kfClose; + errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + } + if (wsaEvents.lNetworkEvents & FD_ACCEPT) + ff |= kfRead; + if (wsaEvents.lNetworkEvents & FD_CLOSE) { + ff |= kfClose; + errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + if (ff != 0) { + disp->OnPreEvent(ff); + disp->OnEvent(ff, errcode); + } + } + } + } + + // Reset the network event until new activity occurs + WSAResetEvent(socket_ev); + } + + // Break? + + if (!fWait_) + break; + cmsElapsed = GetMillisecondCount() - msStart; + if (cmsWait != -1) { + if (cmsElapsed >= cmsWait) + break; + } + } + + // Done + + WSACloseEvent(socket_ev); + return true; +} +#endif // WIN32 + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h new file mode 100644 index 00000000..305b64d9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h @@ -0,0 +1,80 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PHYSICALSOCKETSERVER_H__ +#define __PHYSICALSOCKETSERVER_H__ + +#include "talk/base/asyncfile.h" +#include "talk/base/socketserver.h" +#include "talk/base/criticalsection.h" +#include <vector> + +#ifdef POSIX +typedef int SOCKET; +#endif // POSIX + +namespace cricket { + +class Dispatcher; +class Signaler; + +// A socket server that provides the real sockets of the underlying OS. +class PhysicalSocketServer : public SocketServer { +public: + PhysicalSocketServer(); + virtual ~PhysicalSocketServer(); + + // SocketFactory: + virtual Socket* CreateSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + + // Internal Factory for Accept + AsyncSocket* WrapSocket(SOCKET s); + + // SocketServer: + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + void Add(Dispatcher* dispatcher); + void Remove(Dispatcher* dispatcher); + +#ifdef POSIX + AsyncFile* CreateFile(int fd); +#endif + +private: + std::vector<Dispatcher*> dispatchers_; + Signaler* signal_wakeup_; + CriticalSection crit_; + bool fWait_; + uint32 last_tick_tracked_; + int last_tick_dispatch_count_; +}; + +} // namespace cricket + +#endif // __PHYSICALSOCKETSERVER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h new file mode 100644 index 00000000..1bd817b9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h @@ -0,0 +1,52 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PROXYINFO_H__ +#define __PROXYINFO_H__ + +#include <string> +#include "talk/base/socketaddress.h" +// TODO: move xmpppassword into base +#include "talk/xmpp/xmpppassword.h" + +namespace cricket { + +enum ProxyType { PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN }; +const char * ProxyToString(ProxyType proxy); + +struct ProxyInfo { + ProxyType type; + SocketAddress address; + std::string username; + buzz::XmppPassword password; + + ProxyInfo() : type(PROXY_NONE) { } +}; + +} // namespace cricket + +#endif // __PROXYINFO_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h new file mode 100644 index 00000000..0470ff83 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h @@ -0,0 +1,259 @@ +#ifndef SCOPED_PTR_H +#define SCOPED_PTR_H + +// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. +// Copyright (c) 2001, 2002 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation. +// + +// scoped_ptr mimics a built-in pointer except that it guarantees deletion +// of the object pointed to, either on destruction of the scoped_ptr or via +// an explicit reset(). scoped_ptr is a simple solution for simple needs; +// use shared_ptr or std::auto_ptr if your needs are more complex. + +// scoped_ptr_malloc added in by Google. When one of +// these goes out of scope, instead of doing a delete or delete[], it +// calls free(). scoped_ptr_malloc<char> is likely to see much more +// use than any other specializations. + +// release() added in by Google. Use this to conditionally +// transfer ownership of a heap-allocated object to the caller, usually on +// method success. + +#include <cstddef> // for std::ptrdiff_t +#include <assert.h> // for assert +#include <stdlib.h> // for free() decl + +#ifdef _WIN32 +namespace std { using ::ptrdiff_t; }; +#endif // _WIN32 + +namespace buzz { + +template <typename T> +class scoped_ptr { + private: + + T* ptr; + + scoped_ptr(scoped_ptr const &); + scoped_ptr & operator=(scoped_ptr const &); + + public: + + typedef T element_type; + + explicit scoped_ptr(T* p = 0): ptr(p) {} + + ~scoped_ptr() { + typedef char type_must_be_complete[sizeof(T)]; + delete ptr; + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + delete ptr; + ptr = p; + } + } + + T& operator*() const { + assert(ptr != 0); + return *ptr; + } + + T* operator->() const { + assert(ptr != 0); + return ptr; + } + + T* get() const { + return ptr; + } + + void swap(scoped_ptr & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + T** accept() { + if (ptr) { + delete ptr; + ptr = 0; + } + return &ptr; + } + + T** use() { + return &ptr; + } +}; + +template<typename T> inline +void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) { + a.swap(b); +} + + + + +// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to +// is guaranteed, either on destruction of the scoped_array or via an explicit +// reset(). Use shared_array or std::vector if your needs are more complex. + +template<typename T> +class scoped_array { + private: + + T* ptr; + + scoped_array(scoped_array const &); + scoped_array & operator=(scoped_array const &); + + public: + + typedef T element_type; + + explicit scoped_array(T* p = 0) : ptr(p) {} + + ~scoped_array() { + typedef char type_must_be_complete[sizeof(T)]; + delete[] ptr; + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + delete [] ptr; + ptr = p; + } + } + + T& operator[](std::ptrdiff_t i) const { + assert(ptr != 0); + assert(i >= 0); + return ptr[i]; + } + + T* get() const { + return ptr; + } + + void swap(scoped_array & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + T** accept() { + if (ptr) { + delete [] ptr; + ptr = 0; + } + return &ptr; + } +}; + +template<class T> inline +void swap(scoped_array<T>& a, scoped_array<T>& b) { + a.swap(b); +} + +// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a +// second template argument, the function used to free the object. + +template<typename T, void (*FF)(void*) = free> class scoped_ptr_malloc { + private: + + T* ptr; + + scoped_ptr_malloc(scoped_ptr_malloc const &); + scoped_ptr_malloc & operator=(scoped_ptr_malloc const &); + + public: + + typedef T element_type; + + explicit scoped_ptr_malloc(T* p = 0): ptr(p) {} + + ~scoped_ptr_malloc() { + typedef char type_must_be_complete[sizeof(T)]; + FF(static_cast<void*>(ptr)); + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + FF(static_cast<void*>(ptr)); + ptr = p; + } + } + + T& operator*() const { + assert(ptr != 0); + return *ptr; + } + + T* operator->() const { + assert(ptr != 0); + return ptr; + } + + T* get() const { + return ptr; + } + + void swap(scoped_ptr_malloc & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + T** accept() { + if (ptr) { + FF(static_cast<void*>(ptr)); + ptr = 0; + } + return &ptr; + } +}; + +template<typename T, void (*FF)(void*)> inline +void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) { + a.swap(b); +} + +} + +using buzz::scoped_ptr; + +#endif // #ifndef SCOPED_PTR_H diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h new file mode 100644 index 00000000..446516b8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h @@ -0,0 +1,2700 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// + +#ifndef SIGSLOT_H__ +#define SIGSLOT_H__ + +#include <set> +#include <list> + +// On our copy of sigslot.h, we force single threading +#define SIGSLOT_PURE_ISO + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WIN32) +# define _SIGSLOT_HAS_WIN32_THREADS +# include <windows.h> +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include <pthread.h> +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + + +namespace sigslot { + + class single_threaded + { + public: + single_threaded() + { + ; + } + + virtual ~single_threaded() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + + template<class mt_policy> + class lock_block + { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } + }; + + template<class mt_policy> + class has_slots; + + template<class mt_policy> + class _connection_base0 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class mt_policy> + class _connection_base1 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type) = 0; + virtual _connection_base1<arg1_type, mt_policy>* clone() = 0; + virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class mt_policy> + class _connection_base2 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type) = 0; + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() = 0; + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class mt_policy> + class _connection_base3 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0; + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy> + class _connection_base4 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() = 0; + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class mt_policy> + class _connection_base5 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* clone() = 0; + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class mt_policy> + class _connection_base6 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* clone() = 0; + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class mt_policy> + class _connection_base7 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* clone() = 0; + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy> + class _connection_base8 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() = 0; + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class mt_policy> + class _signal_base : public mt_policy + { + public: + virtual void slot_disconnect(has_slots<mt_policy>* pslot) = 0; + virtual void slot_duplicate(const has_slots<mt_policy>* poldslot, has_slots<mt_policy>* pnewslot) = 0; + }; + + template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class has_slots : public mt_policy + { + private: + typedef typename std::set<_signal_base<mt_policy> *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + + public: + has_slots() + { + ; + } + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block<mt_policy> lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base<mt_policy>* sender) + { + lock_block<mt_policy> lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base<mt_policy>* sender) + { + lock_block<mt_policy> lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template<class mt_policy> + class _signal_base0 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base0<mt_policy> *> connections_list; + + _signal_base0() + { + ; + } + + _signal_base0(const _signal_base0& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class mt_policy> + class _signal_base1 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base1<arg1_type, mt_policy> *> connections_list; + + _signal_base1() + { + ; + } + + _signal_base1(const _signal_base1<arg1_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class mt_policy> + class _signal_base2 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base2<arg1_type, arg2_type, mt_policy> *> + connections_list; + + _signal_base2() + { + ; + } + + _signal_base2(const _signal_base2<arg1_type, arg2_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class mt_policy> + class _signal_base3 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *> + connections_list; + + _signal_base3() + { + ; + } + + _signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy> + class _signal_base4 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base4<arg1_type, arg2_type, arg3_type, + arg4_type, mt_policy> *> connections_list; + + _signal_base4() + { + ; + } + + _signal_base4(const _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class mt_policy> + class _signal_base5 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base5<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, mt_policy> *> connections_list; + + _signal_base5() + { + ; + } + + _signal_base5(const _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class mt_policy> + class _signal_base6 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base6<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, mt_policy> *> connections_list; + + _signal_base6() + { + ; + } + + _signal_base6(const _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class mt_policy> + class _signal_base7 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base7<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> *> connections_list; + + _signal_base7() + { + ; + } + + _signal_base7(const _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy> + class _signal_base8 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base8<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> *> + connections_list; + + _signal_base8() + { + ; + } + + _signal_base8(const _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template<class dest_type, class mt_policy> + class _connection0 : public _connection_base0<mt_policy> + { + public: + _connection0() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base0<mt_policy>* clone() + { + return new _connection0<dest_type, mt_policy>(*this); + } + + virtual _connection_base0<mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection0<dest_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); + }; + + template<class dest_type, class arg1_type, class mt_policy> + class _connection1 : public _connection_base1<arg1_type, mt_policy> + { + public: + _connection1() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base1<arg1_type, mt_policy>* clone() + { + return new _connection1<dest_type, arg1_type, mt_policy>(*this); + } + + virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection1<dest_type, arg1_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class mt_policy> + class _connection2 : public _connection_base2<arg1_type, arg2_type, mt_policy> + { + public: + _connection2() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() + { + return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>(*this); + } + + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy> + class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> + { + public: + _connection3() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() + { + return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this); + } + + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class mt_policy> + class _connection4 : public _connection_base4<arg1_type, arg2_type, + arg3_type, arg4_type, mt_policy> + { + public: + _connection4() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() + { + return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(*this); + } + + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class mt_policy> + class _connection5 : public _connection_base5<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, mt_policy> + { + public: + _connection5() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* clone() + { + return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>(*this); + } + + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class arg6_type, class mt_policy> + class _connection6 : public _connection_base6<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> + { + public: + _connection6() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* clone() + { + return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>(*this); + } + + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class arg6_type, class arg7_type, class mt_policy> + class _connection7 : public _connection_base7<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> + { + public: + _connection7() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* clone() + { + return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>(*this); + } + + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class arg6_type, class arg7_type, + class arg8_type, class mt_policy> + class _connection8 : public _connection_base8<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> + { + public: + _connection8() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() + { + return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(*this); + } + + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal0 : public _signal_base0<mt_policy> + { + public: + typedef _signal_base0<mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal0() + { + ; + } + + signal0(const signal0<mt_policy>& s) + : _signal_base0<mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block<mt_policy> lock(this); + _connection0<desttype, mt_policy>* conn = + new _connection0<desttype, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal1 : public _signal_base1<arg1_type, mt_policy> + { + public: + typedef _signal_base1<arg1_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal1() + { + ; + } + + signal1(const signal1<arg1_type, mt_policy>& s) + : _signal_base1<arg1_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block<mt_policy> lock(this); + _connection1<desttype, arg1_type, mt_policy>* conn = + new _connection1<desttype, arg1_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal2 : public _signal_base2<arg1_type, arg2_type, mt_policy> + { + public: + typedef _signal_base2<arg1_type, arg2_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal2() + { + ; + } + + signal2(const signal2<arg1_type, arg2_type, mt_policy>& s) + : _signal_base2<arg1_type, arg2_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block<mt_policy> lock(this); + _connection2<desttype, arg1_type, arg2_type, mt_policy>* conn = new + _connection2<desttype, arg1_type, arg2_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> + { + public: + typedef _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal3() + { + ; + } + + signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s) + : _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block<mt_policy> lock(this); + _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn = + new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass, + pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal4 : public _signal_base4<arg1_type, arg2_type, arg3_type, + arg4_type, mt_policy> + { + public: + typedef _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal4() + { + ; + } + + signal4(const signal4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s) + : _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block<mt_policy> lock(this); + _connection4<desttype, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* + conn = new _connection4<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal5 : public _signal_base5<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, mt_policy> + { + public: + typedef _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal5() + { + ; + } + + signal5(const signal5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>& s) + : _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block<mt_policy> lock(this); + _connection5<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* conn = new _connection5<desttype, arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal6 : public _signal_base6<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, mt_policy> + { + public: + typedef _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal6() + { + ; + } + + signal6(const signal6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>& s) + : _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block<mt_policy> lock(this); + _connection6<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* conn = + new _connection6<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal7 : public _signal_base7<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> + { + public: + typedef _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal7() + { + ; + } + + signal7(const signal7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>& s) + : _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block<mt_policy> lock(this); + _connection7<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* conn = + new _connection7<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal8 : public _signal_base8<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> + { + public: + typedef _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal8() + { + ; + } + + signal8(const signal8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s) + : _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block<mt_policy> lock(this); + _connection8<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* conn = + new _connection8<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, + arg8_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace sigslot + +#endif // SIGSLOT_H__ + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h new file mode 100644 index 00000000..d4a49d96 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h @@ -0,0 +1,158 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _socket_h_ +#define _socket_h_ + +#include "talk/base/basictypes.h" +#include "talk/base/socketaddress.h" + +#ifdef POSIX +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <errno.h> +#endif + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +// Rather than converting errors into a private namespace, +// Reuse the POSIX socket api errors. Note this depends on +// Win32 compatibility. + +#ifdef WIN32 +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ENOTSOCK WSAENOTSOCK +#define EDESTADDRREQ WSAEDESTADDRREQ +#define EMSGSIZE WSAEMSGSIZE +#define EPROTOTYPE WSAEPROTOTYPE +#define ENOPROTOOPT WSAENOPROTOOPT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#define ENETRESET WSAENETRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ENOBUFS WSAENOBUFS +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define ELOOP WSAELOOP +#undef ENAMETOOLONG // remove errno.h's definition +#define ENAMETOOLONG WSAENAMETOOLONG +#define EHOSTDOWN WSAEHOSTDOWN +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTEMPTY // remove errno.h's definition +#define ENOTEMPTY WSAENOTEMPTY +#define EPROCLIM WSAEPROCLIM +#define EUSERS WSAEUSERS +#define EDQUOT WSAEDQUOT +#define ESTALE WSAESTALE +#define EREMOTE WSAEREMOTE +#undef EACCES +#define EACCES WSAEACCES +#endif // WIN32 + +#ifdef POSIX +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define closesocket(s) close(s) +#endif // POSIX + +namespace cricket { + +inline bool IsBlockingError(int e) { + return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS); +} + +// General interface for the socket implementations of various networks. The +// methods match those of normal UNIX sockets very closely. +class Socket { +public: + virtual ~Socket() {} + + // Returns the address to which the socket is bound. If the socket is not + // bound, then the any-address is returned. + virtual SocketAddress GetLocalAddress() const = 0; + + // Returns the address to which the socket is connected. If the socket is + // not connected, then the any-address is returned. + virtual SocketAddress GetRemoteAddress() const = 0; + + virtual int Bind(const SocketAddress& addr) = 0; + virtual int Connect(const SocketAddress& addr) = 0; + virtual int Send(const void *pv, size_t cb) = 0; + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0; + virtual int Recv(void *pv, size_t cb) = 0; + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) = 0; + virtual int Listen(int backlog) = 0; + virtual Socket *Accept(SocketAddress *paddr) = 0; + virtual int Close() = 0; + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + inline bool IsBlocking() const { return IsBlockingError(GetError()); } + + enum ConnState { + CS_CLOSED, + CS_CONNECTING, + CS_CONNECTED + }; + virtual ConnState GetState() const = 0; + + // Fills in the given uint16 with the current estimate of the MTU along the + // path to the address to which this socket is connected. + virtual int EstimateMTU(uint16* mtu) = 0; + + enum Option { + OPT_DONTFRAGMENT + }; + virtual int SetOption(Option opt, int value) = 0; + +protected: + Socket() {} + +private: + DISALLOW_EVIL_CONSTRUCTORS(Socket); +}; + +} // namespace cricket + +#endif // _socket_h_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc new file mode 100644 index 00000000..049e923c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc @@ -0,0 +1,1130 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <time.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#define _WINSOCKAPI_ +#include <windows.h> +#include <wininet.h> // HTTP_STATUS_PROXY_AUTH_REQ +#define SECURITY_WIN32 +#include <security.h> +#endif + +#include <cassert> + +#include "talk/base/base64.h" +#include "talk/base/basicdefs.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/md5.h" +#include "talk/base/socketadapters.h" +#include "talk/base/stringutils.h" + +#include <errno.h> + + +#ifdef WIN32 +#include "talk/base/sec_buffer.h" +#endif // WIN32 + +namespace cricket { + +#ifdef WIN32 +extern const ConstantLabel SECURITY_ERRORS[]; +#endif + +BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size) + : AsyncSocketAdapter(socket), buffer_size_(buffer_size), data_len_(0), buffering_(false) { + buffer_ = new char[buffer_size_]; +} + +BufferedReadAdapter::~BufferedReadAdapter() { + delete [] buffer_; +} + +int BufferedReadAdapter::Send(const void *pv, size_t cb) { + if (buffering_) { + // TODO: Spoof error better; Signal Writeable + socket_->SetError(EWOULDBLOCK); + return -1; + } + return AsyncSocketAdapter::Send(pv, cb); +} + +int BufferedReadAdapter::Recv(void *pv, size_t cb) { + if (buffering_) { + socket_->SetError(EWOULDBLOCK); + return -1; + } + + size_t read = 0; + + if (data_len_) { + read = _min(cb, data_len_); + memcpy(pv, buffer_, read); + data_len_ -= read; + if (data_len_ > 0) { + memmove(buffer_, buffer_ + read, data_len_); + } + pv = static_cast<char *>(pv) + read; + cb -= read; + } + + // FIX: If cb == 0, we won't generate another read event + + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res < 0) + return res; + + return res + static_cast<int>(read); +} + +void BufferedReadAdapter::BufferInput(bool on) { + buffering_ = on; +} + +void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) { + assert(socket == socket_); + + if (!buffering_) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (data_len_ >= buffer_size_) { + LOG(INFO) << "Input buffer overflow"; + assert(false); + data_len_ = 0; + } + + int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "Recv: " << errno << " " << std::strerror(errno); + return; + } + + data_len_ += len; + + ProcessInput(buffer_, data_len_); +} + +/////////////////////////////////////////////////////////////////////////////// + +const uint8 SSL_SERVER_HELLO[] = { + 22,3,1,0,74,2,0,0,70,3,1,66,133,69,167,39,169,93,160, + 179,197,231,83,218,72,43,63,198,90,202,137,193,88,82, + 161,120,60,91,23,70,0,133,63,32,14,211,6,114,91,91, + 27,95,21,172,19,249,136,83,157,155,232,61,123,12,48, + 50,110,56,77,162,117,87,65,108,52,92,0,4,0 +}; + +const char SSL_CLIENT_HELLO[] = { + -128,70,1,3,1,0,45,0,0,0,16,1,0,-128,3,0,-128,7,0,-64,6,0,64,2,0, + -128,4,0,-128,0,0,4,0,-2,-1,0,0,10,0,-2,-2,0,0,9,0,0,100,0,0,98,0, + 0,3,0,0,6,31,23,12,-90,47,0,120,-4,70,85,46,-79,-125,57,-15,-22 +}; + +AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) : BufferedReadAdapter(socket, 1024) { +} + +int AsyncSSLSocket::Connect(const SocketAddress& addr) { + // Begin buffering before we connect, so that there isn't a race condition between + // potential senders and receiving the OnConnectEvent signal + BufferInput(true); + return BufferedReadAdapter::Connect(addr); +} + +void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) { + assert(socket == socket_); + + // TODO: we could buffer output too... + int res = DirectSend(SSL_CLIENT_HELLO, sizeof(SSL_CLIENT_HELLO)); + assert(res == sizeof(SSL_CLIENT_HELLO)); +} + +void AsyncSSLSocket::ProcessInput(char * data, size_t& len) { + if (len < sizeof(SSL_SERVER_HELLO)) + return; + + if (memcmp(SSL_SERVER_HELLO, data, sizeof(SSL_SERVER_HELLO)) != 0) { + Close(); + SignalCloseEvent(this, 0); // TODO: error code? + return; + } + + len -= sizeof(SSL_SERVER_HELLO); + if (len > 0) { + memmove(data, data + sizeof(SSL_SERVER_HELLO), len); + } + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); +} + +/////////////////////////////////////////////////////////////////////////////// + +#define TEST_DIGEST 0 +#if TEST_DIGEST +/* +const char * const DIGEST_CHALLENGE = + "Digest realm=\"testrealm@host.com\"," + " qop=\"auth,auth-int\"," + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; +const char * const DIGEST_METHOD = "GET"; +const char * const DIGEST_URI = + "/dir/index.html";; +const char * const DIGEST_CNONCE = + "0a4f113b"; +const char * const DIGEST_RESPONSE = + "6629fae49393a05397450978507c4ef1"; +//user_ = "Mufasa"; +//pass_ = "Circle Of Life"; +*/ +const char * const DIGEST_CHALLENGE = + "Digest realm=\"Squid proxy-caching web server\"," + " nonce=\"Nny4QuC5PwiSDixJ\"," + " qop=\"auth\"," + " stale=false"; +const char * const DIGEST_URI = + "/"; +const char * const DIGEST_CNONCE = + "6501d58e9a21cee1e7b5fec894ded024"; +const char * const DIGEST_RESPONSE = + "edffcb0829e755838b073a4a42de06bc"; +#endif + +static std::string MD5(const std::string& data) { + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(data.data())), static_cast<unsigned int>(data.size())); + unsigned char digest[16]; + MD5Final(digest, &ctx); + std::string hex_digest; + const char HEX[] = "0123456789abcdef"; + for (int i=0; i<16; ++i) { + hex_digest += HEX[digest[i] >> 4]; + hex_digest += HEX[digest[i] & 0xf]; + } + return hex_digest; +} + +static std::string Quote(const std::string& str) { + std::string result; + result.push_back('"'); + for (size_t i=0; i<str.size(); ++i) { + if ((str[i] == '"') || (str[i] == '\\')) + result.push_back('\\'); + result.push_back(str[i]); + } + result.push_back('"'); + return result; +} + +#ifdef WIN32 +struct NegotiateAuthContext : public AsyncHttpsProxySocket::AuthContext { + CredHandle cred; + CtxtHandle ctx; + size_t steps; + bool specified_credentials; + + NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2) + : AuthContext(auth), cred(c1), ctx(c2), steps(0), specified_credentials(false) { } + + virtual ~NegotiateAuthContext() { + DeleteSecurityContext(&ctx); + FreeCredentialsHandle(&cred); + } +}; +#endif // WIN32 + +AsyncHttpsProxySocket::AuthResult +AsyncHttpsProxySocket::Authenticate(const char * challenge, size_t len, + const SocketAddress& server, + const std::string& method, const std::string& uri, + const std::string& username, const buzz::XmppPassword& password, + AuthContext *& context, std::string& response, std::string& auth_method) { +#if TEST_DIGEST + challenge = DIGEST_CHALLENGE; + len = strlen(challenge); +#endif + + std::map<std::string, std::string> args; + ParseAuth(challenge, len, auth_method, args); + + if (context && (context->auth_method != auth_method)) + return AR_IGNORE; + + // BASIC + if (stricmp(auth_method.c_str(), "basic") == 0) { + if (context) + return AR_CREDENTIALS; // Bad credentials + if (username.empty()) + return AR_CREDENTIALS; // Missing credentials + + context = new AuthContext(auth_method); + + // TODO: convert sensitive to a secure buffer that gets securely deleted + //std::string decoded = username + ":" + password; + size_t len = username.size() + password.GetLength() + 2; + char * sensitive = new char[len]; + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + response = auth_method; + response.append(" "); + // TODO: create a sensitive-source version of Base64::encode + response.append(Base64::encode(sensitive)); + memset(sensitive, 0, len); + delete [] sensitive; + return AR_RESPONSE; + } + + // DIGEST + if (stricmp(auth_method.c_str(), "digest") == 0) { + if (context) + return AR_CREDENTIALS; // Bad credentials + if (username.empty()) + return AR_CREDENTIALS; // Missing credentials + + context = new AuthContext(auth_method); + + std::string cnonce, ncount; +#if TEST_DIGEST + method = DIGEST_METHOD; + uri = DIGEST_URI; + cnonce = DIGEST_CNONCE; +#else + char buffer[256]; + sprintf(buffer, "%d", time(0)); + cnonce = MD5(buffer); +#endif + ncount = "00000001"; + + // TODO: convert sensitive to be secure buffer + //std::string A1 = username + ":" + args["realm"] + ":" + password; + size_t len = username.size() + args["realm"].size() + password.GetLength() + 3; + char * sensitive = new char[len]; // A1 + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + pos += strcpyn(sensitive + pos, len - pos, args["realm"].c_str()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + std::string A2 = method + ":" + uri; + std::string middle; + if (args.find("qop") != args.end()) { + args["qop"] = "auth"; + middle = args["nonce"] + ":" + ncount + ":" + cnonce + ":" + args["qop"]; + } else { + middle = args["nonce"]; + } + std::string HA1 = MD5(sensitive); + memset(sensitive, 0, len); + delete [] sensitive; + std::string HA2 = MD5(A2); + std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2); + +#if TEST_DIGEST + assert(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0); +#endif + + std::stringstream ss; + ss << auth_method; + ss << " username=" << Quote(username); + ss << ", realm=" << Quote(args["realm"]); + ss << ", nonce=" << Quote(args["nonce"]); + ss << ", uri=" << Quote(uri); + if (args.find("qop") != args.end()) { + ss << ", qop=" << args["qop"]; + ss << ", nc=" << ncount; + ss << ", cnonce=" << Quote(cnonce); + } + ss << ", response=\"" << dig_response << "\""; + if (args.find("opaque") != args.end()) { + ss << ", opaque=" << Quote(args["opaque"]); + } + response = ss.str(); + return AR_RESPONSE; + } + +#ifdef WIN32 +#if 1 + bool want_negotiate = (stricmp(auth_method.c_str(), "negotiate") == 0); + bool want_ntlm = (stricmp(auth_method.c_str(), "ntlm") == 0); + // SPNEGO & NTLM + if (want_negotiate || want_ntlm) { + const size_t MAX_MESSAGE = 12000, MAX_SPN = 256; + char out_buf[MAX_MESSAGE], spn[MAX_SPN]; + +#if 0 // Requires funky windows versions + DWORD len = MAX_SPN; + if (DsMakeSpn("HTTP", server.IPAsString().c_str(), NULL, server.port(), 0, &len, spn) != ERROR_SUCCESS) { + LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) - DsMakeSpn failed"; + return AR_IGNORE; + } +#else + sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str()); +#endif + + SecBuffer out_sec; + out_sec.pvBuffer = out_buf; + out_sec.cbBuffer = sizeof(out_buf); + out_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc out_buf_desc; + out_buf_desc.ulVersion = 0; + out_buf_desc.cBuffers = 1; + out_buf_desc.pBuffers = &out_sec; + + const ULONG NEG_FLAGS_DEFAULT = + //ISC_REQ_ALLOCATE_MEMORY + ISC_REQ_CONFIDENTIALITY + //| ISC_REQ_EXTENDED_ERROR + //| ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + //| ISC_REQ_STREAM + //| ISC_REQ_USE_SUPPLIED_CREDS + ; + + TimeStamp lifetime; + SECURITY_STATUS ret = S_OK; + ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT; + + bool specify_credentials = !username.empty(); + size_t steps = 0; + + //uint32 now = cricket::Time(); + + NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context); + if (neg) { + const size_t max_steps = 10; + if (++neg->steps >= max_steps) { + LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries"; + return AR_ERROR; + } + steps = neg->steps; + + std::string decoded_challenge = Base64::decode(args[""]); + if (!decoded_challenge.empty()) { + SecBuffer in_sec; + in_sec.pvBuffer = const_cast<char *>(decoded_challenge.data()); + in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size()); + in_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc in_buf_desc; + in_buf_desc.ulVersion = 0; + in_buf_desc.cBuffers = 1; + in_buf_desc.pBuffers = &in_sec; + + ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + return AR_ERROR; + } + } else if (neg->specified_credentials) { + // Try again with default credentials + specify_credentials = false; + delete context; + context = neg = 0; + } else { + return AR_CREDENTIALS; + } + } + + if (!neg) { + unsigned char userbuf[256], passbuf[256], domainbuf[16]; + SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0; + if (specify_credentials) { + memset(&auth_id, 0, sizeof(auth_id)); + size_t len = password.GetLength()+1; + char * sensitive = new char[len]; + password.CopyTo(sensitive, true); + std::string::size_type pos = username.find('\\'); + if (pos == std::string::npos) { + auth_id.UserLength = static_cast<unsigned long>( + _min(sizeof(userbuf) - 1, username.size())); + memcpy(userbuf, username.c_str(), auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = 0; + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast<unsigned long>( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } else { + auth_id.UserLength = static_cast<unsigned long>( + _min(sizeof(userbuf) - 1, username.size() - pos - 1)); + memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = static_cast<unsigned long>( + _min(sizeof(domainbuf) - 1, pos)); + memcpy(domainbuf, username.c_str(), auth_id.DomainLength); + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast<unsigned long>( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } + memset(sensitive, 0, len); + delete [] sensitive; + auth_id.User = userbuf; + auth_id.Domain = domainbuf; + auth_id.Password = passbuf; + auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + pauth_id = &auth_id; + LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials"; + } else { + LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials"; + } + + CredHandle cred; + ret = AcquireCredentialsHandleA(0, want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A, SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime); + //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << cricket::TimeDiff(cricket::Time(), now); + if (ret != SEC_E_OK) { + LOG(LS_ERROR) << "AcquireCredentialsHandle error: " + << ErrorName(ret, SECURITY_ERRORS); + return AR_IGNORE; + } + + //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out; + + CtxtHandle ctx; + ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + FreeCredentialsHandle(&cred); + return AR_IGNORE; + } + + assert(!context); + context = neg = new NegotiateAuthContext(auth_method, cred, ctx); + neg->specified_credentials = specify_credentials; + neg->steps = steps; + } + + if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) { + ret = CompleteAuthToken(&neg->ctx, &out_buf_desc); + //LOG(INFO) << "$$$ CompleteAuthToken @ " << cricket::TimeDiff(cricket::Time(), now); + LOG(LS_VERBOSE) << "CompleteAuthToken returned: " + << ErrorName(ret, SECURITY_ERRORS); + if (FAILED(ret)) { + return AR_ERROR; + } + } + + //LOG(INFO) << "$$$ NEGOTIATE took " << cricket::TimeDiff(cricket::Time(), now) << "ms"; + + std::string decoded(out_buf, out_buf + out_sec.cbBuffer); + response = auth_method; + response.append(" "); + response.append(Base64::encode(decoded)); + return AR_RESPONSE; + } +#endif +#endif // WIN32 + + return AR_IGNORE; +} + +inline bool end_of_name(size_t pos, size_t len, const char * data) { + if (pos >= len) + return true; + if (isspace(data[pos])) + return true; + // The reason for this complexity is that some non-compliant auth schemes (like Negotiate) + // use base64 tokens in the challenge instead of name=value. This could confuse us when the + // base64 ends in equal signs. + if ((pos+1 < len) && (data[pos] == '=') && !isspace(data[pos+1]) && (data[pos+1] != '=')) + return true; + return false; +} + +void AsyncHttpsProxySocket::ParseAuth(const char * data, size_t len, std::string& method, std::map<std::string,std::string>& args) { + size_t pos = 0; + while ((pos < len) && isspace(data[pos])) ++pos; + size_t start = pos; + while ((pos < len) && !isspace(data[pos])) ++pos; + method.assign(data + start, data + pos); + while (pos < len) { + while ((pos < len) && isspace(data[pos])) ++pos; + if (pos >= len) + return; + + start = pos; + while (!end_of_name(pos, len, data)) ++pos; + //while ((pos < len) && !isspace(data[pos]) && (data[pos] != '=')) ++pos; + std::string name(data + start, data + pos), value; + + if ((pos < len) && (data[pos] == '=')) { + ++pos; // Skip '=' + // Check if quoted value + if ((pos < len) && (data[pos] == '"')) { + while (++pos < len) { + if (data[pos] == '"') { + ++pos; + break; + } + if ((data[pos] == '\\') && (pos + 1 < len)) + ++pos; + value.append(1, data[pos]); + } + } else { + while ((pos < len) && !isspace(data[pos]) && (data[pos] != ',')) + value.append(1, data[pos++]); + } + } else { + value = name; + name.clear(); + } + + args.insert(std::make_pair(name, value)); + if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ',' + } +} + +AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const buzz::XmppPassword& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), + state_(PS_ERROR), context_(0) { +} + +AsyncHttpsProxySocket::~AsyncHttpsProxySocket() { + delete context_; +} + +int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect(" << proxy_.ToString() << ")"; + dest_ = addr; + if (dest_.port() != 80) { + BufferInput(true); + } + return BufferedReadAdapter::Connect(proxy_); +} + +SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const { + return dest_; +} + +int AsyncHttpsProxySocket::Close() { + headers_.clear(); + state_ = PS_ERROR; + delete context_; + context_ = 0; + return BufferedReadAdapter::Close(); +} + +void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent"; + // TODO: Decide whether tunneling or not should be explicitly set, + // or indicated by destination port (as below) + if (dest_.port() == 80) { + state_ = PS_TUNNEL; + BufferedReadAdapter::OnConnectEvent(socket); + return; + } + SendRequest(); +} + +void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")"; + if ((state_ == PS_WAIT_CLOSE) && (err == 0)) { + state_ = PS_ERROR; + Connect(dest_); + } else { + BufferedReadAdapter::OnCloseEvent(socket, err); + } +} + +void AsyncHttpsProxySocket::ProcessInput(char * data, size_t& len) { + size_t start = 0; + for (size_t pos = start; (state_ < PS_TUNNEL) && (pos < len); ) { + if (state_ == PS_SKIP_BODY) { + size_t consume = _min(len - pos, content_length_); + pos += consume; + start = pos; + content_length_ -= consume; + if (content_length_ == 0) { + EndResponse(); + } + continue; + } + + if (data[pos++] != '\n') + continue; + + size_t len = pos - start - 1; + if ((len > 0) && (data[start + len - 1] == '\r')) + --len; + + data[start + len] = 0; + ProcessLine(data + start, len); + start = pos; + } + + len -= start; + if (len > 0) { + memmove(data, data + start, len); + } + + if (state_ != PS_TUNNEL) + return; + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncHttpsProxySocket::SendRequest() { + std::stringstream ss; + ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n"; + ss << "User-Agent: " USERAGENT_STRING "\r\n"; + ss << "Host: " << dest_.IPAsString() << "\r\n"; + ss << "Content-Length: 0\r\n"; + ss << "Proxy-Connection: Keep-Alive\r\n"; + ss << headers_; + ss << "\r\n"; + std::string str = ss.str(); + DirectSend(str.c_str(), str.size()); + state_ = PS_LEADER; + expect_close_ = true; + content_length_ = 0; + headers_.clear(); + + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str; +} + +void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data; + + if (len == 0) { + if (state_ == PS_TUNNEL_HEADERS) { + state_ = PS_TUNNEL; + } else if (state_ == PS_ERROR_HEADERS) { + Error(defer_error_); + return; + } else if (state_ == PS_SKIP_HEADERS) { + if (content_length_) { + state_ = PS_SKIP_BODY; + } else { + EndResponse(); + return; + } + } else { + static bool report = false; + if (!unknown_mechanisms_.empty() && !report) { + report = true; + std::string msg( + "Unable to connect to the Google Talk service due to an incompatibility " + "with your proxy.\r\nPlease help us resolve this issue by submitting the " + "following information to us using our technical issue submission form " + "at:\r\n\r\n" + "http://www.google.com/support/talk/bin/request.py\r\n\r\n" + "We apologize for the inconvenience.\r\n\r\n" + "Information to submit to Google: " + ); + //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: "); + msg.append(unknown_mechanisms_); +#ifdef WIN32 + MessageBoxA(0, msg.c_str(), "Oops!", MB_OK); +#endif +#ifdef POSIX + //TODO: Raise a signal or something so the UI can be separated. + LOG(LS_ERROR) << "Oops!\n\n" << msg; +#endif + } + // Unexpected end of headers + Error(0); + return; + } + } else if (state_ == PS_LEADER) { + uint32 code; + if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) { + Error(0); + return; + } + switch (code) { + case 200: + // connection good! + state_ = PS_TUNNEL_HEADERS; + return; +#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407) +#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ +#endif + case 407: // HTTP_STATUS_PROXY_AUTH_REQ + state_ = PS_AUTHENTICATE; + return; + default: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + return; + } + } else if ((state_ == PS_AUTHENTICATE) && (strnicmp(data, "Proxy-Authenticate:", 19) == 0)) { + std::string response, auth_method; + switch (Authenticate(data + 19, len - 19, proxy_, "CONNECT", "/", user_, pass_, context_, response, auth_method)) { + case AR_IGNORE: + LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method; + if (!unknown_mechanisms_.empty()) + unknown_mechanisms_.append(", "); + unknown_mechanisms_.append(auth_method); + break; + case AR_RESPONSE: + headers_ = "Proxy-Authorization: "; + headers_.append(response); + headers_.append("\r\n"); + state_ = PS_SKIP_HEADERS; + unknown_mechanisms_.clear(); + break; + case AR_CREDENTIALS: + defer_error_ = EACCES; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + case AR_ERROR: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + } + } else if (strnicmp(data, "Content-Length:", 15) == 0) { + content_length_ = strtoul(data + 15, 0, 0); + } else if (strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) { + expect_close_ = false; + /* + } else if (strnicmp(data, "Connection: close", 17) == 0) { + expect_close_ = true; + */ + } +} + +void AsyncHttpsProxySocket::EndResponse() { + if (!expect_close_) { + SendRequest(); + return; + } + + // No point in waiting for the server to close... let's close now + // TODO: Refactor out PS_WAIT_CLOSE + state_ = PS_WAIT_CLOSE; + BufferedReadAdapter::Close(); + OnCloseEvent(this, 0); +} + +void AsyncHttpsProxySocket::Error(int error) { + BufferInput(false); + Close(); + SetError(error); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const buzz::XmppPassword& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), + state_(SS_ERROR) { +} + +int AsyncSocksProxySocket::Connect(const SocketAddress& addr) { + dest_ = addr; + BufferInput(true); + return BufferedReadAdapter::Connect(proxy_); +} + +SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const { + return dest_; +} + +void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket * socket) { + SendHello(); +} + +void AsyncSocksProxySocket::ProcessInput(char * data, size_t& len) { + assert(state_ < SS_TUNNEL); + + ByteBuffer response(data, len); + + if (state_ == SS_HELLO) { + uint8 ver, method; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(method)) + return; + + if (ver != 5) { + Error(0); + return; + } + + if (method == 0) { + SendConnect(); + } else if (method == 2) { + SendAuth(); + } else { + Error(0); + return; + } + } else if (state_ == SS_AUTH) { + uint8 ver, status; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(status)) + return; + + if ((ver != 1) || (status != 0)) { + Error(EACCES); + return; + } + + SendConnect(); + } else if (state_ == SS_CONNECT) { + uint8 ver, rep, rsv, atyp; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(rep) || + !response.ReadUInt8(rsv) || + !response.ReadUInt8(atyp)) + return; + + if ((ver != 5) || (rep != 0)) { + Error(0); + return; + } + + uint16 port; + if (atyp == 1) { + uint32 addr; + if (!response.ReadUInt32(addr) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 3) { + uint8 len; + std::string addr; + if (!response.ReadUInt8(len) || + !response.ReadString(addr, len) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 4) { + std::string addr; + if (!response.ReadString(addr, 16) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on <IPV6>:" << port; + } else { + Error(0); + return; + } + + state_ = SS_TUNNEL; + } + + // Consume parsed data + len = response.Length(); + memcpy(data, response.Data(), len); + + if (state_ != SS_TUNNEL) + return; + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncSocksProxySocket::SendHello() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + if (user_.empty()) { + request.WriteUInt8(1); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + } else { + request.WriteUInt8(2); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + request.WriteUInt8(2); // Username/Password + } + DirectSend(request.Data(), request.Length()); + state_ = SS_HELLO; +} + +void AsyncSocksProxySocket::SendAuth() { + ByteBuffer request; + request.WriteUInt8(1); // Negotiation Version + request.WriteUInt8(static_cast<uint8>(user_.size())); + request.WriteString(user_); // Username + request.WriteUInt8(static_cast<uint8>(pass_.GetLength())); + size_t len = pass_.GetLength() + 1; + char * sensitive = new char[len]; + pass_.CopyTo(sensitive, true); + request.WriteString(sensitive); // Password + memset(sensitive, 0, len); + delete [] sensitive; + DirectSend(request.Data(), request.Length()); + state_ = SS_AUTH; +} + +void AsyncSocksProxySocket::SendConnect() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + request.WriteUInt8(1); // CONNECT + request.WriteUInt8(0); // Reserved + if (dest_.IsUnresolved()) { + std::string hostname = dest_.IPAsString(); + request.WriteUInt8(3); // DOMAINNAME + request.WriteUInt8(static_cast<uint8>(hostname.size())); + request.WriteString(hostname); // Destination Hostname + } else { + request.WriteUInt8(1); // IPV4 + request.WriteUInt32(dest_.ip()); // Destination IP + } + request.WriteUInt16(dest_.port()); // Destination Port + DirectSend(request.Data(), request.Length()); + state_ = SS_CONNECT; +} + +void AsyncSocksProxySocket::Error(int error) { + state_ = SS_ERROR; + BufferInput(false); + Close(); + SetError(EACCES); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +LoggingAdapter::LoggingAdapter(AsyncSocket* socket, LoggingSeverity level, + const char * label) + : AsyncSocketAdapter(socket), level_(level) +{ + label_.append("["); + label_.append(label); + label_.append("]"); +} + +int +LoggingAdapter::Send(const void *pv, size_t cb) { + int res = AsyncSocketAdapter::Send(pv, cb); + if (res > 0) + LogMultiline(false, static_cast<const char *>(pv), res); + return res; +} + +int +LoggingAdapter::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + int res = AsyncSocketAdapter::SendTo(pv, cb, addr); + if (res > 0) + LogMultiline(false, static_cast<const char *>(pv), res); + return res; +} + +int +LoggingAdapter::Recv(void *pv, size_t cb) { + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res > 0) + LogMultiline(true, static_cast<const char *>(pv), res); + return res; +} + +int +LoggingAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + if (res > 0) + LogMultiline(true, static_cast<const char *>(pv), res); + return res; +} + +void +LoggingAdapter::OnConnectEvent(AsyncSocket * socket) { + LOG(level_) << label_ << " Connected"; + AsyncSocketAdapter::OnConnectEvent(socket); +} + +void +LoggingAdapter::OnCloseEvent(AsyncSocket * socket, int err) { + LOG(level_) << label_ << " Closed with error: " << err; + AsyncSocketAdapter::OnCloseEvent(socket, err); +} + +void +LoggingAdapter::LogMultiline(bool input, const char * data, size_t len) { + const char * direction = (input ? " << " : " >> "); + std::string str(data, len); + while (!str.empty()) { + std::string::size_type pos = str.find('\n'); + std::string substr = str; + if (pos == std::string::npos) { + substr = str; + str.clear(); + } else if ((pos > 0) && (str[pos-1] == '\r')) { + substr = str.substr(0, pos - 1); + str = str.substr(pos + 1); + } else { + substr = str.substr(0, pos); + str = str.substr(pos + 1); + } + + // Filter out any private data + std::string::size_type pos_private = substr.find("Email"); + if (pos_private == std::string::npos) { + pos_private = substr.find("Passwd"); + } + if (pos_private == std::string::npos) { + LOG(level_) << label_ << direction << substr; + } else { + LOG(level_) << label_ << direction << "## TEXT REMOVED ##"; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h new file mode 100644 index 00000000..1c65aa79 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h @@ -0,0 +1,181 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKETADAPTERS_H__ +#define __SOCKETADAPTERS_H__ + +#include <map> + +#include "talk/base/asyncsocket.h" +#include "talk/base/logging.h" +#include "talk/xmpp/xmpppassword.h" // TODO: move xmpppassword to base + +namespace cricket { + +/////////////////////////////////////////////////////////////////////////////// + +class BufferedReadAdapter : public AsyncSocketAdapter { +public: + BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size); + virtual ~BufferedReadAdapter(); + + virtual int Send(const void *pv, size_t cb); + virtual int Recv(void *pv, size_t cb); + +protected: + int DirectSend(const void *pv, size_t cb) { return AsyncSocketAdapter::Send(pv, cb); } + + void BufferInput(bool on = true); + virtual void ProcessInput(char * data, size_t& len) = 0; + + virtual void OnReadEvent(AsyncSocket * socket); + +private: + char * buffer_; + size_t buffer_size_, data_len_; + bool buffering_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class AsyncSSLSocket : public BufferedReadAdapter { +public: + AsyncSSLSocket(AsyncSocket* socket); + + virtual int Connect(const SocketAddress& addr); + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void ProcessInput(char * data, size_t& len); +}; + +/////////////////////////////////////////////////////////////////////////////// + +class AsyncHttpsProxySocket : public BufferedReadAdapter { +public: + AsyncHttpsProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const buzz::XmppPassword& password); + virtual ~AsyncHttpsProxySocket(); + + virtual int Connect(const SocketAddress& addr); + virtual SocketAddress GetRemoteAddress() const; + virtual int Close(); + + struct AuthContext { + std::string auth_method; + AuthContext(const std::string& auth) : auth_method(auth) { } + virtual ~AuthContext() { } + }; + + // 'context' is used by this function to record information between calls. + // Start by passing a null pointer, then pass the same pointer each additional + // call. When the authentication attempt is finished, delete the context. + enum AuthResult { AR_RESPONSE, AR_IGNORE, AR_CREDENTIALS, AR_ERROR }; + static AuthResult Authenticate(const char * challenge, size_t len, + const SocketAddress& server, + const std::string& method, const std::string& uri, + const std::string& username, const buzz::XmppPassword& password, + AuthContext *& context, std::string& response, std::string& auth_method); + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void OnCloseEvent(AsyncSocket * socket, int err); + virtual void ProcessInput(char * data, size_t& len); + + void SendRequest(); + void ProcessLine(char * data, size_t len); + void EndResponse(); + void Error(int error); + + static void ParseAuth(const char * data, size_t len, std::string& method, std::map<std::string,std::string>& args); + +private: + SocketAddress proxy_, dest_; + std::string user_, headers_; + buzz::XmppPassword pass_; + size_t content_length_; + int defer_error_; + bool expect_close_; + enum ProxyState { PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS, PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR } state_; + AuthContext * context_; + std::string unknown_mechanisms_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class AsyncSocksProxySocket : public BufferedReadAdapter { +public: + AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const buzz::XmppPassword& password); + + virtual int Connect(const SocketAddress& addr); + virtual SocketAddress GetRemoteAddress() const; + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void ProcessInput(char * data, size_t& len); + + void SendHello(); + void SendConnect(); + void SendAuth(); + void Error(int error); + +private: + SocketAddress proxy_, dest_; + std::string user_; + buzz::XmppPassword pass_; + enum SocksState { SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR } state_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class LoggingAdapter : public AsyncSocketAdapter { +public: + LoggingAdapter(AsyncSocket* socket, LoggingSeverity level, + const char * label); + + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Recv(void *pv, size_t cb); + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr); + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void OnCloseEvent(AsyncSocket * socket, int err); + +private: + void LogMultiline(bool input, const char * data, size_t len); + + LoggingSeverity level_; + std::string label_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket + +#endif // __SOCKETADAPTERS_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc new file mode 100644 index 00000000..f0228fbd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc @@ -0,0 +1,267 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/socketaddress.h" +#include "talk/base/byteorder.h" +#include "talk/base/logging.h" +#include <cstring> +#include <sstream> +#include <cassert> + +#ifdef WIN32 +#undef SetPort +int inet_aton(const char * cp, struct in_addr * inp) { + inp->s_addr = inet_addr(cp); + return (inp->s_addr == INADDR_NONE) ? 0 : 1; +} +#endif // WIN32 + +#ifdef POSIX +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +#ifdef _DEBUG +#define DISABLE_DNS 0 +#else // !_DEBUG +#define DISABLE_DNS 0 +#endif // !_DEBUG + +namespace cricket { + +SocketAddress::SocketAddress() { + Zero(); +} + +SocketAddress::SocketAddress(const std::string& hostname, int port, bool use_dns) { + Zero(); + SetIP(hostname, use_dns); + SetPort(port); +} + +SocketAddress::SocketAddress(uint32 ip, int port) { + Zero(); + SetIP(ip); + SetPort(port); +} + +SocketAddress::SocketAddress(const SocketAddress& addr) { + Zero(); + this->operator=(addr); +} + +void SocketAddress::Zero() { + ip_ = 0; + port_ = 0; +} + +SocketAddress& SocketAddress::operator =(const SocketAddress& addr) { + hostname_ = addr.hostname_; + ip_ = addr.ip_; + port_ = addr.port_; + return *this; +} + +void SocketAddress::SetIP(uint32 ip) { + hostname_.clear(); + ip_ = ip; +} + +bool SocketAddress::SetIP(const std::string& hostname, bool use_dns) { + hostname_ = hostname; + ip_ = 0; + return Resolve(true, use_dns); +} + +void SocketAddress::SetResolvedIP(uint32 ip) { + ip_ = ip; +} + +void SocketAddress::SetPort(int port) { + assert((0 <= port) && (port < 65536)); + port_ = port; +} + +uint32 SocketAddress::ip() const { + return ip_; +} + +uint16 SocketAddress::port() const { + return port_; +} + +std::string SocketAddress::IPAsString() const { + if (!hostname_.empty()) + return hostname_; + return IPToString(ip_); +} + +std::string SocketAddress::PortAsString() const { + std::ostringstream ost; + ost << port_; + return ost.str(); +} + +std::string SocketAddress::ToString() const { + std::ostringstream ost; + ost << IPAsString(); + ost << ":"; + ost << port(); + return ost.str(); +} + +bool SocketAddress::IsAny() const { + return (ip_ == 0); +} + +bool SocketAddress::IsLocalIP() const { + return (ip_ >> 24) == 127; +} + +bool SocketAddress::IsPrivateIP() const { + return ((ip_ >> 24) == 127) || + ((ip_ >> 24) == 10) || + ((ip_ >> 20) == ((172 << 4) | 1)) || + ((ip_ >> 16) == ((192 << 8) | 168)); +} + +bool SocketAddress::IsUnresolved() const { + return IsAny() && !hostname_.empty(); +} + +bool SocketAddress::Resolve(bool force, bool use_dns) { + if (hostname_.empty()) { + // nothing to resolve + } else if (!force && !IsAny()) { + // already resolved + } else if (uint32 ip = StringToIP(hostname_, use_dns)) { + ip_ = ip; + } else { + return false; + } + return true; +} + +bool SocketAddress::operator ==(const SocketAddress& addr) const { + return EqualIPs(addr) && EqualPorts(addr); +} + +bool SocketAddress::operator <(const SocketAddress& addr) const { + if (ip_ < addr.ip_) + return true; + else if (addr.ip_ < ip_) + return false; + + // We only check hostnames if both IPs are zero. This matches EqualIPs() + if (addr.ip_ == 0) { + if (hostname_ < addr.hostname_) + return true; + else if (addr.hostname_ < hostname_) + return false; + } + + return port_ < addr.port_; +} + +bool SocketAddress::EqualIPs(const SocketAddress& addr) const { + return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_)); +} + +bool SocketAddress::EqualPorts(const SocketAddress& addr) const { + return (port_ == addr.port_); +} + +size_t SocketAddress::Hash() const { + size_t h = 0; + h ^= ip_; + h ^= port_ | (port_ << 16); + return h; +} + +size_t SocketAddress::Size_() const { + return sizeof(ip_) + sizeof(port_); +} + +void SocketAddress::Write_(char* buf, int len) const { + // TODO: Depending on how this is used, we may want/need to write hostname + assert((size_t)len >= Size_()); + reinterpret_cast<uint32*>(buf)[0] = ip_; + buf += sizeof(ip_); + reinterpret_cast<uint16*>(buf)[0] = port_; +} + +void SocketAddress::Read_(const char* buf, int len) { + assert((size_t)len >= Size_()); + ip_ = reinterpret_cast<const uint32*>(buf)[0]; + buf += sizeof(ip_); + port_ = reinterpret_cast<const uint16*>(buf)[0]; +} + +std::string SocketAddress::IPToString(uint32 ip) { + std::ostringstream ost; + ost << ((ip >> 24) & 0xff); + ost << '.'; + ost << ((ip >> 16) & 0xff); + ost << '.'; + ost << ((ip >> 8) & 0xff); + ost << '.'; + ost << ((ip >> 0) & 0xff); + return ost.str(); +} + +uint32 SocketAddress::StringToIP(const std::string& hostname, bool use_dns) { + uint32 ip = 0; + in_addr addr; + if (inet_aton(hostname.c_str(), &addr) != 0) { + ip = NetworkToHost32(addr.s_addr); + } else if (use_dns) { + // Note: this is here so we can spot spurious DNS resolutions for a while + LOG(INFO) << "=== DNS RESOLUTION (" << hostname << ") ==="; +#if DISABLE_DNS + LOG(WARNING) << "*** DNS DISABLED ***"; +#if WIN32 + WSASetLastError(WSAHOST_NOT_FOUND); +#endif // WIN32 +#endif // DISABLE_DNS + if (hostent * pHost = gethostbyname(hostname.c_str())) { + ip = NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[0])); + } else { +#if WIN32 + LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError(); +#else + LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno); +#endif + } + LOG(INFO) << hostname << " resolved to " << IPToString(ip); + } + return ip; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h new file mode 100644 index 00000000..b8a165d3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h @@ -0,0 +1,154 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKETADDRESS_H__ +#define __SOCKETADDRESS_H__ + +#include "talk/base/basictypes.h" +#include <string> +#undef SetPort + +namespace cricket { + +// Records an IP address and port, which are 32 and 16 bit integers, +// respectively, both in <b>host byte-order</b>. +class SocketAddress { +public: + // Creates a missing / unknown address. + SocketAddress(); + + // Creates the address with the given host and port. If use_dns is true, + // the hostname will be immediately resolved to an IP (which may block for + // several seconds if DNS is not available). Alternately, set use_dns to + // false, and then call Resolve() to complete resolution later, or use + // SetResolvedIP to set the IP explictly. + SocketAddress(const std::string& hostname, int port = 0, bool use_dns = true); + + // Creates the address with the given IP and port. + SocketAddress(uint32 ip, int port); + + // Creates a copy of the given address. + SocketAddress(const SocketAddress& addr); + + // Replaces our address with the given one. + SocketAddress& operator =(const SocketAddress& addr); + + // Changes the IP of this address to the given one, and clears the hostname. + void SetIP(uint32 ip); + + // Changes the hostname of this address to the given one. + // Calls Resolve and returns the result. + bool SetIP(const std::string& hostname, bool use_dns = true); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + void SetResolvedIP(uint32 ip); + + // Changes the port of this address to the given one. + void SetPort(int port); + + // Returns the IP address. + uint32 ip() const; + + // Returns the port part of this address. + uint16 port() const; + + // Returns the IP address in dotted form. + std::string IPAsString() const; + + // Returns the port as a string + std::string PortAsString() const; + + // Returns a display version of the IP/port. + std::string ToString() const; + + // Determines whether this represents a missing / any address. + bool IsAny() const; + + // Synomym for missing / any. + bool IsNil() const { return IsAny(); } + + // Determines whether the IP address refers to the local host, i.e. within + // the range 127.0.0.0/8. + bool IsLocalIP() const; + + // Determines whether the IP address is in one of the private ranges: + // 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12. + bool IsPrivateIP() const; + + // Determines whether the hostname has been resolved to an IP + bool IsUnresolved() const; + + // Attempt to resolve a hostname to IP address. + // Returns false if resolution is required but failed. + // 'force' will cause re-resolution of hostname. + // + bool Resolve(bool force = false, bool use_dns = true); + + // Determines whether this address is identical to the given one. + bool operator ==(const SocketAddress& addr) const; + + // Compares based on IP and then port. + bool operator <(const SocketAddress& addr) const; + + // Determines whether this address has the same IP as the one given. + bool EqualIPs(const SocketAddress& addr) const; + + // Deteremines whether this address has the same port as the one given. + bool EqualPorts(const SocketAddress& addr) const; + + // Hashes this address into a small number. + size_t Hash() const; + + // Returns the size of this address when written. + size_t Size_() const; + + // Writes this address into the given buffer. + void Write_(char* buf, int len) const; + + // Reads this address from the given buffer. + void Read_(const char* buf, int len); + + // Converts the IP address given in compact form into dotted form. + static std::string IPToString(uint32 ip); + + // Converts the IP address given in dotted form into compact form. + // Without 'use_dns', only dotted names (A.B.C.D) are resolved. + static uint32 StringToIP(const std::string& str, bool use_dns = true); + +private: + std::string hostname_; + uint32 ip_; + uint16 port_; + + // Initializes the address to missing / any. + void Zero(); +}; + +} // namespace cricket + +#endif // __SOCKETADDRESS_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc new file mode 100644 index 00000000..2166be09 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc @@ -0,0 +1,58 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/socketaddresspair.h" + +namespace cricket { + +SocketAddressPair::SocketAddressPair( + const SocketAddress& src, const SocketAddress& dest) + : src_(src), dest_(dest) { +} + + +bool SocketAddressPair::operator ==(const SocketAddressPair& p) const { + return (src_ == p.src_) && (dest_ == p.dest_); +} + +bool SocketAddressPair::operator <(const SocketAddressPair& p) const { + if (src_ < p.src_) + return true; + if (p.src_ < src_) + return false; + if (dest_ < p.dest_) + return true; + if (p.dest_ < dest_) + return false; + return false; +} + +size_t SocketAddressPair::Hash() const { + return src_.Hash() ^ dest_.Hash(); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h new file mode 100644 index 00000000..098bafdb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h @@ -0,0 +1,58 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKETADDRESSPAIR_H__ +#define __SOCKETADDRESSPAIR_H__ + +#include "talk/base/socketaddress.h" + +namespace cricket { + +// Records a pair (source,destination) of socket addresses. The two addresses +// identify a connection between two machines. (For UDP, this "connection" is +// not maintained explicitly in a socket.) +class SocketAddressPair { +public: + SocketAddressPair() {} + SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest); + + const SocketAddress& source() const { return src_; } + const SocketAddress& destination() const { return dest_; } + + bool operator ==(const SocketAddressPair& r) const; + bool operator <(const SocketAddressPair& r) const; + + size_t Hash() const; + +private: + SocketAddress src_; + SocketAddress dest_; +}; + +} // namespace cricket + +#endif // __SOCKETADDRESSPAIR_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h new file mode 100644 index 00000000..67386160 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h @@ -0,0 +1,50 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKETFACTORY_H__ +#define __SOCKETFACTORY_H__ + +#include "talk/base/socket.h" +#include "talk/base/asyncsocket.h" + +namespace cricket { + +class SocketFactory { +public: + + // Returns a new socket for blocking communication. The type can be + // SOCK_DGRAM and SOCK_STREAM. + virtual Socket* CreateSocket(int type) = 0; + + // Returns a new socket for nonblocking communication. The type can be + // SOCK_DGRAM and SOCK_STREAM. + virtual AsyncSocket* CreateAsyncSocket(int type) = 0; +}; + +} // namespace cricket + +#endif // __SOCKETFACTORY_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h new file mode 100644 index 00000000..d0e7a22a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h @@ -0,0 +1,53 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOCKETSERVER_H__ +#define __SOCKETSERVER_H__ + +#include "talk/base/socketfactory.h" + +namespace cricket { + +// Provides the ability to wait for activity on a set of sockets. The Thread +// class provides a nice wrapper on a socket server. +// +// The server is also a socket factory. The sockets it creates will be +// notified of asynchronous I/O from this server's Wait method. +class SocketServer : public SocketFactory { +public: + + // Performs I/O or sleeps for the given number of milliseconds. + // If process_io is false, just sleeps until WakeUp. + virtual bool Wait(int cms, bool process_io) = 0; + + // Causes the current wait (if one is in progress) to wake up. + virtual void WakeUp() = 0; +}; + +} // namespace cricket + +#endif // __SOCKETSERVER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h new file mode 100644 index 00000000..9c2506f1 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _STL_DECL_H +#define _STL_DECL_H + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0 +#pragma warning(disable:4786) +#endif + +#include <sys/types.h> + +namespace std { + template <class Key> struct hash; + template <class Key> struct equal_to; + template <class Key> struct less; + template <class T> class allocator; + template <class Key, class Val, + class Compare, + class Alloc> class map; + template <class T, class Alloc> class vector; + template <class T, class Alloc> class list; + template <class T, class Alloc> class slist; + template <class T, class Alloc, size_t BufSiz> class deque; + template <class T, class Sequence> class stack; + template <class T, class Sequence> class queue; + template <class T, class Sequence, class Compare> class priority_queue; + template <class T1, class T2> struct pair; + template <class Key, class Compare, class Alloc> class set; +} + +///////////////////////////////////////////////////////////////////////////// +// Workaround declaration problem with defaults +///////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0 + +#define STD_MAP(T1, T2) \ + std::map<T1 , T2, std::less<T1>, std::allocator<T2> > + +#define STD_VECTOR(T1) \ + std::vector<T1, std::allocator<T1> > + +#define STD_SET(T1) \ + std::set<T1, std::less<T1>, std::allocator<T1> > + +#else + +#define STD_MAP(T1, T2) \ + std::map<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2 > > > + +#define STD_VECTOR(T1) \ + std::vector<T1, std::allocator<T1> > + +#define STD_SET(T1) \ + std::set<T1, std::less<T1>, std::allocator<T1> > + +#endif + + +#endif // _STL_DECL_H diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h new file mode 100644 index 00000000..a23132dd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h @@ -0,0 +1,266 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STRINGUTILS_H__ +#define __STRINGUTILS_H__ + +#include <cctype> +#include <cstdarg> +#include <cstdio> +#ifdef WIN32 +#include <wchar.h> +#endif // WIN32 + +#include <string> + +/////////////////////////////////////////////////////////////////////////////// +// Rename a bunch of common string functions so they are consistent across +// platforms and between char and wchar_t variants. +// Here is the full list of functions that are unified: +// strlen, strcmp, stricmp, strncmp, strnicmp +// strchr, vsnprintf, strtoul, tolowercase +// tolowercase is like tolower, but not compatible with end-of-file value +// Note that the wchar_t versions are not available on Linux +/////////////////////////////////////////////////////////////////////////////// + +inline char tolowercase(char c) { + return static_cast<char>(tolower(c)); +} + +#ifdef WIN32 + +inline size_t strlen(const wchar_t* s) { + return wcslen(s); +} +inline int strcmp(const wchar_t* s1, const wchar_t* s2) { + return wcscmp(s1, s2); +} +inline int stricmp(const wchar_t* s1, const wchar_t* s2) { + return wcsicmp(s1, s2); +} +inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) { + return wcsncmp(s1, s2, n); +} +inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) { + return wcsnicmp(s1, s2, n); +} +inline const wchar_t* strchr(const wchar_t* s, wchar_t c) { + return wcschr(s, c); +} +inline int vsnprintf(char* buf, size_t n, const char* fmt, va_list args) { + return _vsnprintf(buf, n, fmt, args); +} +inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) { + return _vsnwprintf(buf, n, fmt, args); +} +inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) { + return wcstoul(snum, end, base); +} +inline wchar_t tolowercase(wchar_t c) { + return static_cast<wchar_t>(towlower(c)); +} + +#endif // WIN32 + +#ifdef POSIX + +inline int stricmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline int strnicmp(const char* s1, const char* s2, size_t n) { + return strncasecmp(s1, s2, n); +} + +#endif // POSIX + +/////////////////////////////////////////////////////////////////////////////// +// Traits simplifies porting string functions to be CTYPE-agnostic +/////////////////////////////////////////////////////////////////////////////// + +namespace cricket { + +const size_t SIZE_UNKNOWN = static_cast<size_t>(-1); + +template<class CTYPE> +struct Traits { + // STL string type + //typedef XXX string; + // Null-terminated string + //inline static const CTYPE* empty_str(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// String utilities which work with char or wchar_t +/////////////////////////////////////////////////////////////////////////////// + +template<class CTYPE> +inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = NULL) { + return str ? str : (def_str ? def_str : Traits<CTYPE>::empty_str()); +} + +template<class CTYPE> +const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) { + for (size_t i=0; str[i]; ++i) { + for (size_t j=0; chs[j]; ++j) { + if (str[i] == chs[j]) { + return str + i; + } + } + } + return 0; +} + +template<class CTYPE> +const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) { + for (size_t i=0; i<slen && str[i]; ++i) { + if (str[i] == ch) { + return str + i; + } + } + return 0; +} + +template<class CTYPE> +size_t strlenn(const CTYPE* buffer, size_t buflen) { + size_t bufpos = 0; + while (buffer[bufpos] && (bufpos < buflen)) { + ++bufpos; + } + return bufpos; +} + +template<class CTYPE> +size_t strcpyn(CTYPE* buffer, size_t buflen, + const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { + if (buflen <= 0) + return 0; + + if (srclen == SIZE_UNKNOWN) { + srclen = strlenn(source, buflen - 1); + } else if (srclen >= buflen) { + srclen = buflen - 1; + } + memcpy(buffer, source, srclen * sizeof(CTYPE)); + buffer[srclen] = 0; + return srclen; +} + +// Safe versions of snprintf and vsnprintf that always null-terminate + +template<class CTYPE> +size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) { + va_list args; + va_start(args, format); + size_t len = vsprintfn(buffer, buflen, format, args); + va_end(args); + return len; +} + +template<class CTYPE> +size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, + va_list args) { + int len = vsnprintf(buffer, buflen, format, args); + if ((len < 0) || (static_cast<size_t>(len) >= buflen)) { + len = static_cast<int>(buflen - 1); + buffer[len] = 0; + } + return len; +} + +/////////////////////////////////////////////////////////////////////////////// +// Allow safe comparing and copying ascii (not UTF-8) with both wide and +// non-wide character strings. +/////////////////////////////////////////////////////////////////////////////// + +inline int asccmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} +inline int ascicmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline int ascncmp(const char* s1, const char* s2, size_t n) { + return strncmp(s1, s2, n); +} +inline int ascnicmp(const char* s1, const char* s2, size_t n) { + return strnicmp(s1, s2, n); +} +inline size_t asccpyn(char* buffer, size_t buflen, + const char* source, size_t srclen = SIZE_UNKNOWN) { + return strcpyn(buffer, buflen, source, srclen); +} + +#ifdef WIN32 + +typedef wchar_t(*CharacterTransformation)(wchar_t); +inline wchar_t identity(wchar_t c) { return c; } +int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, + CharacterTransformation transformation); + +inline int asccmp(const wchar_t* s1, const char* s2) { + return ascii_string_compare(s1, s2, static_cast<size_t>(-1), identity); +} +inline int ascicmp(const wchar_t* s1, const char* s2) { + return ascii_string_compare(s1, s2, static_cast<size_t>(-1), tolowercase); +} +inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) { + return ascii_string_compare(s1, s2, n, identity); +} +inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) { + return ascii_string_compare(s1, s2, n, tolowercase); +} +size_t asccpyn(wchar_t* buffer, size_t buflen, + const char* source, size_t srclen = SIZE_UNKNOWN); + +#endif // WIN32 + +/////////////////////////////////////////////////////////////////////////////// +// Traits<char> specializations +/////////////////////////////////////////////////////////////////////////////// + +template<> +struct Traits<char> { + typedef std::string string; + inline static const char* empty_str() { return ""; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// Traits<wchar_t> specializations (Windows only, currently) +/////////////////////////////////////////////////////////////////////////////// + +#ifdef WIN32 + +template<> +struct Traits<wchar_t> { + typedef std::wstring string; + inline static const wchar_t* Traits<wchar_t>::empty_str() { return L""; } +}; + +#endif // WIN32 + +} // namespace cricket + +#endif // __STRINGUTILS_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc new file mode 100644 index 00000000..a5a94941 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc @@ -0,0 +1,238 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "task.h" +#include "taskrunner.h" + +#include <algorithm> + +namespace buzz { + +Task::Task(Task * parent) : + state_(STATE_INIT), + parent_(parent), + blocked_(false), + done_(false), + aborted_(false), + busy_(false), + error_(false), + child_error_(false), + start_time_(0) { + runner_ = ((parent == NULL) ? (TaskRunner *)this : parent->GetRunner()); + if (parent_ != NULL) { + parent_->AddChild(this); + } +} + +unsigned long long +Task::CurrentTime() { + return runner_->CurrentTime(); +} + +unsigned long long +Task::ElapsedTime() { + return CurrentTime() - start_time_; +} + +void +Task::Start() { + if (state_ != STATE_INIT) + return; + GetRunner()->StartTask(this); + start_time_ = CurrentTime(); +} + +void +Task::Step() { + if (done_) { +#ifdef DEBUG + // we do not know how !blocked_ happens when done_ - should be impossible. + // But it causes problems, so in retail build, we force blocked_, and + // under debug we assert. + assert(blocked_); +#else + blocked_ = true; +#endif + return; + } + + // Async Error() was called + if (error_) { + done_ = true; + state_ = STATE_ERROR; + blocked_ = true; +// obsolete - an errored task is not considered done now +// SignalDone(); + Stop(); + return; + } + + busy_ = true; + int new_state = Process(state_); + busy_ = false; + + if (aborted_) { + Abort(true); // no need to wake because we're awake + return; + } + + if (new_state == STATE_BLOCKED) { + blocked_ = true; + } + else { + state_ = new_state; + blocked_ = false; + } + + if (new_state == STATE_DONE) { + done_ = true; + } + else if (new_state == STATE_ERROR) { + done_ = true; + error_ = true; + } + + if (done_) { +// obsolete - call this yourself +// SignalDone(); + Stop(); + blocked_ = true; + } +} + +void +Task::Abort(bool nowake) { + if (aborted_ || done_) + return; + aborted_ = true; + if (!busy_) { + done_ = true; + blocked_ = true; + error_ = true; + Stop(); + if (!nowake) + Wake(); // to self-delete + } +} + +void +Task::Wake() { + if (done_) + return; + if (blocked_) { + blocked_ = false; + GetRunner()->WakeTasks(); + } +} + +void +Task::Error() { + if (error_ || done_) + return; + error_ = true; + Wake(); +} + +std::string +Task::GetStateName(int state) const { + static const std::string STR_BLOCKED("BLOCKED"); + static const std::string STR_INIT("INIT"); + static const std::string STR_START("START"); + static const std::string STR_DONE("DONE"); + static const std::string STR_ERROR("ERROR"); + static const std::string STR_RESPONSE("RESPONSE"); + static const std::string STR_HUH("??"); + switch (state) { + case STATE_BLOCKED: return STR_BLOCKED; + case STATE_INIT: return STR_INIT; + case STATE_START: return STR_START; + case STATE_DONE: return STR_DONE; + case STATE_ERROR: return STR_ERROR; + case STATE_RESPONSE: return STR_RESPONSE; + } + return STR_HUH; +} + +int Task::Process(int state) { + switch (state) { + case STATE_INIT: + return STATE_START; + case STATE_START: + return ProcessStart(); + case STATE_RESPONSE: + return ProcessResponse(); + case STATE_DONE: + case STATE_ERROR: + return STATE_BLOCKED; + } + return STATE_ERROR; +} + +void +Task::AddChild(Task * child) { + children_.insert(child); +} + +bool +Task::AllChildrenDone() { + for (ChildSet::iterator it = children_.begin(); it != children_.end(); ++it) { + if (!(*it)->IsDone()) + return false; + } + return true; +} + +bool +Task::AnyChildError() { + return child_error_; +} + +void +Task::AbortAllChildren() { + if (children_.size() > 0) { + ChildSet copy = children_; + for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) { + (*it)->Abort(true); // Note we do not wake + } + } +} + +void +Task::Stop() { + AbortAllChildren(); // No need to wake because we're either awake or in abort + parent_->OnChildStopped(this); +} + +void +Task::OnChildStopped(Task * child) { + if (child->HasError()) + child_error_ = true; + children_.erase(child); +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.h new file mode 100644 index 00000000..5a486198 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.h @@ -0,0 +1,186 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TASK_H_ +#define _TASK_H_ + +#include <vector> +#include <string> + +#include "talk/base/sigslot.h" + +///////////////////////////////////////////////////////////////////// +// +// TASK +// +///////////////////////////////////////////////////////////////////// +// +// Task is a state machine infrastructure. States are pushed forward by +// pushing forwards a TaskRunner that holds on to all Tasks. The purpose +// of Task is threefold: +// +// (1) It manages ongoing work on the UI thread. Multitasking without +// threads, keeping it easy, keeping it real. :-) It does this by +// organizing a set of states for each task. When you return from your +// Process*() function, you return an integer for the next state. You do +// not go onto the next state yourself. Every time you enter a state, +// you check to see if you can do anything yet. If not, you return +// STATE_BLOCKED. If you _could_ do anything, do not return +// STATE_BLOCKED - even if you end up in the same state, return +// STATE_mysamestate. When you are done, return STATE_DONE and then the +// task will self-delete sometimea afterwards. +// +// (2) It helps you avoid all those reentrancy problems when you chain +// too many triggers on one thread. Basically if you want to tell a task +// to process something for you, you feed your task some information and +// then you Wake() it. Don't tell it to process it right away. If it +// might be working on something as you send it infomration, you may want +// to have a queue in the task. +// +// (3) Finally it helps manage parent tasks and children. If a parent +// task gets aborted, all the children tasks are too. The nice thing +// about this, for example, is if you have one parent task that +// represents, say, and Xmpp connection, then you can spawn a whole bunch +// of infinite lifetime child tasks and now worry about cleaning them up. +// When the parent task goes to STATE_DONE, the task engine will make +// sure all those children are aborted and get deleted. +// +// Notice that Task has a few built-in states, e.g., +// +// STATE_INIT - the task isn't running yet +// STATE_START - the task is in its first state +// STATE_RESPONSE - the task is in its second state +// STATE_DONE - the task is done +// +// STATE_ERROR - indicates an error - we should audit the error code in +// light of any usage of it to see if it should be improved. When I +// first put down the task stuff I didn't have a good sense of what was +// needed for Abort and Error, and now the subclasses of Task will ground +// the design in a stronger way. +// +// STATE_NEXT - the first undefined state number. (like WM_USER) - you +// can start defining more task states there. +// +// When you define more task states, just override Process(int state) and +// add your own switch statement. If you want to delegate to +// Task::Process, you can effectively delegate to its switch statement. +// No fancy method pointers or such - this is all just pretty low tech, +// easy to debug, and fast. +// + +namespace buzz { + +class TaskRunner; + +// A task executes a sequence of steps + +class Task; +class RootTask; + +class Task { +public: + Task(Task * parent); + virtual ~Task() {} + + void Start(); + void Step(); + int GetState() const { return state_; } + bool HasError() const { return (GetState() == STATE_ERROR); } + bool Blocked() const { return blocked_; } + bool IsDone() const { return done_; } + unsigned long long ElapsedTime(); + virtual void Poll() {} + + Task * GetParent() { return parent_; } + TaskRunner * GetRunner() { return runner_; } + virtual Task * GetParent(int code) { return parent_->GetParent(code); } + + // Called from outside to stop task without any more callbacks + void Abort(bool nowake = false); + + // For managing children + bool AllChildrenDone(); + bool AnyChildError(); + + +protected: + + enum { + STATE_BLOCKED = -1, + STATE_INIT = 0, + STATE_START = 1, + STATE_DONE = 2, + STATE_ERROR = 3, + STATE_RESPONSE = 4, + STATE_NEXT = 5, // Subclasses which need more states start here and higher + }; + + // Called inside the task to signal that the task may be unblocked + void Wake(); + + // Called inside to advise that the task should wake and signal an error + void Error(); + + unsigned long long CurrentTime(); + + virtual std::string GetStateName(int state) const; + virtual int Process(int state); + virtual void Stop(); + virtual int ProcessStart() = 0; + virtual int ProcessResponse() { return STATE_DONE; } + + // for managing children (if any) + void AddChild(Task * child); + void AbortAllChildren(); + +private: + void Done(); + void OnChildStopped(Task * child); + + int state_; + Task * parent_; + TaskRunner * runner_; + bool blocked_; + bool done_; + bool aborted_; + bool busy_; + bool error_; + bool child_error_; + unsigned long long start_time_; + + // for managing children + typedef std::set<Task *> ChildSet; + ChildSet children_; + +}; + + + + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc new file mode 100644 index 00000000..b5ecc55e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc @@ -0,0 +1,92 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "taskrunner.h" +#include "task.h" +#include <algorithm> + + +namespace buzz { + +TaskRunner::~TaskRunner() { + // this kills and deletes children silently! + AbortAllChildren(); + RunTasks(); +} + +void +TaskRunner::StartTask(Task * task) { + tasks_.push_back(task); + WakeTasks(); +} + +void +TaskRunner::RunTasks() { + // Running continues until all tasks are Blocked (ok for a small # of tasks) + if (tasks_running_) { + return; // don't reenter + } + + tasks_running_ = true; + + int did_run = true; + while (did_run) { + did_run = false; + // use indexing instead of iterators because tasks_ may grow + for (size_t i = 0; i < tasks_.size(); ++i) { + while (!tasks_[i]->Blocked()) { + tasks_[i]->Step(); + did_run = true; + } + } + } + // Tasks are deleted when running has paused + for (size_t i = 0; i < tasks_.size(); ++i) { + if (tasks_[i]->IsDone()) { + Task* task = tasks_[i]; + delete task; + tasks_[i] = NULL; + } + } + // Finally, remove nulls + tasks_.erase(std::remove(tasks_.begin(), tasks_.end(), (Task *)NULL), tasks_.end()); + + tasks_running_ = false; +} + +void +TaskRunner::PollTasks() { + // every task gets hit once with a poll - they wake if needed + for (size_t i = 0; i < tasks_.size(); ++i) { + if (!tasks_[i]->IsDone()) { + tasks_[i]->Poll(); + } + } +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h new file mode 100644 index 00000000..eab16eb9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h @@ -0,0 +1,64 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TASKRUNNER_H_ +#define _TASKRUNNER_H_ + +#include <vector> + +#include "talk/base/sigslot.h" +#include "talk/base/task.h" + + +namespace buzz { + + +class Task; + +class TaskRunner : public Task, public sigslot::has_slots<> { +public: + TaskRunner() : Task(NULL), tasks_running_(false) {} + virtual ~TaskRunner(); + + virtual void WakeTasks() = 0; + virtual unsigned long long CurrentTime() = 0 ; + + void StartTask(Task * task); + void RunTasks(); + void PollTasks(); + + // dummy state machine - never run. + virtual int ProcessStart() { return STATE_DONE; } + +private: + std::vector<Task *> tasks_; + bool tasks_running_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc new file mode 100644 index 00000000..8f18a992 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc @@ -0,0 +1,273 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#ifdef POSIX +extern "C" { +#include <sys/time.h> +} +#endif + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#include "talk/base/jtime.h" + +namespace cricket { + +ThreadManager g_thmgr; + +#ifdef POSIX +pthread_key_t ThreadManager::key_; + +ThreadManager::ThreadManager() { + pthread_key_create(&key_, NULL); + main_thread_ = new Thread(); + SetCurrent(main_thread_); +} + +ThreadManager::~ThreadManager() { + pthread_key_delete(key_); + delete main_thread_; +} + +Thread *ThreadManager::CurrentThread() { + return (Thread *)pthread_getspecific(key_); +} + +void ThreadManager::SetCurrent(Thread *thread) { + pthread_setspecific(key_, thread); +} +#endif + +#ifdef WIN32 +DWORD ThreadManager::key_; + +ThreadManager::ThreadManager() { + key_ = TlsAlloc(); + main_thread_ = new Thread(); + SetCurrent(main_thread_); +} + +ThreadManager::~ThreadManager() { + TlsFree(key_); + delete main_thread_; +} + +Thread *ThreadManager::CurrentThread() { + return (Thread *)TlsGetValue(key_); +} + +void ThreadManager::SetCurrent(Thread *thread) { + TlsSetValue(key_, thread); +} +#endif + +void ThreadManager::Add(Thread *thread) { + CritScope cs(&crit_); + threads_.push_back(thread); +} + +void ThreadManager::Remove(Thread *thread) { + CritScope cs(&crit_); + threads_.erase(std::remove(threads_.begin(), threads_.end(), thread), threads_.end()); +} + +Thread::Thread(SocketServer* ss) : MessageQueue(ss) { + g_thmgr.Add(this); + started_ = false; + has_sends_ = false; +} + +Thread::~Thread() { + Stop(); + Clear(NULL); + g_thmgr.Remove(this); +} + +#ifdef POSIX +void Thread::Start() { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_create(&thread_, &attr, PreLoop, this); + started_ = true; +} + +void Thread::Join() { + if (started_) { + void *pv; + pthread_join(thread_, &pv); + } +} +#endif + +#ifdef WIN32 +void Thread::Start() { + thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreLoop, this, 0, NULL); + started_ = true; +} + +void Thread::Join() { + if (started_) { + WaitForSingleObject(thread_, INFINITE); + CloseHandle(thread_); + started_ = false; + } +} +#endif + +void *Thread::PreLoop(void *pv) { + Thread *thread = (Thread *)pv; + ThreadManager::SetCurrent(thread); + thread->Loop(); + return NULL; +} + +void Thread::Loop(int cmsLoop) { + uint32 msEnd; + if (cmsLoop != -1) + msEnd = GetMillisecondCount() + cmsLoop; + int cmsNext = cmsLoop; + + while (true) { + Message msg; + if (!Get(&msg, cmsNext)) + return; + Dispatch(&msg); + + if (cmsLoop != -1) { + uint32 msCur = GetMillisecondCount(); + if (msCur >= msEnd) + return; + cmsNext = msEnd - msCur; + } + } +} + +void Thread::Stop() { + MessageQueue::Stop(); + Join(); +} + +void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { + // Sent messages are sent to the MessageHandler directly, in the context + // of "thread", like Win32 SendMessage. If in the right context, + // call the handler directly. + + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + if (IsCurrent()) { + phandler->OnMessage(&msg); + return; + } + + AutoThread thread; + Thread *current_thread = Thread::Current(); + ASSERT(current_thread != NULL); // AutoThread ensures this + + crit_.Enter(); + bool ready = false; + _SendMessage smsg; + smsg.thread = current_thread; + smsg.msg = msg; + smsg.ready = &ready; + sendlist_.push_back(smsg); + has_sends_ = true; + crit_.Leave(); + + // Wait for a reply + + ss_->WakeUp(); + while (!ready) { + current_thread->ReceiveSends(); + current_thread->socketserver()->Wait(-1, false); + } +} + +void Thread::ReceiveSends() { + // Before entering critical section, check boolean. + + if (!has_sends_) + return; + + // Receive a sent message. Cleanup scenarios: + // - thread sending exits: We don't allow this, since thread can exit + // only via Join, so Send must complete. + // - thread receiving exits: Wakeup/set ready in Thread::Clear() + // - object target cleared: Wakeup/set ready in Thread::Clear() + crit_.Enter(); + while (!sendlist_.empty()) { + _SendMessage smsg = sendlist_.front(); + sendlist_.pop_front(); + crit_.Leave(); + smsg.msg.phandler->OnMessage(&smsg.msg); + crit_.Enter(); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + } + has_sends_ = false; + crit_.Leave(); +} + +void Thread::Clear(MessageHandler *phandler, uint32 id) { + CritScope cs(&crit_); + + // Remove messages on sendlist_ with phandler + // Object target cleared: remove from send list, wakeup/set ready + // if sender not NULL. + + std::list<_SendMessage>::iterator iter = sendlist_.begin(); + while (iter != sendlist_.end()) { + _SendMessage smsg = *iter; + if (phandler == NULL || smsg.msg.phandler == phandler) { + if (id == (uint32)-1 || smsg.msg.message_id == id) { + iter = sendlist_.erase(iter); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + continue; + } + } + ++iter; + } + + MessageQueue::Clear(phandler, id); +} + +AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { + if (!ThreadManager::CurrentThread()) { + ThreadManager::SetCurrent(this); + } +} + +AutoThread::~AutoThread() { + if (ThreadManager::CurrentThread() == this) { + ThreadManager::SetCurrent(NULL); + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h new file mode 100644 index 00000000..56c23384 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h @@ -0,0 +1,141 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __THREAD_H__ +#define __THREAD_H__ + +#include "talk/base/messagequeue.h" + +#include <algorithm> +#include <list> +#include <vector> + +#ifdef POSIX +#include <pthread.h> +#endif + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +namespace cricket { + +class Thread; + +class ThreadManager { +public: + ThreadManager(); + ~ThreadManager(); + + static Thread *CurrentThread(); + static void SetCurrent(Thread *thread); + void Add(Thread *thread); + void Remove(Thread *thread); + +private: + Thread *main_thread_; + std::vector<Thread *> threads_; + CriticalSection crit_; + +#ifdef POSIX + static pthread_key_t key_; +#endif + +#ifdef WIN32 + static DWORD key_; +#endif +}; + +class Thread; + +struct _SendMessage { + _SendMessage() {} + Thread *thread; + Message msg; + bool *ready; +}; + +class Thread : public MessageQueue { +public: + Thread(SocketServer* ss = 0); + virtual ~Thread(); + + static inline Thread* Current() { + return ThreadManager::CurrentThread(); + } + inline bool IsCurrent() const { + return (ThreadManager::CurrentThread() == this); + } + + virtual void Start(); + virtual void Stop(); + virtual void Loop(int cms = -1); + virtual void Send(MessageHandler *phandler, uint32 id = 0, + MessageData *pdata = NULL); + + // From MessageQueue + virtual void Clear(MessageHandler *phandler, uint32 id = (uint32)-1); + virtual void ReceiveSends(); + +#ifdef WIN32 + HANDLE GetHandle() { + return thread_; + } +#endif + +private: + static void *PreLoop(void *pv); + void Join(); + + std::list<_SendMessage> sendlist_; + bool started_; + bool has_sends_; + +#ifdef POSIX + pthread_t thread_; +#endif + +#ifdef WIN32 + HANDLE thread_; +#endif + + friend class ThreadManager; +}; + +// AutoThread automatically installs itself at construction +// uninstalls at destruction, if a Thread object is +// _not already_ associated with the current OS thread. + +class AutoThread : public Thread { +public: + AutoThread(SocketServer* ss = 0); + virtual ~AutoThread(); +}; + +} // namespace cricket + +#endif // __THREAD_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h new file mode 100644 index 00000000..99ba2fdc --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h @@ -0,0 +1,101 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _WINPING_H_ +#define _WINPING_H_ + +#ifdef WIN32 + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "talk/base/basictypes.h" + +#include <winsock2.h> +#define _WINSOCKAPI_ +#include <windows.h> +#undef SetPort + +// This class wraps a Win32 API for doing ICMP pinging. This API, unlike the +// the normal socket APIs (as implemented on Win9x), will return an error if +// an ICMP packet with the dont-fragment bit set is too large. This means this +// class can be used to detect the MTU to a given address. + +typedef struct ip_option_information { + UCHAR Ttl; // Time To Live + UCHAR Tos; // Type Of Service + UCHAR Flags; // IP header flags + UCHAR OptionsSize; // Size in bytes of options data + PUCHAR OptionsData; // Pointer to options data +} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION; + +typedef HANDLE (WINAPI *PIcmpCreateFile)(); + +typedef BOOL (WINAPI *PIcmpCloseHandle)(HANDLE icmp_handle); + +typedef DWORD (WINAPI *PIcmpSendEcho)( + HANDLE IcmpHandle, + ULONG DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout); + +class WinPing { +public: + WinPing(); + ~WinPing(); + + // Determines whether the class was initialized correctly. + bool IsValid() { return valid_; } + + // Attempts to send a ping with the given parameters. + enum PingResult { PING_FAIL, PING_TOO_LARGE, PING_TIMEOUT, PING_SUCCESS }; + PingResult Ping( + uint32 ip, uint32 data_size, uint32 timeout_millis, uint8 ttl, + bool allow_fragments); + +private: + HMODULE dll_; + HANDLE hping_; + PIcmpCreateFile create_; + PIcmpCloseHandle close_; + PIcmpSendEcho send_; + char* data_; + uint32 dlen_; + char* reply_; + uint32 rlen_; + bool valid_; +}; + +#endif // WIN32 + +#endif // _WINPING_H_ + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am new file mode 100644 index 00000000..43b0edb5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=login call diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am new file mode 100644 index 00000000..81cf9345 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am @@ -0,0 +1,16 @@ +bin_PROGRAMS = call +call_CXXFLAGS = $(AM_CXXFLAGS) +call_SOURCES = call_main.cc callclient.cc console.cc presencepushtask.cc presenceouttask.cc +noinst_HEADERS = callclient.h console.h presenceouttask.h presencepushtask.h status.h +call_LDADD = \ + $(srcdir)/../../../talk/examples/login/libcricketexampleslogin.la \ + $(srcdir)/../../../talk/session/phone/libcricketsessionphone.la \ + $(srcdir)/../../../talk/p2p/client/libcricketp2pclient.la \ + $(srcdir)/../../../talk/p2p/base/libcricketp2pbase.la \ + $(srcdir)/../../../talk/xmpp/libcricketxmpp.la \ + $(srcdir)/../../../talk/xmllite/libcricketxmllite.la \ + $(srcdir)/../../../talk/base/libcricketbase.la \ + $(srcdir)/../../../talk/third_party/mediastreamer/libmediastreamer.la \ + $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) -lasound +AM_CPPFLAGS = -DPOSIX +DEFAULT_INCLUDES = -I$(srcdir)/../../..
\ No newline at end of file diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro new file mode 100644 index 00000000..ccf0638b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro @@ -0,0 +1,19 @@ +TEMPLATE = app +INCLUDEPATH = ../../.. +DEFINES += POSIX + +include(../../../../../conf.pri) + +# Input +SOURCES += \ + call_main.cc \ + callclient.cc \ + console.cc \ + presenceouttask.cc \ + presencepushtask.cc \ + ../login/xmppauth.cc \ + ../login/xmpppump.cc \ + ../login/xmppsocket.cc \ + ../login/xmppthread.cc + +LIBS += ../../../liblibjingle.a diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc new file mode 100644 index 00000000..1a965326 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc @@ -0,0 +1,62 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include "talk/examples/login/xmppauth.h" +#include "talk/examples/call/callclient.h" +#include "talk/examples/call/console.h" + +void GetString(const char* desc, char* out) { + printf("%s: ", desc); + fflush(stdout); + scanf("%s", out); +} + +int main(int argc, char **argv) { + // TODO: Make this into a console task + char username[256], auth_cookie[256]; + GetString("Username", username); + GetString("Auth Cookie", auth_cookie); + + printf("Logging in as %s@gmail.com\n", username); + + // We will run the console and the XMPP client on the main thread. The + // CallClient maintains a separate worker thread for voice. + + cricket::PhysicalSocketServer ss; + cricket::Thread main_thread(&ss); + cricket::ThreadManager::SetCurrent(&main_thread); + + InitConsole(&ss); + XmppPump pump; + CallClient client(pump.client()); + + buzz::XmppClientSettings xcs; + xcs.set_user(username); + xcs.set_host("gmail.com"); + xcs.set_use_tls(false); + xcs.set_auth_cookie(auth_cookie); + xcs.set_server(cricket::SocketAddress("talk.google.com", 5222)); + pump.DoLogin(xcs, new XmppSocket(false), new XmppAuth()); + + main_thread.Loop(); + + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc new file mode 100644 index 00000000..c8c28310 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc @@ -0,0 +1,390 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string> +#include <vector> + +#include "talk/xmpp/constants.h" +#include "talk/base/thread.h" +#include "talk/base/network.h" +#include "talk/base/socketaddress.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/helpers.h" +#include "talk/p2p/client/basicportallocator.h" +#include "talk/session/receiver.h" +#include "talk/session/sessionsendtask.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/examples/call/callclient.h" +#include "talk/examples/call/console.h" +#include "talk/examples/call/presencepushtask.h" +#include "talk/examples/call/presenceouttask.h" + +namespace { + +const char* CALL_COMMANDS = +"Available commands:\n" +"\n" +" hangup Ends the call.\n" +" mute Stops sending voice.\n" +" unmute Re-starts sending voice.\n" +""; + +class CallTask: public ConsoleTask, public sigslot::has_slots<> { +public: + CallTask(CallClient* call_client, const buzz::Jid& jid, cricket::Call* call) + : call_client_(call_client), jid_(jid), call_(call) { + } + + virtual ~CallTask() {} + + virtual void Start() { + call_client_->phone_client()->SignalCallDestroy.connect( + this, &CallTask::OnCallDestroy); + if (!call_) { + call_ = call_client_->phone_client()->CreateCall(); + call_->SignalSessionState.connect(this, &CallTask::OnSessionState); + session_ = call_->InitiateSession(jid_); + } + call_client_->phone_client()->SetFocus(call_); + } + + virtual std::string GetPrompt() { return jid_.node(); } + + virtual void ProcessLine(const std::string& line) { + std::vector<std::string> words; + ParseLine(line, &words); + + if ((words.size() == 1) && (words[0] == "hangup")) { + call_->Terminate(); + SignalDone(this); + } else if ((words.size() == 1) && (words[0] == "mute")) { + call_->Mute(true); + } else if ((words.size() == 1) && (words[0] == "unmute")) { + call_->Mute(false); + } else { + console()->Print(CALL_COMMANDS); + } + } + +private: + CallClient* call_client_; + buzz::Jid jid_; + cricket::Call* call_; + cricket::Session* session_; + + void OnCallDestroy(cricket::Call* call) { + if (call == call_) { + console()->Print("call destroyed"); + SignalDone(this); + } + } + + void OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state) { + if (state == cricket::Session::STATE_SENTINITIATE) { + console()->Print("calling..."); + } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) { + console()->Print("call answered"); + } else if (state == cricket::Session::STATE_RECEIVEDREJECT) { + console()->Print("call not answered"); + SignalDone(this); + } else if (state == cricket::Session::STATE_INPROGRESS) { + console()->Print("call in progress"); + } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) { + console()->Print("other side hung up"); + SignalDone(this); + } + } +}; + +const char* RECEIVE_COMMANDS = +"Available commands:\n" +"\n" +" accept Accepts the incoming call and switches to it.\n" +" reject Rejects the incoming call and stays with the current call.\n" +""; + +class ReceiveTask: public ConsoleTask { +public: + ReceiveTask(CallClient* call_client, + const buzz::Jid& jid, + cricket::Call* call) + : call_client_(call_client), jid_(jid), call_(call) { + } + + virtual std::string GetPrompt() { return jid_.node(); } + + virtual void ProcessLine(const std::string& line) { + std::vector<std::string> words; + ParseLine(line, &words); + + if ((words.size() == 1) && (words[0] == "accept")) { + assert(call_->sessions().size() == 1); + call_->AcceptSession(call_->sessions()[0]); + Console()->Push(new CallTask(call_client_, jid_, call_)); + SignalDone(this); + } else if ((words.size() == 1) && (words[0] == "reject")) { + call_->RejectSession(call_->sessions()[0]); + SignalDone(this); + } else { + console()->Print(RECEIVE_COMMANDS); + } + } + +private: + CallClient* call_client_; + buzz::Jid jid_; + cricket::Call* call_; +}; + +const char* CONSOLE_COMMANDS = +"Available commands:\n" +"\n" +" roster Prints the online friends from your roster.\n" +" call <name> Initiates a call to the friend with the given name.\n" +" quit Quits the application.\n" +""; + +class CallConsoleTask: public ConsoleTask { +public: + CallConsoleTask(CallClient* call_client) : call_client_(call_client) {} + virtual ~CallConsoleTask() {} + + virtual std::string GetPrompt() { return "console"; } + + virtual void ProcessLine(const std::string& line) { + std::vector<std::string> words; + ParseLine(line, &words); + + if ((words.size() == 1) && (words[0] == "quit")) { + SignalDone(this); + } else if ((words.size() == 1) && (words[0] == "roster")) { + call_client_->PrintRoster(); + } else if ((words.size() == 2) && (words[0] == "call")) { + call_client_->MakeCallTo(words[1]); + } else { + console()->Print(CONSOLE_COMMANDS); + } + } + +private: + CallClient* call_client_; +}; + +const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) { + switch (show) { + case buzz::Status::SHOW_XA: return desc.c_str(); + case buzz::Status::SHOW_ONLINE: return "online"; + case buzz::Status::SHOW_AWAY: return "away"; + case buzz::Status::SHOW_DND: return "do not disturb"; + case buzz::Status::SHOW_CHAT: return "ready to chat"; + delault: return "offline"; + } +} + +} // namespace + +CallClient::CallClient(buzz::XmppClient* xmpp_client) + : xmpp_client_(xmpp_client), roster_(new RosterMap) { + xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); + Console()->Push(new CallConsoleTask(this)); +} + +CallClient::~CallClient() { + delete roster_; +} + +const std::string CallClient::strerror(buzz::XmppEngine::Error err) { + switch (err) { + case buzz::XmppEngine::ERROR_NONE: + return ""; + case buzz::XmppEngine::ERROR_XML: + return "Malformed XML or encoding error"; + case buzz::XmppEngine::ERROR_STREAM: + return "XMPP stream error"; + case buzz::XmppEngine::ERROR_VERSION: + return "XMPP version error"; + case buzz::XmppEngine::ERROR_UNAUTHORIZED: + return "User is not authorized (Confirm your GX cookie at mail.google.com)"; + case buzz::XmppEngine::ERROR_TLS: + return "TLS could not be negotiated"; + case buzz::XmppEngine::ERROR_AUTH: + return "Authentication could not be negotiated"; + case buzz::XmppEngine::ERROR_BIND: + return "Resource or session binding could not be negotiated"; + case buzz::XmppEngine::ERROR_CONNECTION_CLOSED: + return "Connection closed by output handler."; + case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED: + return "Closed by </stream:stream>"; + case buzz::XmppEngine::ERROR_SOCKET: + return "Socket error"; + } +} + +void CallClient::OnStateChange(buzz::XmppEngine::State state) { + switch (state) { + case buzz::XmppEngine::STATE_START: + Console()->Print("connecting..."); + break; + + case buzz::XmppEngine::STATE_OPENING: + Console()->Print("logging in..."); + break; + + case buzz::XmppEngine::STATE_OPEN: + Console()->Print("logged in..."); + InitPhone(); + InitPresence(); + break; + + case buzz::XmppEngine::STATE_CLOSED: + buzz::XmppEngine::Error error = xmpp_client_->GetError(); + Console()->Print("logged out..." + strerror(error)); + exit(0); + } +} + +void CallClient::InitPhone() { + std::string client_unique = xmpp_client_->jid().Str(); + cricket::InitRandom(client_unique.c_str(), client_unique.size()); + + worker_thread_ = new cricket::Thread(); + + network_manager_ = new cricket::NetworkManager(); + + cricket::SocketAddress *stun_addr = new cricket::SocketAddress("64.233.167.126", 19302); + port_allocator_ = new cricket::BasicPortAllocator(network_manager_, stun_addr, NULL); + + session_manager_ = new cricket::SessionManager( + port_allocator_, worker_thread_); + session_manager_->SignalRequestSignaling.connect( + this, &CallClient::OnRequestSignaling); + session_manager_->OnSignalingReady(); + + phone_client_ = new cricket::PhoneSessionClient( + xmpp_client_->jid(),session_manager_); + phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); + phone_client_->SignalSendStanza.connect(this, &CallClient::OnSendStanza); + + receiver_ = new cricket::Receiver(xmpp_client_, phone_client_); + receiver_->Start(); + + worker_thread_->Start(); +} + +void CallClient::OnRequestSignaling() { + session_manager_->OnSignalingReady(); +} + +void CallClient::OnCallCreate(cricket::Call* call) { + call->SignalSessionState.connect(this, &CallClient::OnSessionState); +} + +void CallClient::OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state) { + if (state == cricket::Session::STATE_RECEIVEDINITIATE) { + buzz::Jid jid(session->remote_address()); + Console()->Printf("Incoming call from '%s'", jid.Str().c_str()); + Console()->Push(new ReceiveTask(this, jid, call)); + } +} + +void CallClient::OnSendStanza(cricket::SessionClient *client, const buzz::XmlElement* stanza) { + cricket::SessionSendTask* sender = + new cricket::SessionSendTask(xmpp_client_, phone_client_); + sender->Send(stanza); + sender->Start(); +} + +void CallClient::InitPresence() { + presence_push_ = new buzz::PresencePushTask(xmpp_client_); + presence_push_->SignalStatusUpdate.connect( + this, &CallClient::OnStatusUpdate); + presence_push_->Start(); + + buzz::Status my_status; + my_status.set_jid(xmpp_client_->jid()); + my_status.set_available(true); + my_status.set_invisible(false); + my_status.set_show(buzz::Status::SHOW_ONLINE); + my_status.set_priority(0); + my_status.set_know_capabilities(true); + my_status.set_phone_capability(true); + my_status.set_is_google_client(true); + my_status.set_version("1.0.0.66"); + + buzz::PresenceOutTask* presence_out_ = + new buzz::PresenceOutTask(xmpp_client_); + presence_out_->Send(my_status); + presence_out_->Start(); +} + +void CallClient::OnStatusUpdate(const buzz::Status& status) { + RosterItem item; + item.jid = status.jid(); + item.show = status.show(); + item.status = status.status(); + + std::string key = item.jid.Str(); + + if (status.available() && status.phone_capability()) { + Console()->Printf("Adding to roster: %s", key.c_str()); + (*roster_)[key] = item; + } else { + Console()->Printf("Removing from roster: %s", key.c_str()); + RosterMap::iterator iter = roster_->find(key); + if (iter != roster_->end()) + roster_->erase(iter); + } +} + +void CallClient::PrintRoster() { + Console()->Printf("Roster contains %d callable", roster_->size()); + RosterMap::iterator iter = roster_->begin(); + while (iter != roster_->end()) { + Console()->Printf("%s - %s", + iter->second.jid.BareJid().Str().c_str(), + DescribeStatus(iter->second.show, iter->second.status)); + iter++; + } +} + +void CallClient::MakeCallTo(const std::string& name) { + bool found = false; + buzz::Jid found_jid; + + RosterMap::iterator iter = roster_->begin(); + while (iter != roster_->end()) { + if (iter->second.jid.node() == name) { + found = true; + found_jid = iter->second.jid; + break; + } + ++iter; + } + + if (found) { + Console()->Printf("Found online friend '%s'", found_jid.Str().c_str()); + Console()->Push(new CallTask(this, found_jid, NULL)); + } else { + Console()->Printf("Could not find online friend '%s'", name.c_str()); + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h new file mode 100644 index 00000000..2400b7db --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h @@ -0,0 +1,88 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CRICKET_EXAMPLES_CALL_CALLCLIENT_H__ +#define CRICKET_EXAMPLES_CALL_CALLCLIENT_H__ + +#include <map> +#include <string> +#include "talk/p2p/base/session.h" +#include "talk/p2p/client/sessionclient.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/examples/call/status.h" + +namespace buzz { +class PresencePushTask; +class Status; +} + +namespace cricket { +class Thread; +class NetworkManager; +class PortAllocator; +class PhoneSessionClient; +class Receiver; +class Call; +} + +struct RosterItem { + buzz::Jid jid; + buzz::Status::Show show; + std::string status; +}; + +class CallClient: public sigslot::has_slots<> { +public: + CallClient(buzz::XmppClient* xmpp_client); + ~CallClient(); + + cricket::PhoneSessionClient* phone_client() const { return phone_client_; } + + void PrintRoster(); + void MakeCallTo(const std::string& name); + +private: + typedef std::map<std::string,RosterItem> RosterMap; + + buzz::XmppClient* xmpp_client_; + cricket::Thread* worker_thread_; + cricket::NetworkManager* network_manager_; + cricket::PortAllocator* port_allocator_; + cricket::SessionManager* session_manager_; + cricket::PhoneSessionClient* phone_client_; + cricket::Receiver* receiver_; + buzz::PresencePushTask* presence_push_; + RosterMap* roster_; + + void OnStateChange(buzz::XmppEngine::State state); + + void InitPhone(); + void OnRequestSignaling(); + void OnCallCreate(cricket::Call* call); + const std::string strerror(buzz::XmppEngine::Error err); + void OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state); + void OnSendStanza(cricket::SessionClient *client, const buzz::XmlElement* stanza); + + void InitPresence(); + void OnStatusUpdate(const buzz::Status& status); +}; + +#endif // CRICKET_EXAMPLES_CALL_CALLCLIENT_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc new file mode 100644 index 00000000..4150f281 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc @@ -0,0 +1,196 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern "C" { +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +} +#include <cassert> +#include <cstdio> +#include <cstdlib> +#include <cstdarg> + +#include "talk/examples/call/console.h" + +namespace { + +void PError(const char* desc) { + perror(desc); + exit(1); +} + +CConsole* gConsole = NULL; + +const uint32 MSG_UPDATE = 1; + +} // namespace + +void InitConsole(cricket::PhysicalSocketServer* ss) { + assert(gConsole == NULL); + assert(ss); + gConsole = new CConsole(ss); +} + +CConsole* Console() { + assert(gConsole); + return gConsole; +} + +CConsole::CConsole(cricket::PhysicalSocketServer* ss) + : prompting_(false), prompt_dirty_(false) { + stdin_ = ss->CreateFile(0); + stdin_->SignalReadEvent.connect(this, &CConsole::OnReadInput); + + tasks_ = new std::vector<ConsoleTask*>; +} + +CConsole::~CConsole() { + delete stdin_; + delete tasks_; +} + +void CConsole::Push(ConsoleTask* task) { + task->set_console(this); + task->SignalDone.connect(this, &CConsole::OnTaskDone); + tasks_->push_back(task); + task->Start(); + UpdatePrompt(); +} + +void CConsole::Remove(ConsoleTask* task) { + int index = -1; + for (size_t i = 0; i < tasks_->size(); ++i) { + if ((*tasks_)[i] == task) + index = i; + } + + assert(index >= 0); + tasks_->erase(tasks_->begin() + index); + if (static_cast<int>(tasks_->size()) == index) + UpdatePrompt(); + + delete task; + + if (tasks_->size() == 0) + exit(0); +} + +void CConsole::Print(const char* str) { + if (prompting_) + printf("\r"); + printf("%s\n", str); + prompting_ = false; + UpdatePrompt(); +} + +void CConsole::Print(const std::string& str) { + Print(str.c_str()); +} + +void CConsole::Printf(const char* format, ...) { + va_list ap; + va_start(ap, format); + + char buf[4096]; + int size = vsnprintf(buf, sizeof(buf), format, ap); + assert(size >= 0); + assert(size < static_cast<int>(sizeof(buf))); + buf[size] = '\0'; + Print(buf); + + va_end(ap); +} + +void CConsole::OnTaskDone(ConsoleTask* task) { + Remove(task); +} + +void CConsole::OnReadInput(cricket::AsyncFile* file) { + assert(file == stdin_); + + char buf[4096]; + int size = read(0, buf, sizeof(buf)); + if (size < 0) + PError("read"); + + prompting_ = (buf[size-1] != '\n'); + + int start = 0; + for (int i = 0; i < size; ++i) { + if (buf[i] == '\n') { + std::string line = input_; + line.append(buf + start, i + 1 - start); + input_.clear(); + + assert(tasks_->size() > 0); + tasks_->back()->ProcessLine(line); + + start = i + 1; + } + } + + input_.append(buf + start, size - start); +} + +void CConsole::OnMessage(cricket::Message* pmsg) { + assert(pmsg->message_id == MSG_UPDATE); + assert(tasks_->size() > 0); + if (prompting_) + printf("\n"); + printf("%s: %s", tasks_->back()->GetPrompt().c_str(), input_.c_str()); + fflush(stdout); + prompting_ = true; + prompt_dirty_ = false; +} + +void CConsole::UpdatePrompt() { + if (!prompt_dirty_) { + prompt_dirty_ = true; + cricket::Thread::Current()->Post(this, MSG_UPDATE); + } +} + +void ConsoleTask::ParseLine(const std::string& line, + std::vector<std::string>* words) { + assert(line.size() > 0); + assert(line[line.size() - 1] == '\n'); + + int start = -1; + int state = 0; + for (int index = 0; index <= static_cast<int>(line.size()); ++index) { + if (state == 0) { + if (!isspace(line[index])) { + start = index; + state = 1; + } + } else { + assert(state == 1); + assert(start >= 0); + if (isspace(line[index])) { + std::string word(line, start, index - start); + words->push_back(word); + start = -1; + state = 0; + } + } + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h new file mode 100644 index 00000000..aca229b9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h @@ -0,0 +1,82 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CRICKET_EXAMPLES_CALL_CONSOLE_H__ +#define CRICKET_EXAMPLES_CALL_CONSOLE_H__ + +#include <string> +#include <vector> + +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" +#include "talk/base/physicalsocketserver.h" + +class CConsole; + +class ConsoleTask { +public: + ConsoleTask() : console_(NULL) {} + virtual ~ConsoleTask() {} + + CConsole* console() const { return console_; } + void set_console(CConsole* console) { console_ = console; } + + virtual void Start() {} + virtual std::string GetPrompt() = 0; + virtual void ProcessLine(const std::string& line) = 0; // includes newline + + sigslot::signal1<ConsoleTask*> SignalDone; + +protected: + void ParseLine(const std::string& line, std::vector<std::string>* words); + +private: + CConsole* console_; +}; + +class CConsole: public cricket::MessageHandler, public sigslot::has_slots<> { +public: + CConsole(cricket::PhysicalSocketServer* ss); + ~CConsole(); + + void Push(ConsoleTask* task); + void Remove(ConsoleTask* task); + + // final newline should not be included + void Print(const char* str); + void Print(const std::string& str); + void Printf(const char* format, ...); + +private: + cricket::AsyncFile* stdin_; + std::vector<ConsoleTask*>* tasks_; + std::string input_; + bool prompting_; + bool prompt_dirty_; + + void OnTaskDone(ConsoleTask* task); + void OnReadInput(cricket::AsyncFile* file); + void OnMessage(cricket::Message* pmsg); + void UpdatePrompt(); +}; + +void InitConsole(cricket::PhysicalSocketServer* ss); +CConsole* Console(); + +#endif // CRICKET_EXAMPLES_CALL_CONSOLE_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc new file mode 100644 index 00000000..3ecdb420 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc @@ -0,0 +1,148 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <sstream> +#include <time.h> +#include "talk/xmpp/constants.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/examples/call/presenceouttask.h" + +namespace buzz { + +// string helper functions ----------------------------------------------------- +template <class T> static +bool FromString(const std::string& s, + T * t) { + std::istringstream iss(s); + return !(iss>>*t).fail(); +} + +template <class T> static +bool ToString(const T &t, + std::string* s) { + std::ostringstream oss; + oss << t; + *s = oss.str(); + return !oss.fail(); +} + +XmppReturnStatus +PresenceOutTask::Send(const Status & s) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + stanza_.reset(TranslateStatus(s)); + return XMPP_RETURN_OK; +} + +XmppReturnStatus +PresenceOutTask::SendDirected(const Jid & j, const Status & s) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + XmlElement * presence = TranslateStatus(s); + presence->AddAttr(QN_TO, j.Str()); + stanza_.reset(presence); + return XMPP_RETURN_OK; +} + +XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + XmlElement * presence = new XmlElement(QN_PRESENCE); + presence->AddAttr(QN_TO, jid.Str()); + presence->AddAttr(QN_TYPE, "probe"); + + stanza_.reset(presence); + return XMPP_RETURN_OK; +} + +int +PresenceOutTask::ProcessStart() { + if (SendStanza(stanza_.get()) != XMPP_RETURN_OK) + return STATE_ERROR; + return STATE_DONE; +} + +XmlElement * +PresenceOutTask::TranslateStatus(const Status & s) { + XmlElement * result = new XmlElement(QN_PRESENCE); + if (!s.available()) { + result->AddAttr(QN_TYPE, STR_UNAVAILABLE); + } + else { + if (s.invisible()) { + result->AddAttr(QN_TYPE, STR_INVISIBLE); + } + + if (s.show() != Status::SHOW_ONLINE && s.show() != Status::SHOW_OFFLINE) { + result->AddElement(new XmlElement(QN_SHOW)); + switch (s.show()) { + default: + result->AddText(STR_SHOW_AWAY, 1); + break; + case Status::SHOW_XA: + result->AddText(STR_SHOW_XA, 1); + break; + case Status::SHOW_DND: + result->AddText(STR_SHOW_DND, 1); + break; + case Status::SHOW_CHAT: + result->AddText(STR_SHOW_CHAT, 1); + break; + } + } + + result->AddElement(new XmlElement(QN_STATUS)); + result->AddText(s.status(), 1); + + std::string pri; + ToString(s.priority(), &pri); + + result->AddElement(new XmlElement(QN_PRIORITY)); + result->AddText(pri, 1); + + if (s.know_capabilities() && s.is_google_client()) { + result->AddElement(new XmlElement(QN_CAPS_C, true)); + result->AddAttr(QN_NODE, GOOGLE_CLIENT_NODE, 1); + result->AddAttr(QN_VER, s.version(), 1); + result->AddAttr(QN_EXT, s.phone_capability() ? "voice-v1" : "", 1); + } + + // Put the delay mark on the presence according to JEP-0091 + { + result->AddElement(new XmlElement(kQnDelayX, true)); + + // This here is why we *love* the C runtime + time_t current_time_seconds; + time(¤t_time_seconds); + struct tm* current_time = gmtime(¤t_time_seconds); + char output[256]; + strftime(output, ARRAY_SIZE(output), "%Y%m%dT%H:%M:%S", current_time); + result->AddAttr(kQnStamp, output, 1); + } + + } + + return result; +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h new file mode 100644 index 00000000..868bda59 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h @@ -0,0 +1,46 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PRESENCEOUTTASK_H_ +#define _PRESENCEOUTTASK_H_ + +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/examples/call/status.h" + +namespace buzz { + +class PresenceOutTask : public XmppTask { +public: + PresenceOutTask(Task * parent) : XmppTask(parent) {} + virtual ~PresenceOutTask() {} + + XmppReturnStatus Send(const Status & s); + XmppReturnStatus SendDirected(const Jid & j, const Status & s); + XmppReturnStatus SendProbe(const Jid& jid); + + virtual int ProcessStart(); +private: + XmlElement * TranslateStatus(const Status & s); + scoped_ptr<XmlElement> stanza_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc new file mode 100644 index 00000000..d0543b99 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc @@ -0,0 +1,172 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "talk/examples/call/presencepushtask.h" +#include "talk/xmpp/constants.h" +#include <sstream> + + +namespace buzz { + +// string helper functions ----------------------------------------------------- +template <class T> static +bool FromString(const std::string& s, + T * t) { + std::istringstream iss(s); + return !(iss>>*t).fail(); +} + +template <class T> static +bool ToString(const T &t, + std::string* s) { + std::ostringstream oss; + oss << t; + *s = oss.str(); + return !oss.fail(); +} + +static bool +IsXmlSpace(int ch) { + return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; +} + +static bool +ListContainsToken(const std::string & list, const std::string & token) { + size_t i = list.find(token); + if (i == std::string::npos || token.empty()) + return false; + bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1])); + bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()])); + return boundary_before && boundary_after; +} + + +bool +PresencePushTask::HandleStanza(const XmlElement * stanza) { + if (stanza->Name() != QN_PRESENCE) + return false; + if (stanza->HasAttr(QN_TYPE) && stanza->Attr(QN_TYPE) != STR_UNAVAILABLE) + return false; + QueueStanza(stanza); + return true; +} + +static bool IsUtf8FirstByte(int c) { + return (((c)&0x80)==0) || // is single byte + ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte +} + +int +PresencePushTask::ProcessStart() { + const XmlElement * stanza = NextStanza(); + if (stanza == NULL) + return STATE_BLOCKED; + Status s; + + s.set_jid(Jid(stanza->Attr(QN_FROM))); + + if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) { + s.set_available(false); + SignalStatusUpdate(s); + } + else { + s.set_available(true); + const XmlElement * status = stanza->FirstNamed(QN_STATUS); + if (status != NULL) { + s.set_status(status->BodyText()); + + // Truncate status messages longer than 300 bytes + if (s.status().length() > 300) { + size_t len = 300; + + // Be careful not to split legal utf-8 chars in half + while (!IsUtf8FirstByte(s.status()[len]) && len > 0) { + len -= 1; + } + std::string truncated(s.status(), 0, len); + s.set_status(truncated); + } + } + + const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY); + if (priority != NULL) { + int pri; + if (FromString(priority->BodyText(), &pri)) { + s.set_priority(pri); + } + } + + const XmlElement * show = stanza->FirstNamed(QN_SHOW); + if (show == NULL || show->FirstChild() == NULL) { + s.set_show(Status::SHOW_ONLINE); + } + else { + if (show->BodyText() == "away") { + s.set_show(Status::SHOW_AWAY); + } + else if (show->BodyText() == "xa") { + s.set_show(Status::SHOW_XA); + } + else if (show->BodyText() == "dnd") { + s.set_show(Status::SHOW_DND); + } + else if (show->BodyText() == "chat") { + s.set_show(Status::SHOW_CHAT); + } + else { + s.set_show(Status::SHOW_ONLINE); + } + } + + const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C); + if (caps != NULL) { + std::string node = caps->Attr(QN_NODE); + std::string ver = caps->Attr(QN_VER); + std::string exts = caps->Attr(QN_EXT); + + s.set_know_capabilities(true); + + if (node == GOOGLE_CLIENT_NODE) { + s.set_is_google_client(true); + s.set_version(ver); + if (ListContainsToken(exts, "voice-v1")) { + s.set_phone_capability(true); + } + } + } + + const XmlElement* delay = stanza->FirstNamed(kQnDelayX); + if (delay != NULL) { + // Ideally we would parse this according to the Psuedo ISO-8601 rules + // that are laid out in JEP-0082: + // http://www.jabber.org/jeps/jep-0082.html + std::string stamp = delay->Attr(kQnStamp); + s.set_sent_time(stamp); + } + + SignalStatusUpdate(s); + } + + return STATE_START; +} + + +} + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h new file mode 100644 index 00000000..45bc9020 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h @@ -0,0 +1,44 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PRESENCEPUSHTASK_H_ +#define _PRESENCEPUSHTASK_H_ + +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/base/sigslot.h" +#include "talk/examples/call/status.h" + +namespace buzz { + +class PresencePushTask : public XmppTask { + +public: + PresencePushTask(Task * parent) : XmppTask(parent, XmppEngine::HL_TYPE) {} + virtual int ProcessStart(); + sigslot::signal1<const Status &>SignalStatusUpdate; + +protected: + virtual bool HandleStanza(const XmlElement * stanza); +}; + + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h new file mode 100644 index 00000000..a1e76f62 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h @@ -0,0 +1,213 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" + +#define GOOGLE_CLIENT_NODE "http://www.google.com/xmpp/client/caps" + +namespace buzz { + +class Status { +public: + Status() : + pri_(0), + show_(SHOW_NONE), + available_(false), + invisible_(false), + e_code_(0), + phone_capability_(false), + know_capabilities_(false), + is_google_client_(false), + feedback_probation_(false) {}; + + ~Status() {} + + // These are arranged in "priority order", i.e., if we see + // two statuses at the same priority but with different Shows, + // we will show the one with the highest show in the following + // order. + enum Show { + SHOW_NONE = 0, + SHOW_INVISIBLE = 1, + SHOW_OFFLINE = 2, + SHOW_XA = 3, + SHOW_AWAY = 4, + SHOW_DND = 5, + SHOW_ONLINE = 6, + SHOW_CHAT = 7, + }; + + const Jid & jid() const { return jid_; } + int priority() const { return pri_; } + Show show() const { return show_; } + const std::string & status() const { return status_; } + bool available() const { return available_ ; } + bool invisible() const { return invisible_; } + int error_code() const { return e_code_; } + const std::string & error_string() const { return e_str_; } + bool know_capabilities() const { return know_capabilities_; } + bool phone_capability() const { return phone_capability_; } + bool is_google_client() const { return is_google_client_; } + const std::string & version() const { return version_; } + bool feedback_probation() const { return feedback_probation_; } + const std::string& sent_time() const { return sent_time_; } + + void set_jid(const Jid & jid) { jid_ = jid; } + void set_priority(int pri) { pri_ = pri; } + void set_show(Show show) { show_ = show; } + void set_status(const std::string & status) { status_ = status; } + void set_available(bool a) { available_ = a; } + void set_invisible(bool i) { invisible_ = i; } + void set_error(int e_code, const std::string e_str) + { e_code_ = e_code; e_str_ = e_str; } + void set_know_capabilities(bool f) { know_capabilities_ = f; } + void set_phone_capability(bool f) { phone_capability_ = f; } + void set_is_google_client(bool f) { is_google_client_ = f; } + void set_version(const std::string & v) { version_ = v; } + void set_feedback_probation(bool f) { feedback_probation_ = f; } + void set_sent_time(const std::string& time) { sent_time_ = time; } + + void UpdateWith(const Status & new_value) { + if (!new_value.know_capabilities()) { + bool k = know_capabilities(); + bool i = is_google_client(); + bool p = phone_capability(); + std::string v = version(); + + *this = new_value; + + set_know_capabilities(k); + set_is_google_client(i); + set_phone_capability(p); + set_version(v); + } + else { + *this = new_value; + } + } + + bool HasQuietStatus() const { + if (status_.empty()) + return false; + return !(QuietStatus().empty()); + } + + // Knowledge of other clients' silly automatic status strings - + // Don't show these. + std::string QuietStatus() const { + if (jid_.resource().find("Psi") != std::string::npos) { + if (status_ == "Online" || + status_.find("Auto Status") != std::string::npos) + return STR_EMPTY; + } + if (jid_.resource().find("Gaim") != std::string::npos) { + if (status_ == "Sorry, I ran out for a bit!") + return STR_EMPTY; + } + return TrimStatus(status_); + } + + std::string ExplicitStatus() const { + std::string result = QuietStatus(); + if (result.empty()) { + result = ShowStatus(); + } + return result; + } + + std::string ShowStatus() const { + std::string result; + if (!available()) { + result = "Offline"; + } + else { + switch (show()) { + case SHOW_AWAY: + case SHOW_XA: + result = "Idle"; + break; + case SHOW_DND: + result = "Busy"; + break; + case SHOW_CHAT: + result = "Chatty"; + break; + default: + result = "Available"; + break; + } + } + return result; + } + + static std::string TrimStatus(const std::string & st) { + std::string s(st); + int j = 0; + bool collapsing = true; + for (unsigned int i = 0; i < s.length(); i+= 1) { + if (s[i] <= ' ' && s[i] >= 0) { + if (collapsing) { + continue; + } + else { + s[j] = ' '; + j += 1; + collapsing = true; + } + } + else { + s[j] = s[i]; + j += 1; + collapsing = false; + } + } + if (collapsing && j > 0) { + j -= 1; + } + s.erase(j, s.length()); + return s; + } + +private: + Jid jid_; + int pri_; + Show show_; + std::string status_; + bool available_; + bool invisible_; + int e_code_; + std::string e_str_; + bool feedback_probation_; + + // capabilities (valid only if know_capabilities_ + bool know_capabilities_; + bool phone_capability_; + bool is_google_client_; + std::string version_; + + std::string sent_time_; // from the jabber:x:delay element +}; + +} + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am new file mode 100644 index 00000000..16164fb7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am @@ -0,0 +1,15 @@ +noinst_LTLIBRARIES= libcricketexampleslogin.la +libcricketexampleslogin_la_SOURCES = xmppsocket.cc \ + xmppauth.cc \ + xmppthread.cc \ + xmpppump.cc +noinst_HEADERS = xmppauth.h xmpppump.h xmppsocket.h xmppthread.h +bin_PROGRAMS = login +login_CXXFLAGS = $(AM_CXXFLAGS) +login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc +login_LDADD = $(srcdir)/../../../talk/xmpp/libcricketxmpp.la \ + $(srcdir)/../../../talk/xmllite/libcricketxmllite.la \ + $(srcdir)/../../../talk/base/libcricketbase.la \ + $(EXPAT_LIBS) -lpthread +AM_CPPFLAGS = -DPOSIX +DEFAULT_INCLUDES = -I$(srcdir)/../../.. diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc new file mode 100644 index 00000000..2939c79f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/thread.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include <iostream> + +int main(int argc, char **argv) { + printf("Auth Cookie: "); + fflush(stdout); + + char auth_cookie[256]; + scanf("%s", auth_cookie); + + char username[256]; + scanf("%s", username); + + // Start xmpp on a different thread + XmppThread thread; + thread.Start(); + + buzz::XmppClientSettings xcs; + xcs.set_user(username); + xcs.set_host("gmail.com"); + xcs.set_use_tls(false); + xcs.set_auth_cookie(auth_cookie); + xcs.set_server(cricket::SocketAddress("talk.google.com", 5222)); + thread.Login(xcs); + + // Use main thread for console input + std::string line; + while (std::getline(std::cin, line)) { + if (line == "quit") + break; + } + return 0; +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc new file mode 100644 index 00000000..66191f12 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc @@ -0,0 +1,93 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <algorithm> +#include "talk/examples/login/xmppauth.h" +#include "talk/xmpp/saslcookiemechanism.h" +#include "talk/xmpp/saslplainmechanism.h" + +XmppAuth::XmppAuth() : done_(false), error_(false) { +} + +XmppAuth::~XmppAuth() { +} + +void XmppAuth::StartPreXmppAuth(const buzz::Jid & jid, + const cricket::SocketAddress & server, + const buzz::XmppPassword & pass, + const std::string & auth_cookie) { + jid_ = jid; + passwd_ = pass; + auth_cookie_ = auth_cookie; + error_ = auth_cookie.empty(); + done_ = true; + + SignalAuthDone(); +} + +std::string XmppAuth::ChooseBestSaslMechanism( + const std::vector<std::string> & mechanisms, + bool encrypted) { + std::vector<std::string>::const_iterator it; + + // a token is the weakest auth - 15s, service-limited, so prefer it. + it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-TOKEN"); + if (it != mechanisms.end()) + return "X-GOOGLE-TOKEN"; + + // a cookie is the next weakest - 14 days + it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-COOKIE"); + if (it != mechanisms.end()) + return "X-GOOGLE-COOKIE"; + + // never pass @google.com passwords without encryption!! + if (!encrypted && (jid_.domain() == "google.com")) + return ""; + + // as a last resort, use plain authentication + if (jid_.domain() != "google.com") { + it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN"); + if (it != mechanisms.end()) + return "PLAIN"; + } + + // No good mechanism found + return ""; +} + +buzz::SaslMechanism* XmppAuth::CreateSaslMechanism( + const std::string & mechanism) { + if (mechanism == "X-GOOGLE-TOKEN") { + return new buzz::SaslCookieMechanism(mechanism, jid_.Str(), auth_cookie_); + //} else if (mechanism == "X-GOOGLE-COOKIE") { + // return new buzz::SaslCookieMechanism(mechanism, jid.Str(), sid_); + } else if (mechanism == "PLAIN") { + return new buzz::SaslPlainMechanism(jid_, passwd_); + } else { + return NULL; + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h new file mode 100644 index 00000000..57743f90 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h @@ -0,0 +1,71 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPAUTH_H_ +#define _XMPPAUTH_H_ + +#include <vector> + +#include "talk/base/sigslot.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/saslhandler.h" +#include "talk/xmpp/prexmppauth.h" +#include "talk/xmpp/xmpppassword.h" + +class XmppAuth: public buzz::PreXmppAuth { +public: + XmppAuth(); + virtual ~XmppAuth(); + + virtual void StartPreXmppAuth(const buzz::Jid & jid, + const cricket::SocketAddress & server, + const buzz::XmppPassword & pass, + const std::string & auth_cookie); + + virtual bool IsAuthDone() { return done_; } + virtual bool IsAuthorized() { return !error_; } + virtual bool HadError() { return error_; } + virtual buzz::CaptchaChallenge GetCaptchaChallenge() { + return buzz::CaptchaChallenge(); + } + virtual std::string GetAuthCookie() { return auth_cookie_; } + + virtual std::string ChooseBestSaslMechanism( + const std::vector<std::string> & mechanisms, + bool encrypted); + + virtual buzz::SaslMechanism * CreateSaslMechanism( + const std::string & mechanism); + +private: + buzz::Jid jid_; + buzz::XmppPassword passwd_; + std::string auth_cookie_; + bool done_, error_; +}; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc new file mode 100644 index 00000000..7d966fb3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/examples/login/xmpppump.h" +#include "talk/examples/login/xmppauth.h" + +XmppPump::XmppPump(XmppPumpNotify * notify) { + state_ = buzz::XmppEngine::STATE_NONE; + notify_ = notify; + client_ = new buzz::XmppClient(this); // NOTE: deleted by TaskRunner +} + +void XmppPump::DoLogin(const buzz::XmppClientSettings & xcs, + buzz::AsyncSocket* socket, + buzz::PreXmppAuth* auth) { + OnStateChange(buzz::XmppEngine::STATE_START); + client_->SignalStateChange.connect(this, &XmppPump::OnStateChange); + client_->Connect(xcs, socket, auth); + client_->Start(); +} + +void XmppPump::DoDisconnect() { + client_->Disconnect(); + OnStateChange(buzz::XmppEngine::STATE_CLOSED); +} + +void XmppPump::OnStateChange(buzz::XmppEngine::State state) { + if (state_ == state) + return; + state_ = state; + if (notify_ != NULL) + notify_->OnStateChange(state); +} + +void XmppPump::WakeTasks() { + cricket::Thread::Current()->Post(this); +} + +unsigned long long XmppPump::CurrentTime() { + return cricket::Time(); +} + +void XmppPump::OnMessage(cricket::Message *pmsg) { + RunTasks(); +} + +buzz::XmppReturnStatus XmppPump::SendStanza(const buzz::XmlElement *stanza) { + return client_->SendStanza(stanza); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h new file mode 100644 index 00000000..fd6f88bb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPPUMP_H_ +#define _XMPPPUMP_H_ + +#include "talk/base/messagequeue.h" +#include "talk/base/taskrunner.h" +#include "talk/base/thread.h" +#include "talk/base/jtime.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" + +// Simple xmpp pump + +class XmppPumpNotify { +public: + virtual void OnStateChange(buzz::XmppEngine::State state) = 0; +}; + +class XmppPump : public cricket::MessageHandler, public buzz::TaskRunner { +public: + XmppPump(XmppPumpNotify * notify = NULL); + + buzz::XmppClient *client() { return client_; } + + void DoLogin(const buzz::XmppClientSettings & xcs, + buzz::AsyncSocket* socket, + buzz::PreXmppAuth* auth); + void DoDisconnect(); + + void OnStateChange(buzz::XmppEngine::State state); + + void WakeTasks(); + + unsigned long long CurrentTime(); + + void OnMessage(cricket::Message *pmsg); + + buzz::XmppReturnStatus SendStanza(const buzz::XmlElement *stanza); + +private: + buzz::XmppClient *client_; + buzz::XmppEngine::State state_; + XmppPumpNotify *notify_; +}; + +#endif // _XMPPPUMP_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc new file mode 100644 index 00000000..33aabf3e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc @@ -0,0 +1,144 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <errno.h> +#include "talk/base/basicdefs.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#ifdef FEATURE_ENABLE_SSL +#include "talk/base/schanneladapter.h" +#endif +#include "xmppsocket.h" + +XmppSocket::XmppSocket(bool tls) : tls_(tls) { + cricket::Thread* pth = cricket::Thread::Current(); + cricket::AsyncSocket* socket = + pth->socketserver()->CreateAsyncSocket(SOCK_STREAM); +#ifdef FEATURE_ENABLE_SSL + if (tls_) + socket = new cricket::SChannelAdapter(socket); +#endif + cricket_socket_ = socket; + cricket_socket_->SignalReadEvent.connect(this, &XmppSocket::OnReadEvent); + cricket_socket_->SignalWriteEvent.connect(this, &XmppSocket::OnWriteEvent); + cricket_socket_->SignalConnectEvent.connect(this, + &XmppSocket::OnConnectEvent); + state_ = buzz::AsyncSocket::STATE_CLOSED; +} + +XmppSocket::~XmppSocket() { + Close(); + delete cricket_socket_; +} + +void XmppSocket::OnReadEvent(cricket::AsyncSocket * socket) { + SignalRead(); +} + +void XmppSocket::OnWriteEvent(cricket::AsyncSocket * socket) { + // Write bytes if there are any + while (buffer_.Length() != 0) { + int written = cricket_socket_->Send(buffer_.Data(), buffer_.Length()); + if (written > 0) { + buffer_.Shift(written); + continue; + } + if (!cricket_socket_->IsBlocking()) + LOG(LS_ERROR) << "Send error: " << cricket_socket_->GetError(); + return; + } +} + +void XmppSocket::OnConnectEvent(cricket::AsyncSocket * socket) { +#if defined(FEATURE_ENABLE_SSL) + if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) { + state_ = buzz::AsyncSocket::STATE_TLS_OPEN; + SignalSSLConnected(); + OnWriteEvent(cricket_socket_); + return; + } +#endif // !defined(FEATURE_ENABLE_SSL) + state_ = buzz::AsyncSocket::STATE_OPEN; + SignalConnected(); +} + +buzz::AsyncSocket::State XmppSocket::state() { + return state_; +} + +buzz::AsyncSocket::Error XmppSocket::error() { + return buzz::AsyncSocket::ERROR_NONE; +} + +bool XmppSocket::Connect(const cricket::SocketAddress& addr) { + if (cricket_socket_->Connect(addr) < 0) { + return cricket_socket_->IsBlocking(); + } + return true; +} + +bool XmppSocket::Read(char * data, size_t len, size_t* len_read) { + int read = cricket_socket_->Recv(data, len); + if (read > 0) { + *len_read = (size_t)read; + return true; + } + return false; +} + +bool XmppSocket::Write(const char * data, size_t len) { + buffer_.WriteBytes(data, len); + OnWriteEvent(cricket_socket_); + return true; +} + +bool XmppSocket::Close() { + if (state_ != buzz::AsyncSocket::STATE_OPEN) + return false; + if (cricket_socket_->Close() == 0) { + state_ = buzz::AsyncSocket::STATE_CLOSED; + SignalClosed(); + return true; + } + return false; +} + +bool XmppSocket::StartTls(const std::string & domainname) { +#if defined(FEATURE_ENABLE_SSL) + if (!tls_) + return false; + cricket::SChannelAdapter * ssl = + static_cast<cricket::SChannelAdapter *>(cricket_socket_); + ssl->set_ignore_bad_cert(true); + if (ssl->StartSSL(domainname.c_str(), false) != 0) + return false; + state_ = buzz::AsyncSocket::STATE_TLS_CONNECTING; + return true; +#else // !defined(FEATURE_ENABLE_SSL) + return false; +#endif // !defined(FEATURE_ENABLE_SSL) +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h new file mode 100644 index 00000000..f6c53d7d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPSOCKET_H_ +#define _XMPPSOCKET_H_ + +#include "talk/base/asyncsocket.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/sigslot.h" +#include "talk/xmpp/asyncsocket.h" + +extern cricket::AsyncSocket* cricket_socket_; + +class XmppSocket : public buzz::AsyncSocket, public sigslot::has_slots<> { +public: + XmppSocket(bool tls); + ~XmppSocket(); + + virtual buzz::AsyncSocket::State state(); + virtual buzz::AsyncSocket::Error error(); + + virtual bool Connect(const cricket::SocketAddress& addr); + virtual bool Read(char * data, size_t len, size_t* len_read); + virtual bool Write(const char * data, size_t len); + virtual bool Close(); + virtual bool StartTls(const std::string & domainname); + +private: + void OnReadEvent(cricket::AsyncSocket * socket); + void OnWriteEvent(cricket::AsyncSocket * socket); + void OnConnectEvent(cricket::AsyncSocket * socket); + + cricket::AsyncSocket * cricket_socket_; + buzz::AsyncSocket::State state_; + cricket::ByteBuffer buffer_; + bool tls_; +}; + +#endif // _XMPPSOCKET_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc new file mode 100644 index 00000000..7a1b2a13 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc @@ -0,0 +1,80 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include "talk/examples/login/xmppauth.h" + +namespace { + +const uint32 MSG_LOGIN = 1; +const uint32 MSG_DISCONNECT = 2; + +struct LoginData: public cricket::MessageData { + LoginData(const buzz::XmppClientSettings& s) : xcs(s) {} + virtual ~LoginData() {} + + buzz::XmppClientSettings xcs; +}; + +} // namespace + +XmppThread::XmppThread() { + pump_ = new XmppPump(this); +} + +XmppThread::~XmppThread() { + delete pump_; +} + +void XmppThread::Loop(int cms) { + Thread::Loop(cms); +} + +void XmppThread::Login(const buzz::XmppClientSettings& xcs) { + Post(this, MSG_LOGIN, new LoginData(xcs)); +} + +void XmppThread::Disconnect() { + Post(this, MSG_DISCONNECT); +} + +void XmppThread::OnStateChange(buzz::XmppEngine::State state) { +} + +void XmppThread::OnMessage(cricket::Message* pmsg) { + if (pmsg->message_id == MSG_LOGIN) { + assert(pmsg->pdata); + LoginData* data = reinterpret_cast<LoginData*>(pmsg->pdata); + pump_->DoLogin(data->xcs, new XmppSocket(false), new XmppAuth()); + delete data; + } else if (pmsg->message_id == MSG_DISCONNECT) { + pump_->DoDisconnect(); + } else { + assert(false); + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h new file mode 100644 index 00000000..533a2376 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h @@ -0,0 +1,57 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPTHREAD_H_ +#define _XMPPTHREAD_H_ + +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/base/thread.h" +#include "talk/examples/login/xmpppump.h" +#include "talk/examples/login/xmppsocket.h" +#include <iostream> + +class XmppThread: + public cricket::Thread, XmppPumpNotify, cricket::MessageHandler { +public: + XmppThread(); + ~XmppThread(); + + buzz::XmppClient* client() { return pump_->client(); } + + void Loop(int cms); + + void Login(const buzz::XmppClientSettings & xcs); + void Disconnect(); + +private: + XmppPump* pump_; + + void OnStateChange(buzz::XmppEngine::State state); + void OnMessage(cricket::Message* pmsg); +}; + +#endif // _XMPPTHREAD_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am new file mode 100644 index 00000000..c935a6bd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=base client diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am new file mode 100644 index 00000000..6cc30f15 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am @@ -0,0 +1,47 @@ +## Does not compile with final +KDE_OPTIONS = nofinal + +libcricketp2pbase_la_SOURCES = stun.cc \ + port.cc \ + udpport.cc \ + tcpport.cc \ + helpers.cc \ + sessionmanager.cc \ + session.cc \ + p2psocket.cc \ + relayport.cc \ + stunrequest.cc \ + stunport.cc \ + socketmanager.cc + +noinst_HEADERS = candidate.h \ + portallocator.h \ + relayport.h \ + session.h \ + sessionmessage.h \ + stunport.h \ + tcpport.h \ + helpers.h \ + port.h \ + sessionid.h \ + socketmanager.h \ + stunrequest.h \ + udpport.h \ + p2psocket.h \ + sessiondescription.h \ + sessionmanager.h \ + stun.h \ + relayserver.h \ + stunserver.h + +AM_CPPFLAGS = -DPOSIX $(all_includes) -I$(srcdir)/../../.. + +bin_PROGRAMS = relayserver stunserver +relayserver_SOURCES = relayserver.cc relayserver_main.cc +relayserver_LDADD = ../../base/libcricketbase.la libcricketp2pbase.la -lpthread +stunserver_SOURCES = stunserver.cc stunserver_main.cc +stunserver_LDADD = ../../base/libcricketbase.la libcricketp2pbase.la -lpthread + +noinst_LTLIBRARIES = libcricketp2pbase.la + +DEFAULT_INCLUDES = -I$(srcdir)/../../.. diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h new file mode 100644 index 00000000..c2f974b8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h @@ -0,0 +1,118 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CANDIDATE_H_ +#define _CANDIDATE_H_ + +#include <string> +#include <sstream> +#include "talk/base/socketaddress.h" + +namespace cricket { + +// Candidate for ICE based connection discovery. + +class Candidate { +public: + + const std::string & name() const { return name_; } + void set_name(const std::string & name) { name_ = name; } + + const std::string & protocol() const { return protocol_; } + void set_protocol(const std::string & protocol) { protocol_ = protocol; } + + const SocketAddress & address() const { return address_; } + void set_address(const SocketAddress & address) { address_ = address; } + + const float preference() const { return preference_; } + void set_preference(const float preference) { preference_ = preference; } + const std::string preference_str() const { + std::ostringstream ost; + ost << preference_; + return ost.str(); + } + void set_preference_str(const std::string & preference) { + std::istringstream ist(preference); + ist >> preference_; + } + + const std::string & username() const { return username_; } + void set_username(const std::string & username) { username_ = username; } + + const std::string & password() const { return password_; } + void set_password(const std::string & password) { password_ = password; } + + const std::string & type() const { return type_; } + void set_type(const std::string & type) { type_ = type; } + + const std::string & network_name() const { return network_name_; } + void set_network_name(const std::string & network_name) { + network_name_ = network_name; + } + + // Candidates in a new generation replace those in the old generation. + uint32 generation() const { return generation_; } + void set_generation(uint32 generation) { generation_ = generation; } + const std::string generation_str() const { + std::ostringstream ost; + ost << generation_; + return ost.str(); + } + void set_generation_str(const std::string& str) { + std::istringstream ist(str); + ist >> generation_; + } + + // Determines whether this candidate is equivalent to the given one. + bool IsEquivalent(const Candidate& c) const { + // We ignore the network name, since that is just debug information, and + // the preference, since that should be the same if the rest is (and it's + // a float so equality checking is always worrisome). + return (name_ == c.name_) && + (protocol_ == c.protocol_) && + (address_ == c.address_) && + (username_ == c.username_) && + (password_ == c.password_) && + (type_ == c.type_) && + (generation_ == c.generation_); + } + +private: + std::string name_; + std::string protocol_; + SocketAddress address_; + float preference_; + std::string username_; + std::string password_; + std::string type_; + std::string network_name_; + uint32 generation_; +}; + +} // namespace cricket + +#endif // _CANDIDATE_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc new file mode 100644 index 00000000..83e02a27 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc @@ -0,0 +1,129 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/p2p/base/helpers.h" +#include "talk/base/jtime.h" +#include <cstdlib> +#include <cassert> + +// TODO: Change this implementation to use OpenSSL's RAND_bytes. That will +// give cryptographically random values on all platforms. + +#ifdef WIN32 +#include <time.h> +#include <windows.h> +#endif + +namespace cricket { + +static long g_seed = 1L; + +int GetRandom() { + return ((g_seed = g_seed * 214013L + 2531011L) >> 16) & 0x7fff; +} + +void SetRandomSeed(unsigned long seed) +{ + g_seed = (long)seed; +} + +static bool s_initrandom; + +void InitRandom(const char *client_unique, size_t len) { + s_initrandom = true; + + // Hash this string - unique per client + + uint32 hash = 0; + if (client_unique != NULL) { + for (int i = 0; i < (int)len; i++) + hash = ((hash << 2) + hash) + client_unique[i]; + } + + // Now initialize the seed against a high resolution + // counter + +#ifdef WIN32 + LARGE_INTEGER big; + QueryPerformanceCounter(&big); + SetRandomSeed(big.LowPart ^ hash); +#else + SetRandomSeed(Time() ^ hash); +#endif +} + +const char BASE64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +// Generates a random string of the given length. We generate base64 values so +// that they will be printable, though that's not necessary. + +std::string CreateRandomString(int len) { + // Random number generator should of been initialized! + assert(s_initrandom); + if (!s_initrandom) + InitRandom(0, 0); + + std::string str; + for (int i = 0; i < len; i++) +#if defined(_MSC_VER) && _MSC_VER < 1300 + str.insert(str.end(), BASE64[GetRandom() & 63]); +#else + str.push_back(BASE64[GetRandom() & 63]); +#endif + return str; +} + +uint32 CreateRandomId() { + uint8 b1 = (uint8)(GetRandom() & 255); + uint8 b2 = (uint8)(GetRandom() & 255); + uint8 b3 = (uint8)(GetRandom() & 255); + uint8 b4 = (uint8)(GetRandom() & 255); + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); +} + +bool IsBase64Char(char ch) { + return (('A' <= ch) && (ch <= 'Z')) || + (('a' <= ch) && (ch <= 'z')) || + (('0' <= ch) && (ch <= '9')) || + (ch == '+') || (ch == '/'); +} + +bool IsBase64Encoded(const std::string& str) { + for (size_t i = 0; i < str.size(); ++i) { + if (!IsBase64Char(str.at(i))) + return false; + } + return true; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h new file mode 100644 index 00000000..1c8dfa7f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h @@ -0,0 +1,51 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HELPERS_H__ +#define __HELPERS_H__ + +#include "talk/base/basictypes.h" +#include <string> + +namespace cricket { + +// srand initializer +void InitRandom(const char *client_unique, size_t len); + +// Generates a (cryptographically) random string of the given length. +std::string CreateRandomString(int length); + +// Generates a random id +uint32 CreateRandomId(); + +// Determines whether the given string consists entirely of valid base64 +// encoded characters. +bool IsBase64Encoded(const std::string& str); + +} // namespace cricket + +#endif // __HELPERS_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc new file mode 100644 index 00000000..eb53efeb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc @@ -0,0 +1,910 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +// Description of the P2PSocket class in P2PSocket.h +// +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include <iostream> +#include <cassert> +#include "talk/base/logging.h" +#include "talk/p2p/base/p2psocket.h" +#include <errno.h> +namespace { + +// messages for queuing up work for ourselves +const uint32 MSG_SORT = 1; +const uint32 MSG_PING = 2; +const uint32 MSG_ALLOCATE = 3; + +// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) +// for pinging. When the socket is writable, we will use only 1 Kbps because +// we don't want to degrade the quality on a modem. These numbers should work +// well on a 28.8K modem, which is the slowest connection on which the voice +// quality is reasonable at all. +static const uint32 PING_PACKET_SIZE = 60 * 8; +static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms +static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000;// 50ms + +// If there is a current writable connection, then we will also try hard to +// make sure it is pinged at this rate. +static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit + +// The minimum improvement in MOS that justifies a switch. +static const double kMinImprovement = 10; + +// Amount of time that we wait when *losing* writability before we try doing +// another allocation. +static const int kAllocateDelay = 1 * 1000; // 1 second + +// We will try creating a new allocator from scratch after a delay of this +// length without becoming writable (or timing out). +static const int kAllocatePeriod = 20 * 1000; // 20 seconds + +cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port, + cricket::Port* origin_port) { + if (!origin_port) + return cricket::Port::ORIGIN_MESSAGE; + else if (port == origin_port) + return cricket::Port::ORIGIN_THIS_PORT; + else + return cricket::Port::ORIGIN_OTHER_PORT; +} + +// Compares two connections based only on static information about them. +int CompareConnectionCandidates(cricket::Connection* a, + cricket::Connection* b) { + // Combine local and remote preferences + assert(a->local_candidate().preference() == a->port()->preference()); + assert(b->local_candidate().preference() == b->port()->preference()); + double a_pref = a->local_candidate().preference() + * a->remote_candidate().preference(); + double b_pref = b->local_candidate().preference() + * b->remote_candidate().preference(); + + // Now check combined preferences. Lower values get sorted last. + if (a_pref > b_pref) + return 1; + if (a_pref < b_pref) + return -1; + + return 0; +} + +// Compare two connections based on their writability and static preferences. +int CompareConnections(cricket::Connection *a, cricket::Connection *b) { + // Sort based on write-state. Better states have lower values. + if (a->write_state() < b->write_state()) + return 1; + if (a->write_state() > b->write_state()) + return -1; + + // Compare the candidate information. + return CompareConnectionCandidates(a, b); +} + +// Wraps the comparison connection into a less than operator that puts higher +// priority writable connections first. +class ConnectionCompare { +public: + bool operator()(const cricket::Connection *ca, + const cricket::Connection *cb) { + cricket::Connection* a = const_cast<cricket::Connection*>(ca); + cricket::Connection* b = const_cast<cricket::Connection*>(cb); + + // Compare first on writability and static preferences. + int cmp = CompareConnections(a, b); + if (cmp > 0) + return true; + if (cmp < 0) + return false; + + // Otherwise, sort based on latency estimate. + return a->rtt() < b->rtt(); + + // Should we bother checking for the last connection that last received + // data? It would help rendezvous on the connection that is also receiving + // packets. + // + // TODO: Yes we should definitely do this. The TCP protocol gains + // efficiency by being used bidirectionally, as opposed to two separate + // unidirectional streams. This test should probably occur before + // comparison of local prefs (assuming combined prefs are the same). We + // need to be careful though, not to bounce back and forth with both sides + // trying to rendevous with the other. + } +}; + +// Determines whether we should switch between two connections, based first on +// static preferences and then (if those are equal) on latency estimates. +bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { + if (a_conn == b_conn) + return false; + + if ((a_conn == NULL) || (b_conn == NULL)) // don't think the latter should happen + return true; + + int prefs_cmp = CompareConnections(a_conn, b_conn); + if (prefs_cmp < 0) + return true; + if (prefs_cmp > 0) + return false; + + return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; +} + +} // unnamed namespace + +namespace cricket { + +P2PSocket::P2PSocket(const std::string &name, PortAllocator *allocator) +: worker_thread_(Thread::Current()), name_(name), allocator_(allocator), + error_(0), state_(STATE_CONNECTING), waiting_for_signaling_(false), + best_connection_(NULL), pinging_started_(false), sort_dirty_(false), + was_writable_(false), was_timed_out_(true) { +} + +P2PSocket::~P2PSocket() { + assert(worker_thread_ == Thread::Current()); + + thread()->Clear(this); + + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) + delete allocator_sessions_[i]; +} + +// Add the allocator session to our list so that we know which sessions +// are still active. +void P2PSocket::AddAllocatorSession(PortAllocatorSession* session) { + session->set_generation(static_cast<uint32>(allocator_sessions_.size())); + allocator_sessions_.push_back(session); + + // We now only want to apply new candidates that we receive to the ports + // created by this new session because these are replacing those of the + // previous sessions. + ports_.clear(); + + session->SignalPortReady.connect(this, &P2PSocket::OnPortReady); + session->SignalCandidatesReady.connect(this, &P2PSocket::OnCandidatesReady); + session->GetInitialPorts(); + if (pinging_started_) + session->StartGetAllPorts(); +} + +// Go into the state of processing candidates, and running in general +void P2PSocket::StartProcessingCandidates() { + assert(worker_thread_ == Thread::Current()); + + // Kick off an allocator session + OnAllocate(); + + // Start pinging as the ports come in. + thread()->Post(this, MSG_PING); +} + +// Reset the socket, clear up any previous allocations and start over +void P2PSocket::Reset() { + assert(worker_thread_ == Thread::Current()); + + // Get rid of all the old allocators. This should clean up everything. + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) + delete allocator_sessions_[i]; + + allocator_sessions_.clear(); + ports_.clear(); + connections_.clear(); + best_connection_ = NULL; + + // Forget about all of the candidates we got before. + remote_candidates_.clear(); + + // Revert to the connecting state. + set_state(STATE_CONNECTING); + + // Reinitialize the rest of our state. + waiting_for_signaling_ = false; + pinging_started_ = false; + sort_dirty_ = false; + was_writable_ = false; + was_timed_out_ = true; + + // Start a new allocator. + OnAllocate(); + + // Start pinging as the ports come in. + thread()->Clear(this); + thread()->Post(this, MSG_PING); +} + +// A new port is available, attempt to make connections for it +void P2PSocket::OnPortReady(PortAllocatorSession *session, Port* port) { + assert(worker_thread_ == Thread::Current()); + + // Set in-effect options on the new port + for (OptionMap::const_iterator it = options_.begin(); it != options_.end(); ++it) { + int val = port->SetOption(it->first, it->second); + if (val < 0) { + LOG(WARNING) << "SetOption(" << it->first << ", " << it->second << ") failed: " << port->GetError(); + } + } + + // Remember the ports and candidates, and signal that candidates are ready. + // The session will handle this, and send an initiate/accept/modify message + // if one is pending. + + ports_.push_back(port); + port->SignalUnknownAddress.connect(this, &P2PSocket::OnUnknownAddress); + port->SignalDestroyed.connect(this, &P2PSocket::OnPortDestroyed); + + // Attempt to create a connection from this new port to all of the remote + // candidates that we were given so far. + + std::vector<RemoteCandidate>::iterator iter; + for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); + ++iter) + CreateConnection(port, *iter, iter->origin_port(), false); + + SortConnections(); +} + +// A new candidate is available, let listeners know +void P2PSocket::OnCandidatesReady(PortAllocatorSession *session, + const std::vector<Candidate>& candidates) { + SignalCandidatesReady(this, candidates); +} + +// Handle stun packets +void P2PSocket::OnUnknownAddress(Port *port, + const SocketAddress &address, + StunMessage *stun_msg, + const std::string &remote_username) { + assert(worker_thread_ == Thread::Current()); + + // Port has received a valid stun packet from an address that no Connection + // is currently available for. See if the remote user name is in the remote + // candidate list. If it isn't return error to the stun request. + + const Candidate *candidate = NULL; + std::vector<RemoteCandidate>::iterator it; + for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) { + if ((*it).username() == remote_username) { + candidate = &(*it); + break; + } + } + if (candidate == NULL) { + // Don't know about this username, the request is bogus + // This sometimes happens if a binding response comes in before the ACCEPT + // message. It is totally valid; the retry state machine will try again. + + port->SendBindingErrorResponse(stun_msg, address, + STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS); + delete stun_msg; + return; + } + + // Check for connectivity to this address. Create connections + // to this address across all local ports. First, add this as a new remote + // address + + Candidate new_remote_candidate = *candidate; + new_remote_candidate.set_address(address); + //new_remote_candidate.set_protocol(port->protocol()); + + // This remote username exists. Now create connections using this candidate, + // and resort + + if (CreateConnections(new_remote_candidate, port, true)) { + // Send the pinger a successful stun response. + port->SendBindingResponse(stun_msg, address); + + // Update the list of connections since we just added another. We do this + // after sending the response since it could (in principle) delete the + // connection in question. + SortConnections(); + } else { + // Hopefully this won't occur, because changing a destination address + // shouldn't cause a new connection to fail + assert(false); + port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, + STUN_ERROR_REASON_SERVER_ERROR); + } + + delete stun_msg; +} + +// We received a candidate from the other side, make connections so we +// can try to use these remote candidates with our local candidates. +void P2PSocket::AddRemoteCandidates( + const std::vector<Candidate> &remote_candidates) { + assert(worker_thread_ == Thread::Current()); + + // The remote candidates have come in. Remember them and start to establish + // connections + + std::vector<Candidate>::const_iterator it; + for (it = remote_candidates.begin(); it != remote_candidates.end(); ++it) + CreateConnections(*it, NULL, false); + + // Resort the connections + + SortConnections(); +} + +// Creates connections from all of the ports that we care about to the given +// remote candidate. The return value is true iff we created a connection from +// the origin port. +bool P2PSocket::CreateConnections(const Candidate &remote_candidate, + Port* origin_port, + bool readable) { + assert(worker_thread_ == Thread::Current()); + + // Add a new connection for this candidate to every port that allows such a + // connection (i.e., if they have compatible protocols) and that does not + // already have a connection to an equivalent candidate. We must be careful + // to make sure that the origin port is included, even if it was pruned, + // since that may be the only port that can create this connection. + + bool created = false; + + std::vector<Port *>::reverse_iterator it; + for (it = ports_.rbegin(); it != ports_.rend(); ++it) { + if (CreateConnection(*it, remote_candidate, origin_port, readable)) { + if (*it == origin_port) + created = true; + } + } + + if ((origin_port != NULL) && + find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { + if (CreateConnection(origin_port, remote_candidate, origin_port, readable)) + created = true; + } + + // Remember this remote candidate so that we can add it to future ports. + RememberRemoteCandidate(remote_candidate, origin_port); + + return created; +} + +// Setup a connection object for the local and remote candidate combination. +// And then listen to connection object for changes. +bool P2PSocket::CreateConnection(Port* port, + const Candidate& remote_candidate, + Port* origin_port, + bool readable) { + // Look for an existing connection with this remote address. If one is not + // found, then we can create a new connection for this address. + Connection* connection = port->GetConnection(remote_candidate.address()); + if (connection != NULL) { + // It is not legal to try to change any of the parameters of an existing + // connection; however, the other side can send a duplicate candidate. + if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { + LOG(INFO) << "Attempt to change a remote candidate"; + return false; + } + } else { + Port::CandidateOrigin origin = GetOrigin(port, origin_port); + connection = port->CreateConnection(remote_candidate, origin); + if (!connection) + return false; + + connections_.push_back(connection); + connection->SignalReadPacket.connect(this, &P2PSocket::OnReadPacket); + connection->SignalStateChange.connect(this, &P2PSocket::OnConnectionStateChange); + connection->SignalDestroyed.connect(this, &P2PSocket::OnConnectionDestroyed); + } + + // If we are readable, it is because we are creating this in response to a + // ping from the other side. This will cause the state to become readable. + if (readable) + connection->ReceivedPing(); + + return true; +} + +// Maintain our remote candidate list, adding this new remote one. +void P2PSocket::RememberRemoteCandidate(const Candidate& remote_candidate, + Port* origin_port) { + // Remove any candidates whose generation is older than this one. The + // presence of a new generation indicates that the old ones are not useful. + uint32 i = 0; + while (i < remote_candidates_.size()) { + if (remote_candidates_[i].generation() < remote_candidate.generation()) { + remote_candidates_.erase(remote_candidates_.begin() + i); + LOG(INFO) << "Pruning candidate from old generation: " + << remote_candidates_[i].address().ToString(); + + } else { + i += 1; + } + } + + // Make sure this candidate is not a duplicate. + for (uint32 i = 0; i < remote_candidates_.size(); ++i) { + if (remote_candidates_[i].IsEquivalent(remote_candidate)) { + LOG(INFO) << "Duplicate candidate: " + << remote_candidate.address().ToString(); + return; + } + } + + // Try this candidate for all future ports. + remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port)); + + // We have some candidates from the other side, we are now serious about + // this connection. Let's do the StartGetAllPorts thing. + if (!pinging_started_) { + pinging_started_ = true; + for (size_t i = 0; i < allocator_sessions_.size(); ++i) { + if (!allocator_sessions_[i]->IsGettingAllPorts()) + allocator_sessions_[i]->StartGetAllPorts(); + } + } +} + +// Send data to the other side, using our best connection +int P2PSocket::Send(const char *data, size_t len) { + // This can get called on any thread that is convenient to write from! + if (best_connection_ == NULL) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = best_connection_->Send(data, len); + if (sent <= 0) { + assert(sent < 0); + error_ = best_connection_->GetError(); + } + return sent; +} + +// Monitor connection states +void P2PSocket::UpdateConnectionStates() { + uint32 now = Time(); + + // We need to copy the list of connections since some may delete themselves + // when we call UpdateState. + for (uint32 i = 0; i < connections_.size(); ++i) + connections_[i]->UpdateState(now); +} + +// Prepare for best candidate sorting +void P2PSocket::RequestSort() { + if (!sort_dirty_) { + worker_thread_->Post(this, MSG_SORT); + sort_dirty_ = true; + } +} + +// Sort the available connections to find the best one. We also monitor +// the number of available connections and the current state so that we +// can possibly kick off more allocators (for more connections). +void P2PSocket::SortConnections() { + assert(worker_thread_ == Thread::Current()); + + // Make sure the connection states are up-to-date since this affects how they + // will be sorted. + UpdateConnectionStates(); + + // Any changes after this point will require a re-sort. + sort_dirty_ = false; + + // Get a list of the networks that we are using. + std::set<Network*> networks; + for (uint32 i = 0; i < connections_.size(); ++i) + networks.insert(connections_[i]->port()->network()); + + // Find the best alternative connection by sorting. It is important to note + // that amongst equal preference, writable connections, this will choose the + // one whose estimated latency is lowest. So it is the only one that we + // need to consider switching to. + + ConnectionCompare cmp; + std::stable_sort(connections_.begin(), connections_.end(), cmp); + Connection* top_connection = NULL; + if (connections_.size() > 0) + top_connection = connections_[0]; + + // If necessary, switch to the new choice. + if (ShouldSwitch(best_connection_, top_connection)) + SwitchBestConnectionTo(top_connection); + + // We can prune any connection for which there is a writable connection on + // the same network with better or equal prefences. We leave those with + // better preference just in case they become writable later (at which point, + // we would prune out the current best connection). We leave connections on + // other networks because they may not be using the same resources and they + // may represent very distinct paths over which we can switch. + std::set<Network*>::iterator network; + for (network = networks.begin(); network != networks.end(); ++network) { + Connection* primier = GetBestConnectionOnNetwork(*network); + if (!primier || (primier->write_state() != Connection::STATE_WRITABLE)) + continue; + + for (uint32 i = 0; i < connections_.size(); ++i) { + if ((connections_[i] != primier) && + (connections_[i]->port()->network() == *network) && + (CompareConnectionCandidates(primier, connections_[i]) >= 0)) { + connections_[i]->Prune(); + } + } + } + + // Count the number of connections in the various states. + + int writable = 0; + int write_connect = 0; + int write_timeout = 0; + + for (uint32 i = 0; i < connections_.size(); ++i) { + switch (connections_[i]->write_state()) { + case Connection::STATE_WRITABLE: + ++writable; + break; + case Connection::STATE_WRITE_CONNECT: + ++write_connect; + break; + case Connection::STATE_WRITE_TIMEOUT: + ++write_timeout; + break; + default: + assert(false); + } + } + + if (writable > 0) { + HandleWritable(); + } else if (write_connect > 0) { + HandleNotWritable(); + } else { + HandleAllTimedOut(); + } + + // Notify of connection state change + SignalConnectionMonitor(this); +} + +// Track the best connection, and let listeners know +void P2PSocket::SwitchBestConnectionTo(Connection* conn) { + best_connection_ = conn; + if (best_connection_) + SignalConnectionChanged(this, + best_connection_->remote_candidate().address()); +} + +// We checked the status of our connections and we had at least one that +// was writable, go into the writable state. +void P2PSocket::HandleWritable() { + // + // One or more connections writable! + // + if (state_ != STATE_WRITABLE) { + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) { + if (allocator_sessions_[i]->IsGettingAllPorts()) { + allocator_sessions_[i]->StopGetAllPorts(); + } + } + + // Stop further allocations. + thread()->Clear(this, MSG_ALLOCATE); + } + + // We're writable, obviously we aren't timed out + was_writable_ = true; + was_timed_out_ = false; + set_state(STATE_WRITABLE); +} + +// We checked the status of our connections and we didn't have any that +// were writable, go into the connecting state (kick off a new allocator +// session). +void P2PSocket::HandleNotWritable() { + // + // No connections are writable but not timed out! + // + if (was_writable_) { + // If we were writable, let's kick off an allocator session immediately + was_writable_ = false; + OnAllocate(); + } + + // We were connecting, obviously not ALL timed out. + was_timed_out_ = false; + set_state(STATE_CONNECTING); +} + +// We checked the status of our connections and not only weren't they writable +// but they were also timed out, we really need a new allocator. +void P2PSocket::HandleAllTimedOut() { + // + // No connections... all are timed out! + // + if (!was_timed_out_) { + // We weren't timed out before, so kick off an allocator now (we'll still + // be in the fully timed out state until the allocator actually gives back + // new ports) + OnAllocate(); + } + + // NOTE: we start was_timed_out_ in the true state so that we don't get + // another allocator created WHILE we are in the process of building up + // our first allocator. + was_timed_out_ = true; + was_writable_ = false; + set_state(STATE_CONNECTING); +} + +// If we have a best connection, return it, otherwise return top one in the +// list (later we will mark it best). +Connection* P2PSocket::GetBestConnectionOnNetwork(Network* network) { + // If the best connection is on this network, then it wins. + if (best_connection_ && (best_connection_->port()->network() == network)) + return best_connection_; + + // Otherwise, we return the top-most in sorted order. + for (uint32 i = 0; i < connections_.size(); ++i) { + if (connections_[i]->port()->network() == network) + return connections_[i]; + } + + return NULL; +} + +// Handle any queued up requests +void P2PSocket::OnMessage(Message *pmsg) { + if (pmsg->message_id == MSG_SORT) + OnSort(); + else if (pmsg->message_id == MSG_PING) + OnPing(); + else if (pmsg->message_id == MSG_ALLOCATE) + OnAllocate(); + else + assert(false); +} + +// Handle queued up sort request +void P2PSocket::OnSort() { + // Resort the connections based on the new statistics. + SortConnections(); +} + +// Handle queued up ping request +void P2PSocket::OnPing() { + // Make sure the states of the connections are up-to-date (since this affects + // which ones are pingable). + UpdateConnectionStates(); + + // Find the oldest pingable connection and have it do a ping. + Connection* conn = FindNextPingableConnection(); + if (conn) + conn->Ping(Time()); + + // Post ourselves a message to perform the next ping. + uint32 delay = (state_ == STATE_WRITABLE) ? WRITABLE_DELAY : UNWRITABLE_DELAY; + thread()->PostDelayed(delay, this, MSG_PING); +} + +// Is the connection in a state for us to even consider pinging the other side? +bool P2PSocket::IsPingable(Connection* conn) { + // An unconnected connection cannot be written to at all, so pinging is out + // of the question. + if (!conn->connected()) + return false; + + if (state_ == STATE_WRITABLE) { + // If we are writable, then we only want to ping connections that could be + // better than this one, i.e., the ones that were not pruned. + return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT); + } else { + // If we are not writable, then we need to try everything that might work. + // This includes both connections that do not have write timeout as well as + // ones that do not have read timeout. A connection could be readable but + // be in write-timeout if we pruned it before. Since the other side is + // still pinging it, it very well might still work. + return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) || + (conn->read_state() != Connection::STATE_READ_TIMEOUT); + } +} + +// Returns the next pingable connection to ping. This will be the oldest +// pingable connection unless we have a writable connection that is past the +// maximum acceptable ping delay. +Connection* P2PSocket::FindNextPingableConnection() { + uint32 now = Time(); + if (best_connection_ && + (best_connection_->write_state() == Connection::STATE_WRITABLE) && + (best_connection_->last_ping_sent() + + MAX_CURRENT_WRITABLE_DELAY <= now)) { + return best_connection_; + } + + Connection* oldest_conn = NULL; + uint32 oldest_time = 0xFFFFFFFF; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (IsPingable(connections_[i])) { + if (connections_[i]->last_ping_sent() < oldest_time) { + oldest_time = connections_[i]->last_ping_sent(); + oldest_conn = connections_[i]; + } + } + } + return oldest_conn; +} + +// return the number of "pingable" connections +uint32 P2PSocket::NumPingableConnections() { + uint32 count = 0; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (IsPingable(connections_[i])) + count += 1; + } + return count; +} + +// When a connection's state changes, we need to figure out who to use as +// the best connection again. It could have become usable, or become unusable. +void P2PSocket::OnConnectionStateChange(Connection *connection) { + assert(worker_thread_ == Thread::Current()); + + // We have to unroll the stack before doing this because we may be changing + // the state of connections while sorting. + RequestSort(); +} + +// When a connection is removed, edit it out, and then update our best +// connection. +void P2PSocket::OnConnectionDestroyed(Connection *connection) { + assert(worker_thread_ == Thread::Current()); + + // Remove this connection from the list. + std::vector<Connection*>::iterator iter = + find(connections_.begin(), connections_.end(), connection); + assert(iter != connections_.end()); + connections_.erase(iter); + + LOG(INFO) << "Removed connection from p2p socket: " + << static_cast<int>(connections_.size()) << " remaining"; + + // If this is currently the best connection, then we need to pick a new one. + // The call to SortConnections will pick a new one. It looks at the current + // best connection in order to avoid switching between fairly similar ones. + // Since this connection is no longer an option, we can just set best to NULL + // and re-choose a best assuming that there was no best connection. + if (best_connection_ == connection) { + SwitchBestConnectionTo(NULL); + RequestSort(); + } +} + +// When a port is destroyed remove it from our list of ports to use for +// connection attempts. +void P2PSocket::OnPortDestroyed(Port* port) { + assert(worker_thread_ == Thread::Current()); + + // Remove this port from the list (if we didn't drop it already). + std::vector<Port*>::iterator iter = find(ports_.begin(), ports_.end(), port); + if (iter != ports_.end()) + ports_.erase(iter); + + LOG(INFO) << "Removed port from p2p socket: " + << static_cast<int>(ports_.size()) << " remaining"; +} + +// We data is available, let listeners know +void P2PSocket::OnReadPacket(Connection *connection, + const char *data, size_t len) { + assert(worker_thread_ == Thread::Current()); + + // Let the client know of an incoming packet + + SignalReadPacket(this, data, len); +} + +// return socket name +const std::string &P2PSocket::name() const { + return name_; +} + +// return socket error value +int P2PSocket::GetError() { + return error_; +} + +// return a reference to the list of connections +const std::vector<Connection *>& P2PSocket::connections() { + return connections_; +} + +// Set options on ourselves is simply setting options on all of our available +// port objects. +int P2PSocket::SetOption(Socket::Option opt, int value) { + OptionMap::iterator it = options_.find(opt); + if (it == options_.end()) { + options_.insert(std::make_pair(opt, value)); + } else if (it->second == value) { + return 0; + } else { + it->second = value; + } + + for (uint32 i = 0; i < ports_.size(); ++i) { + int val = ports_[i]->SetOption(opt, value); + if (val < 0) { + // Because this also occurs deferred, probably no point in reporting an error + LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: " << ports_[i]->GetError(); + } + } + return 0; +} + +// returns the current state +P2PSocket::State P2PSocket::state() { + return state_; +} + +// Set the current state, and let listeners know when it changes +void P2PSocket::set_state(P2PSocket::State state) { + assert(worker_thread_ == Thread::Current()); + if (state != state_) { + state_ = state; + SignalState(this, state); + } +} + +// Time for a new allocator, lets make sure we have a signalling channel +// to communicate candidates through first. +void P2PSocket::OnAllocate() { + // Allocation timer went off + waiting_for_signaling_ = true; + SignalRequestSignaling(); +} + +// When the signalling channel is ready, we can really kick off the allocator +void P2PSocket::OnSignalingReady() { + if (waiting_for_signaling_) { + waiting_for_signaling_ = false; + AddAllocatorSession(allocator_->CreateSession(name_)); + thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE); + } +} + +// return the current best connection writable state. +bool P2PSocket::writable() { + assert(worker_thread_ == Thread::Current()); + + if (best_connection_ == NULL) + return false; + return best_connection_->write_state() == Connection::STATE_WRITABLE; +} + +// return the worker thread +Thread *P2PSocket::thread() { + return worker_thread_; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h new file mode 100644 index 00000000..32eb1f6d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h @@ -0,0 +1,164 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +// P2PSocket wraps up the state management of the connection between two +// P2P clients. Clients have candidate ports for connecting, and connections +// which are combinations of candidates from each end (Alice and Bob each +// have candidates, one candidate from Alice and one candidate from Bob are +// used to make a connection, repeat to make many connections). +// +// When all of the available connections become invalid (non-writable), we +// kick off a process of determining more candidates and more connections. +// +#ifndef _CRICKET_P2P_BASE_P2PSOCKET_H_ +#define _CRICKET_P2P_BASE_P2PSOCKET_H_ + +#include <vector> +#include <string> +#include "talk/base/sigslot.h" +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/portallocator.h" + +namespace cricket { + +// Adds the port on which the candidate originated. +class RemoteCandidate: public Candidate { + public: + RemoteCandidate(const Candidate& c, Port* origin_port) + : Candidate(c), origin_port_(origin_port) {} + + Port* origin_port() { return origin_port_; } + + private: + Port* origin_port_; +}; + +// P2PSocket manages the candidates and connection process to keep two P2P +// clients connected to each other. +class P2PSocket : public MessageHandler, public sigslot::has_slots<> { + public: + enum State { + STATE_CONNECTING = 0, // establishing writability + STATE_WRITABLE, // connected - ready for writing + }; + + P2PSocket(const std::string &name, PortAllocator *allocator); + virtual ~P2PSocket(); + + // Typically SocketManager calls these + + const std::string &name() const; + void StartProcessingCandidates(); + void AddRemoteCandidates(const std::vector<Candidate> &remote_candidates); + void OnSignalingReady(); + void Reset(); + + // Typically the Session Client calls these + + int Send(const char *data, size_t len); + int SetOption(Socket::Option opt, int value); + int GetError(); + + State state(); + bool writable(); + const std::vector<Connection *>& connections(); + Connection* best_connection() { return best_connection_; } + Thread *thread(); + + sigslot::signal2<P2PSocket *, State> SignalState; + sigslot::signal0<> SignalRequestSignaling; + sigslot::signal3<P2PSocket *, const char *, size_t> SignalReadPacket; + sigslot::signal2<P2PSocket *, const SocketAddress &> SignalConnectionChanged; + sigslot::signal2<P2PSocket *, const std::vector<Candidate>&> + SignalCandidatesReady; + sigslot::signal1<P2PSocket *> SignalConnectionMonitor; + + // Handler for internal messages. + virtual void OnMessage(Message *pmsg); + + private: + void set_state(State state); + void UpdateConnectionStates(); + void RequestSort(); + void SortConnections(); + void SwitchBestConnectionTo(Connection* conn); + void HandleWritable(); + void HandleNotWritable(); + void HandleAllTimedOut(); + Connection* GetBestConnectionOnNetwork(Network* network); + bool CreateConnections(const Candidate &remote_candidate, Port* origin_port, + bool readable); + bool CreateConnection(Port* port, const Candidate& remote_candidate, + Port* origin_port, bool readable); + void RememberRemoteCandidate(const Candidate& remote_candidate, + Port* origin_port); + void OnUnknownAddress(Port *port, const SocketAddress &addr, + StunMessage *stun_msg, + const std::string &remote_username); + void OnPortReady(PortAllocatorSession *session, Port* port); + void OnCandidatesReady(PortAllocatorSession *session, + const std::vector<Candidate>& candidates); + void OnConnectionStateChange(Connection *connection); + void OnConnectionDestroyed(Connection *connection); + void OnPortDestroyed(Port* port); + void OnAllocate(); + void OnReadPacket(Connection *connection, const char *data, size_t len); + void OnSort(); + void OnPing(); + bool IsPingable(Connection* conn); + Connection* FindNextPingableConnection(); + uint32 NumPingableConnections(); + PortAllocatorSession* allocator_session() { + return allocator_sessions_.back(); + } + void AddAllocatorSession(PortAllocatorSession* session); + + Thread *worker_thread_; + State state_; + bool waiting_for_signaling_; + int error_; + std::string name_; + PortAllocator *allocator_; + std::vector<PortAllocatorSession*> allocator_sessions_; + std::vector<Port *> ports_; + std::vector<Connection *> connections_; + Connection *best_connection_; + std::vector<RemoteCandidate> remote_candidates_; + bool pinging_started_; // indicates whether StartGetAllCandidates has been called + bool sort_dirty_; // indicates whether another sort is needed right now + bool was_writable_; + bool was_timed_out_; + typedef std::map<Socket::Option, int> OptionMap; + OptionMap options_; + + DISALLOW_EVIL_CONSTRUCTORS(P2PSocket); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_P2PSOCKET_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc new file mode 100644 index 00000000..14549b5b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc @@ -0,0 +1,869 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/base/asyncudpsocket.h" +#include "talk/base/asynctcpsocket.h" +#include "talk/base/socketadapters.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/helpers.h" +#include "talk/base/scoped_ptr.h" +#include <errno.h> +#include <algorithm> +#include <iostream> +#include <cassert> +#include <vector> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcmp; +} +#endif + +namespace { + +// The length of time we wait before timing out readability on a connection. +const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds + +// The length of time we wait before timing out writability on a connection. +const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds + +// The length of time we wait before we become unwritable. +const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds + +// The number of pings that must fail to respond before we become unwritable. +const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5; + +// This is the length of time that we wait for a ping response to come back. +const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds + +// Determines whether we have seen at least the given maximum number of +// pings fail to have a response. +inline bool TooManyFailures( + const std::vector<uint32>& pings_since_last_response, + uint32 maximum_failures, + uint32 rtt_estimate, + uint32 now) { + + // If we haven't sent that many pings, then we can't have failed that many. + if (pings_since_last_response.size() < maximum_failures) + return false; + + // Check if the window in which we would expect a response to the ping has + // already elapsed. + return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now; +} + +// Determines whether we have gone too long without seeing any response. +inline bool TooLongWithoutResponse( + const std::vector<uint32>& pings_since_last_response, + uint32 maximum_time, + uint32 now) { + + if (pings_since_last_response.size() == 0) + return false; + + return pings_since_last_response[0] + maximum_time < now; +} + +// We will restrict RTT estimates (when used for determining state) to be +// within a reasonable range. +const uint32 MINIMUM_RTT = 100; // 0.1 seconds +const uint32 MAXIMUM_RTT = 3000; // 3 seconds + +// When we don't have any RTT data, we have to pick something reasonable. We +// use a large value just in case the connection is really slow. +const uint32 DEFAULT_RTT = MAXIMUM_RTT; + +// Computes our estimate of the RTT given the current estimate and the number +// of data points on which it is based. +inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) { + if (rtt_data_points == 0) + return DEFAULT_RTT; + else + return cricket::_max(MINIMUM_RTT, cricket::_min(MAXIMUM_RTT, 2 * rtt)); +} + +// Weighting of the old rtt value to new data. +const int RTT_RATIO = 3; // 3 : 1 + +// The delay before we begin checking if this port is useless. +const int kPortTimeoutDelay = 30 * 1000; // 30 seconds + +const uint32 MSG_CHECKTIMEOUT = 1; +const uint32 MSG_DELETE = 1; + +} + +namespace cricket { + +static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" }; + +const char * ProtoToString(ProtocolType proto) { + return PROTO_NAMES[proto]; +} + +bool StringToProto(const char * value, ProtocolType& proto) { + for (size_t i=0; i<=PROTO_LAST; ++i) { + if (strcmp(PROTO_NAMES[i], value) == 0) { + proto = static_cast<ProtocolType>(i); + return true; + } + } + return false; +} + +ProxyInfo Port::proxy_; + +Port::Port(Thread* thread, const std::string& type, SocketFactory* factory, + Network* network) + : thread_(thread), factory_(factory), type_(type), network_(network), + preference_(-1), lifetime_(LT_PRESTART) { + + if (factory_ == NULL) + factory_ = thread_->socketserver(); + + set_username_fragment(CreateRandomString(16)); + set_password(CreateRandomString(16)); +} + +Port::~Port() { + // Delete all of the remaining connections. We copy the list up front + // because each deletion will cause it to be modified. + + std::vector<Connection*> list; + + AddressMap::iterator iter = connections_.begin(); + while (iter != connections_.end()) { + list.push_back(iter->second); + ++iter; + } + + for (uint32 i = 0; i < list.size(); i++) + delete list[i]; +} + +Connection* Port::GetConnection(const SocketAddress& remote_addr) { + AddressMap::const_iterator iter = connections_.find(remote_addr); + if (iter != connections_.end()) + return iter->second; + else + return NULL; +} + +void Port::set_username_fragment(const std::string& username_fragment) { + username_frag_ = username_fragment; +} + +void Port::set_password(const std::string& password) { + password_ = password; +} + +void Port::add_address(const SocketAddress& address, const std::string& protocol, bool final) { + Candidate c; + c.set_name(name_); + c.set_type(type_); + c.set_protocol(protocol); + c.set_address(address); + c.set_preference(preference_); + c.set_username(username_frag_); + c.set_password(password_); + c.set_network_name(network_->name()); + c.set_generation(generation_); + candidates_.push_back(c); + + if (final) + SignalAddressReady(this); +} + +void Port::AddConnection(Connection* conn) { + connections_[conn->remote_candidate().address()] = conn; + conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); + SignalConnectionCreated(this, conn); +} + +void Port::OnReadPacket( + const char* data, size_t size, const SocketAddress& addr) { + + // If this is an authenticated STUN request, then signal unknown address and + // send back a proper binding response. + StunMessage* msg; + std::string remote_username; + if (!GetStunMessage(data, size, addr, msg, remote_username)) { + LOG(LERROR) << "Received non-STUN packet from unknown address: " + << addr.ToString(); + } else if (!msg) { + // STUN message handled already + } else if (msg->type() == STUN_BINDING_REQUEST) { + SignalUnknownAddress(this, addr, msg, remote_username); + } else { + LOG(LERROR) << "Received unexpected STUN message type (" << msg->type() + << ") from unknown address: " << addr.ToString(); + delete msg; + } +} + +void Port::SendBindingRequest(Connection* conn) { + + // Construct the request message. + + StunMessage request; + request.SetType(STUN_BINDING_REQUEST); + request.SetTransactionID(CreateRandomString(16)); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + std::string username = conn->remote_candidate().username(); + username.append(username_frag_); + username_attr->CopyBytes(username.c_str(), (uint16)username.size()); + request.AddAttribute(username_attr); + + // Send the request message. + // NOTE: If we wanted to, this is where we would add the HMAC. + ByteBuffer buf; + request.Write(&buf); + SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false); +} + +bool Port::GetStunMessage(const char* data, size_t size, + const SocketAddress& addr, StunMessage *& msg, + std::string& remote_username) { + // NOTE: This could clearly be optimized to avoid allocating any memory. + // However, at the data rates we'll be looking at on the client side, + // this probably isn't worth worrying about. + + msg = 0; + + // Parse the request message. If the packet is not a complete and correct + // STUN message, then ignore it. + buzz::scoped_ptr<StunMessage> stun_msg(new StunMessage()); + ByteBuffer buf(data, size); + if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { + return false; + } + + // The packet must include a username that either begins or ends with our + // fragment. It should begin with our fragment if it is a request and it + // should end with our fragment if it is a response. + const StunByteStringAttribute* username_attr = + stun_msg->GetByteString(STUN_ATTR_USERNAME); + + int remote_frag_len = (username_attr ? username_attr->length() : 0); + remote_frag_len -= static_cast<int>(username_frag_.size()); + + if (stun_msg->type() == STUN_BINDING_REQUEST) { + if ((remote_frag_len < 0) + || (std::memcmp(username_attr->bytes(), + username_frag_.c_str(), username_frag_.size()) != 0)) { + LOG(LERROR) << "Received STUN request with bad username"; + SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, + STUN_ERROR_REASON_BAD_REQUEST); + return true; + } + + remote_username.assign(username_attr->bytes() + username_frag_.size(), + username_attr->bytes() + username_attr->length()); + } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) + || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { + if ((remote_frag_len < 0) + || (std::memcmp(username_attr->bytes() + remote_frag_len, + username_frag_.c_str(), username_frag_.size()) != 0)) { + LOG(LERROR) << "Received STUN response with bad username"; + // Do not send error response to a response + return true; + } + + remote_username.assign(username_attr->bytes(), + username_attr->bytes() + remote_frag_len); + + if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) { + if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) { + LOG(LERROR) << "Received STUN binding error:" + << " class=" << error_code->error_class() + << " number=" << error_code->number() + << " reason='" << error_code->reason() << "'"; + // Return message to allow error-specific processing + } else { + LOG(LERROR) << "Received STUN error response with no error code"; + // Drop corrupt message + return true; + } + } + } else { + LOG(LERROR) << "Received STUN packet with invalid type: " + << stun_msg->type(); + return true; + } + + // Return the STUN message found. + msg = stun_msg.release(); + return true; +} + +void Port::SendBindingResponse( + StunMessage* request, const SocketAddress& addr) { + + assert(request->type() == STUN_BINDING_REQUEST); + + // Retrieve the username from the request. + const StunByteStringAttribute* username_attr = + request->GetByteString(STUN_ATTR_USERNAME); + assert(username_attr); + + // Fill in the response message. + + StunMessage response; + response.SetType(STUN_BINDING_RESPONSE); + response.SetTransactionID(request->transaction_id()); + + StunByteStringAttribute* username2_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); + response.AddAttribute(username2_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetPort(addr.port()); + addr_attr->SetIP(addr.ip()); + response.AddAttribute(addr_attr); + + // Send the response message. + // NOTE: If we wanted to, this is where we would add the HMAC. + ByteBuffer buf; + response.Write(&buf); + SendTo(buf.Data(), buf.Length(), addr, false); + + // The fact that we received a successful request means that this connection + // (if one exists) should now be readable. + Connection* conn = GetConnection(addr); + assert(conn); + if (conn) + conn->ReceivedPing(); +} + +void Port::SendBindingErrorResponse( + StunMessage* request, const SocketAddress& addr, int error_code, + const std::string& reason) { + + assert(request->type() == STUN_BINDING_REQUEST); + + // Retrieve the username from the request. If it didn't have one, we + // shouldn't be responding at all. + const StunByteStringAttribute* username_attr = + request->GetByteString(STUN_ATTR_USERNAME); + assert(username_attr); + + // Fill in the response message. + + StunMessage response; + response.SetType(STUN_BINDING_ERROR_RESPONSE); + response.SetTransactionID(request->transaction_id()); + + StunByteStringAttribute* username2_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); + response.AddAttribute(username2_attr); + + StunErrorCodeAttribute* error_attr = StunAttribute::CreateErrorCode(); + error_attr->SetErrorCode(error_code); + error_attr->SetReason(reason); + response.AddAttribute(error_attr); + + // Send the response message. + // NOTE: If we wanted to, this is where we would add the HMAC. + ByteBuffer buf; + response.Write(&buf); + SendTo(buf.Data(), buf.Length(), addr, false); +} + +AsyncPacketSocket * Port::CreatePacketSocket(ProtocolType proto) { + if (proto == PROTO_UDP) { + return new AsyncUDPSocket(factory_->CreateAsyncSocket(SOCK_DGRAM)); + } else if ((proto == PROTO_TCP) || (proto == PROTO_SSLTCP)) { + AsyncSocket * socket = factory_->CreateAsyncSocket(SOCK_STREAM); + switch (proxy().type) { + case PROXY_NONE: + break; + case PROXY_SOCKS5: + socket = new AsyncSocksProxySocket(socket, proxy().address, proxy().username, proxy().password); + break; + case PROXY_HTTPS: + default: + socket = new AsyncHttpsProxySocket(socket, proxy().address, proxy().username, proxy().password); + break; + } + if (proto == PROTO_SSLTCP) { + socket = new AsyncSSLSocket(socket); + } + return new AsyncTCPSocket(socket); + } else { + LOG(INFO) << "Unknown protocol: " << proto; + return 0; + } +} + +void Port::OnMessage(Message *pmsg) { + assert(pmsg->message_id == MSG_CHECKTIMEOUT); + assert(lifetime_ == LT_PRETIMEOUT); + lifetime_ = LT_POSTTIMEOUT; + CheckTimeout(); +} + +void Port::Start() { + // The port sticks around for a minimum lifetime, after which + // we destroy it when it drops to zero connections. + if (lifetime_ == LT_PRESTART) { + lifetime_ = LT_PRETIMEOUT; + thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT); + } else { + LOG(WARNING) << "Port restart attempted"; + } +} + +void Port::OnConnectionDestroyed(Connection* conn) { + AddressMap::iterator iter = connections_.find(conn->remote_candidate().address()); + assert(iter != connections_.end()); + connections_.erase(iter); + + CheckTimeout(); +} + +void Port::CheckTimeout() { + // If this port has no connections, then there's no reason to keep it around. + // When the connections time out (both read and write), they will delete + // themselves, so if we have any connections, they are either readable or + // writable (or still connecting). + if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) { + LOG(INFO) << "Destroying port: " << name_ << "-" << type_; + SignalDestroyed(this); + delete this; + } +} + +// A ConnectionRequest is a simple STUN ping used to determine writability. +class ConnectionRequest : public StunRequest { +public: + ConnectionRequest(Connection* connection) : connection_(connection) { + } + + virtual ~ConnectionRequest() { + } + + virtual void Prepare(StunMessage* request) { + request->SetType(STUN_BINDING_REQUEST); + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + std::string username = connection_->remote_candidate().username(); + username.append(connection_->port()->username_fragment()); + username_attr->CopyBytes(username.c_str(), (uint16)username.size()); + request->AddAttribute(username_attr); + } + + virtual void OnResponse(StunMessage* response) { + connection_->OnConnectionRequestResponse(response, Elapsed()); + } + + virtual void OnErrorResponse(StunMessage* response) { + connection_->OnConnectionRequestErrorResponse(response, Elapsed()); + } + + virtual void OnTimeout() { + } + + virtual int GetNextDelay() { + // Each request is sent only once. After a single delay , the request will + // time out. + timeout_ = true; + return CONNECTION_RESPONSE_TIMEOUT; + } + +private: + Connection* connection_; +}; + +// +// Connection +// + +Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate) + : requests_(port->thread()), port_(port), local_candidate_index_(index), + remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT), + write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false), + rtt_(0), rtt_data_points_(0), last_ping_sent_(0), last_ping_received_(0), + recv_total_bytes_(0), recv_bytes_second_(0), + last_recv_bytes_second_time_((uint32)-1), last_recv_bytes_second_calc_(0), + sent_total_bytes_(0), sent_bytes_second_(0), + last_sent_bytes_second_time_((uint32)-1), last_sent_bytes_second_calc_(0) { + + // Wire up to send stun packets + requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket); +} + +Connection::~Connection() { +} + +const Candidate& Connection::local_candidate() const { + if (local_candidate_index_ < port_->candidates().size()) + return port_->candidates()[local_candidate_index_]; + assert(false); + static Candidate foo; + return foo; +} + +void Connection::set_read_state(ReadState value) { + ReadState old_value = read_state_; + read_state_ = value; + if (value != old_value) { + SignalStateChange(this); + CheckTimeout(); + } +} + +void Connection::set_write_state(WriteState value) { + WriteState old_value = write_state_; + write_state_ = value; + if (value != old_value) { + SignalStateChange(this); + CheckTimeout(); + } +} + +void Connection::set_connected(bool value) { + bool old_value = connected_; + connected_ = value; + + // When connectedness is turned off, this connection is done. + if (old_value && !value) + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::OnSendStunPacket(const void* data, size_t size) { + port_->SendTo(data, size, remote_candidate_.address(), false); +} + +void Connection::OnReadPacket(const char* data, size_t size) { + StunMessage* msg; + std::string remote_username; + const SocketAddress& addr(remote_candidate_.address()); + if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) { + // The packet did not parse as a valid STUN message + + // If this connection is readable, then pass along the packet. + if (read_state_ == STATE_READABLE) { + // readable means data from this address is acceptable + // Send it on! + + recv_total_bytes_ += size; + SignalReadPacket(this, data, size); + + // If timed out sending writability checks, start up again + if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) + set_write_state(STATE_WRITE_CONNECT); + } else { + // Not readable means the remote address hasn't send a valid + // binding request yet. + + LOG(WARNING) << "Received non-STUN packet from an unreadable connection."; + } + } else if (!msg) { + // The packet was STUN, but was already handled + } else if (remote_username != remote_candidate_.username()) { + // Not destined this connection + LOG(LERROR) << "Received STUN packet on wrong address."; + if (msg->type() == STUN_BINDING_REQUEST) { + port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST, + STUN_ERROR_REASON_BAD_REQUEST); + } + delete msg; + } else { + // The packet is STUN, with the current username + // If this is a STUN request, then update the readable bit and respond. + // If this is a STUN response, then update the writable bit. + + switch (msg->type()) { + case STUN_BINDING_REQUEST: + // Incoming, validated stun request from remote peer. + // This call will also set the connection readable. + + port_->SendBindingResponse(msg, addr); + + // If timed out sending writability checks, start up again + if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) + set_write_state(STATE_WRITE_CONNECT); + break; + + case STUN_BINDING_RESPONSE: + case STUN_BINDING_ERROR_RESPONSE: + // Response from remote peer. Does it match request sent? + // This doesn't just check, it makes callbacks if transaction + // id's match + requests_.CheckResponse(msg); + break; + + default: + assert(false); + break; + } + + // Done with the message; delete + + delete msg; + } +} + +void Connection::Prune() { + pruned_ = true; + requests_.Clear(); + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::Destroy() { + set_read_state(STATE_READ_TIMEOUT); + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::UpdateState(uint32 now) { + // Check the readable state. + // + // Since we don't know how many pings the other side has attempted, the best + // test we can do is a simple window. + + if ((read_state_ == STATE_READABLE) && + (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) { + set_read_state(STATE_READ_TIMEOUT); + } + + // Check the writable state. (The order of these checks is important.) + // + // Before becoming unwritable, we allow for a fixed number of pings to fail + // (i.e., receive no response). We also have to give the response time to + // get back, so we include a conservative estimate of this. + // + // Before timing out writability, we give a fixed amount of time. This is to + // allow for changes in network conditions. + + uint32 rtt = ConservativeRTTEstimate(rtt_, rtt_data_points_); + + if ((write_state_ == STATE_WRITABLE) && + TooManyFailures(pings_since_last_response_, + CONNECTION_WRITE_CONNECT_FAILURES, + rtt, + now) && + TooLongWithoutResponse(pings_since_last_response_, + CONNECTION_WRITE_CONNECT_TIMEOUT, + now)) { + set_write_state(STATE_WRITE_CONNECT); + } + + if ((write_state_ == STATE_WRITE_CONNECT) && + TooLongWithoutResponse(pings_since_last_response_, + CONNECTION_WRITE_TIMEOUT, + now)) { + set_write_state(STATE_WRITE_TIMEOUT); + } +} + +void Connection::Ping(uint32 now) { + assert(connected_); + last_ping_sent_ = now; + pings_since_last_response_.push_back(now); + requests_.Send(new ConnectionRequest(this)); +} + +void Connection::ReceivedPing() { + last_ping_received_ = Time(); + set_read_state(STATE_READABLE); +} + +void Connection::OnConnectionRequestResponse(StunMessage *response, uint32 rtt) { + // We have a potentially valid reply from the remote address. + // The packet must include a username that ends with our fragment, + // since it is a response. + + // Check exact message type + bool valid = true; + if (response->type() != STUN_BINDING_RESPONSE) + valid = false; + + // Must have username attribute + const StunByteStringAttribute* username_attr = + response->GetByteString(STUN_ATTR_USERNAME); + if (valid) { + if (!username_attr) { + LOG(LERROR) << "Received likely STUN packet with no username"; + valid = false; + } + } + + // Length must be at least the size of our fragment (actually, should + // be bigger since our fragment is at the end!) + if (valid) { + if (username_attr->length() <= port_->username_fragment().size()) { + LOG(LERROR) << "Received likely STUN packet with short username"; + valid = false; + } + } + + // Compare our fragment with the end of the username - must be exact match + if (valid) { + std::string username_fragment = port_->username_fragment(); + int offset = (int)(username_attr->length() - username_fragment.size()); + if (std::memcmp(username_attr->bytes() + offset, + username_fragment.c_str(), username_fragment.size()) != 0) { + LOG(LERROR) << "Received STUN response with bad username"; + valid = false; + } + } + + if (valid) { + // Valid response. If we're not already, become writable. We may be + // bringing a pruned connection back to life, but if we don't really want + // it, we can always prune it again. + set_write_state(STATE_WRITABLE); + + pings_since_last_response_.clear(); + rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); + rtt_data_points_ += 1; + } +} + +void Connection::OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt) { + const StunErrorCodeAttribute* error = response->GetErrorCode(); + uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE; + + if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE) + || (error_code == STUN_ERROR_SERVER_ERROR) + || (error_code == STUN_ERROR_UNAUTHORIZED)) { + // Recoverable error, retry + } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) { + // Race failure, retry + } else { + // This is not a valid connection. + set_connected(false); + } +} + +void Connection::CheckTimeout() { + // If both read and write have timed out, then this connection can contribute + // no more to p2p socket unless at some later date readability were to come + // back. However, we gave readability a long time to timeout, so at this + // point, it seems fair to get rid of this connectoin. + if ((read_state_ == STATE_READ_TIMEOUT) && + (write_state_ == STATE_WRITE_TIMEOUT)) { + port_->thread()->Post(this, MSG_DELETE); + } +} + +void Connection::OnMessage(Message *pmsg) { + assert(pmsg->message_id == MSG_DELETE); + + LOG(INFO) << "Destroying connection: from " + << local_candidate().address().ToString() + << " to " << remote_candidate_.address().ToString(); + + SignalDestroyed(this); + delete this; +} + +size_t Connection::recv_bytes_second() { + // Snapshot bytes / second calculator + + uint32 current_time = Time(); + if (last_recv_bytes_second_time_ != (uint32)-1) { + int delta = TimeDiff(current_time, last_recv_bytes_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds_time = delta - fraction_time; + int fraction_bytes = (int)(recv_total_bytes_ - last_recv_bytes_second_calc_) * fraction_time / delta; + recv_bytes_second_ = (recv_total_bytes_ - last_recv_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; + last_recv_bytes_second_time_ = current_time - fraction_time; + last_recv_bytes_second_calc_ = recv_total_bytes_ - fraction_bytes; + } + } + if (last_recv_bytes_second_time_ == (uint32)-1) { + last_recv_bytes_second_time_ = current_time; + last_recv_bytes_second_calc_ = recv_total_bytes_; + } + + return recv_bytes_second_; +} + +size_t Connection::recv_total_bytes() { + return recv_total_bytes_; +} + +size_t Connection::sent_bytes_second() { + // Snapshot bytes / second calculator + + uint32 current_time = Time(); + if (last_sent_bytes_second_time_ != (uint32)-1) { + int delta = TimeDiff(current_time, last_sent_bytes_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds_time = delta - fraction_time; + int fraction_bytes = (int)(sent_total_bytes_ - last_sent_bytes_second_calc_) * fraction_time / delta; + sent_bytes_second_ = (sent_total_bytes_ - last_sent_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; + last_sent_bytes_second_time_ = current_time - fraction_time; + last_sent_bytes_second_calc_ = sent_total_bytes_ - fraction_bytes; + } + } + if (last_sent_bytes_second_time_ == (uint32)-1) { + last_sent_bytes_second_time_ = current_time; + last_sent_bytes_second_calc_ = sent_total_bytes_; + } + + return sent_bytes_second_; +} + +size_t Connection::sent_total_bytes() { + return sent_total_bytes_; +} + +ProxyConnection::ProxyConnection(Port* port, size_t index, const Candidate& candidate) + : Connection(port, index, candidate), error_(0) { +} + +int ProxyConnection::Send(const void* data, size_t size) { + if (write_state() != STATE_WRITABLE) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = port_->SendTo(data, size, remote_candidate_.address(), true); + if (sent <= 0) { + assert(sent < 0); + error_ = port_->GetError(); + } else { + sent_total_bytes_ += sent; + } + return sent; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h new file mode 100644 index 00000000..c22fad28 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h @@ -0,0 +1,367 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PORT_H__ +#define __PORT_H__ + +#include "talk/base/network.h" +#include "talk/base/socketaddress.h" +#include "talk/base/proxyinfo.h" +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/stun.h" +#include "talk/p2p/base/stunrequest.h" + +#include <string> +#include <vector> +#include <map> + +namespace cricket { + +class Connection; +class AsyncPacketSocket; + +enum ProtocolType { PROTO_UDP, PROTO_TCP, PROTO_SSLTCP, PROTO_LAST = PROTO_SSLTCP }; +const char * ProtoToString(ProtocolType proto); +bool StringToProto(const char * value, ProtocolType& proto); + +struct ProtocolAddress { + SocketAddress address; + ProtocolType proto; + + ProtocolAddress(const SocketAddress& a, ProtocolType p) : address(a), proto(p) { } +}; + +// Represents a local communication mechanism that can be used to create +// connections to similar mechanisms of the other client. Subclasses of this +// one add support for specific mechanisms like local UDP ports. +class Port: public MessageHandler, public sigslot::has_slots<> { +public: + Port(Thread* thread, const std::string &type, SocketFactory* factory, + Network* network); + virtual ~Port(); + + // The thread on which this port performs its I/O. + Thread* thread() { return thread_; } + + // The factory used to create the sockets of this port. + SocketFactory* socket_factory() const { return factory_; } + void set_socket_factory(SocketFactory* factory) { factory_ = factory; } + + // Each port is identified by a name (for debugging purposes). + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + // In order to establish a connection to this Port (so that real data can be + // sent through), the other side must send us a STUN binding request that is + // authenticated with this username and password. + const std::string& username_fragment() const { return username_frag_; } + const std::string& password() const { return password_; } + + // A value in [0,1] that indicates the preference for this port versus other + // ports on this client. (Larger indicates more preference.) + float preference() const { return preference_; } + void set_preference(float preference) { preference_ = preference; } + + // Identifies the port type. + //const std::string& protocol() const { return proto_; } + const std::string& type() const { return type_; } + + // Identifies network that this port was allocated on. + Network* network() { return network_; } + + // Identifies the generation that this port was created in. + uint32 generation() { return generation_; } + void set_generation(uint32 generation) { generation_ = generation; } + + // PrepareAddress will attempt to get an address for this port that other + // clients can send to. It may take some time before the address is read. + // Once it is ready, we will send SignalAddressReady. + virtual void PrepareAddress() = 0; + sigslot::signal1<Port*> SignalAddressReady; + //const SocketAddress& address() const { return address_; } + + // Provides all of the above information in one handy object. + const std::vector<Candidate>& candidates() const { return candidates_; } + + // Returns a map containing all of the connections of this port, keyed by the + // remote address. + typedef std::map<SocketAddress, Connection*> AddressMap; + const AddressMap& connections() { return connections_; } + + // Returns the connection to the given address or NULL if none exists. + Connection* GetConnection(const SocketAddress& remote_addr); + + // Creates a new connection to the given address. + enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE }; + virtual Connection* CreateConnection(const Candidate& remote_candidate, CandidateOrigin origin) = 0; + + // Called each time a connection is created. + sigslot::signal2<Port*, Connection*> SignalConnectionCreated; + + // Sends the given packet to the given address, provided that the address is + // that of a connection or an address that has sent to us already. + virtual int SendTo( + const void* data, size_t size, const SocketAddress& addr, bool payload) = 0; + + // Indicates that we received a successful STUN binding request from an + // address that doesn't correspond to any current connection. To turn this + // into a real connection, call CreateConnection. + sigslot::signal4<Port*, const SocketAddress&, StunMessage*, const std::string&> SignalUnknownAddress; + + // Sends a response message (normal or error) to the given request. One of + // these methods should be called as a response to SignalUnknownAddress. + // NOTE: You MUST call CreateConnection BEFORE SendBindingResponse. + void SendBindingResponse(StunMessage* request, const SocketAddress& addr); + void SendBindingErrorResponse( + StunMessage* request, const SocketAddress& addr, int error_code, + const std::string& reason); + + // Indicates that errors occurred when performing I/O. + sigslot::signal2<Port*, int> SignalReadError; + sigslot::signal2<Port*, int> SignalWriteError; + + // Functions on the underlying socket(s). + virtual int SetOption(Socket::Option opt, int value) = 0; + virtual int GetError() = 0; + + static void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; } + static const ProxyInfo& proxy() { return proxy_; } + + AsyncPacketSocket * CreatePacketSocket(ProtocolType proto); + + virtual void OnMessage(Message *pmsg); + + // Indicates to the port that its official use has now begun. This will + // start the timer that checks to see if the port is being used. + void Start(); + + // Signaled when this port decides to delete itself because it no longer has + // any usefulness. + sigslot::signal1<Port*> SignalDestroyed; + +protected: + Thread* thread_; + SocketFactory* factory_; + std::string type_; + Network* network_; + uint32 generation_; + std::string name_; + std::string username_frag_; + std::string password_; + float preference_; + std::vector<Candidate> candidates_; + AddressMap connections_; + enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_; + + // Fills in the username fragment and password. These will be initially set + // in the constructor to random values. Subclasses can override, though. + void set_username_fragment(const std::string& username_fragment); + void set_password(const std::string& password); + + // Fills in the local address of the port. + void add_address(const SocketAddress& address, const std::string& protocol, bool final = true); + + // Adds the given connection to the list. (Deleting removes them.) + void AddConnection(Connection* conn); + + // Called when a packet is received from an unknown address that is not + // currently a connection. If this is an authenticated STUN binding request, + // then we will signal the client. + void OnReadPacket(const char* data, size_t size, const SocketAddress& addr); + + // Constructs a STUN binding request for the given connection and sends it. + void SendBindingRequest(Connection* conn); + + // If the given data comprises a complete and correct STUN message then the + // return value is true, otherwise false. If the message username corresponds + // with this port's username fragment, msg will contain the parsed STUN + // message. Otherwise, the function may send a STUN response internally. + // remote_username contains the remote fragment of the STUN username. + bool GetStunMessage(const char* data, size_t size, const SocketAddress& addr, + StunMessage *& msg, std::string& remote_username); + + friend class Connection; + +private: + // Called when one of our connections deletes itself. + void OnConnectionDestroyed(Connection* conn); + + // Checks if this port is useless, and hence, should be destroyed. + void CheckTimeout(); + + static ProxyInfo proxy_; +}; + +// Represents a communication link between a port on the local client and a +// port on the remote client. +class Connection : public MessageHandler, public sigslot::has_slots<> { +public: + virtual ~Connection(); + + // The local port where this connection sends and receives packets. + Port* port() { return port_; } + const Port* port() const { return port_; } + + // Returns the description of the local port + virtual const Candidate& local_candidate() const; + + // Returns the description of the remote port to which we communicate. + const Candidate& remote_candidate() const { return remote_candidate_; } + + enum ReadState { + STATE_READABLE = 0, // we have received pings recently + STATE_READ_TIMEOUT = 1 // we haven't received pings in a while + }; + + ReadState read_state() const { return read_state_; } + + enum WriteState { + STATE_WRITABLE = 0, // we have received ping responses recently + STATE_WRITE_CONNECT = 1, // we have had a few ping failures + STATE_WRITE_TIMEOUT = 2 // we have had a large number of ping failures + }; + + WriteState write_state() const { return write_state_; } + + // Determines whether the connection has finished connecting. This can only + // be false for TCP connections. + bool connected() const { return connected_; } + + // Estimate of the round-trip time over this connection. + uint32 rtt() const { return rtt_; } + + size_t sent_total_bytes(); + size_t sent_bytes_second(); + size_t recv_total_bytes(); + size_t recv_bytes_second(); + sigslot::signal1<Connection*> SignalStateChange; + + // Sent when the connection has decided that it is no longer of value. It + // will delete itself immediately after this call. + sigslot::signal1<Connection*> SignalDestroyed; + + // The connection can send and receive packets asynchronously. This matches + // the interface of AsyncPacketSocket, which may use UDP or TCP under the covers. + virtual int Send(const void* data, size_t size) = 0; + + // Error if Send() returns < 0 + virtual int GetError() = 0; + + sigslot::signal3<Connection*, const char*, size_t> SignalReadPacket; + + // Called when a packet is received on this connection. + void OnReadPacket(const char* data, size_t size); + + // Called when a connection is determined to be no longer useful to us. We + // still keep it around in case the other side wants to use it. But we can + // safely stop pinging on it and we can allow it to time out if the other + // side stops using it as well. + bool pruned() { return pruned_; } + void Prune(); + + // Makes the connection go away. + void Destroy(); + + // Checks that the state of this connection is up-to-date. The argument is + // the current time, which is compared against various timeouts. + void UpdateState(uint32 now); + + // Called when this connection should try checking writability again. + uint32 last_ping_sent() { return last_ping_sent_; } + void Ping(uint32 now); + + // Called whenever a valid ping is received on this connection. This is + // public because the connection intercepts the first ping for us. + void ReceivedPing(); + +protected: + Port* port_; + size_t local_candidate_index_; + Candidate remote_candidate_; + ReadState read_state_; + WriteState write_state_; + bool connected_; + bool pruned_; + StunRequestManager requests_; + uint32 rtt_; + uint32 rtt_data_points_; + uint32 last_ping_sent_; // last time we sent a ping to the other side + uint32 last_ping_received_; // last time we received a ping from the other side + std::vector<uint32> pings_since_last_response_; + + size_t recv_total_bytes_; + size_t recv_bytes_second_; + uint32 last_recv_bytes_second_time_; + size_t last_recv_bytes_second_calc_; + + size_t sent_total_bytes_; + size_t sent_bytes_second_; + uint32 last_sent_bytes_second_time_; + size_t last_sent_bytes_second_calc_; + + // Callbacks from ConnectionRequest + void OnConnectionRequestResponse(StunMessage *response, uint32 rtt); + void OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt); + + // Called back when StunRequestManager has a stun packet to send + void OnSendStunPacket(const void* data, size_t size); + + // Constructs a new connection to the given remote port. + Connection(Port* port, size_t index, const Candidate& candidate); + + // Changes the state and signals if necessary. + void set_read_state(ReadState value); + void set_write_state(WriteState value); + void set_connected(bool value); + + // Checks if this connection is useless, and hence, should be destroyed. + void CheckTimeout(); + + void OnMessage(Message *pmsg); + + friend class Port; + friend class ConnectionRequest; +}; + +// ProxyConnection defers all the interesting work to the port + +class ProxyConnection : public Connection { +public: + ProxyConnection(Port* port, size_t index, const Candidate& candidate); + + virtual int Send(const void* data, size_t size); + virtual int GetError() { return error_; } + +private: + int error_; +}; + +} // namespace cricket + +#endif // __PORT_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h new file mode 100644 index 00000000..3246f29f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h @@ -0,0 +1,91 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PORTALLOCATOR_H_ +#define _PORTALLOCATOR_H_ + +#include "talk/base/sigslot.h" +#include "talk/p2p/base/port.h" +#include <string> +#undef SetPort + +namespace cricket { + +// PortAllocator is responsible for allocating Port types for a given +// P2PSocket. It also handles port freeing. +// +// Clients can override this class to control port allocation, including +// what kinds of ports are allocated. + +class PortAllocatorSession : public sigslot::has_slots<> { +public: + // Prepares an initial set of ports to try. + virtual void GetInitialPorts() = 0; + + // Starts and stops the flow of additional ports to try. + virtual void StartGetAllPorts() = 0; + virtual void StopGetAllPorts() = 0; + virtual bool IsGettingAllPorts() = 0; + + sigslot::signal2<PortAllocatorSession*, Port*> SignalPortReady; + sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&> SignalCandidatesReady; + + uint32 generation() { return generation_; } + void set_generation(uint32 generation) { generation_ = generation; } + +private: + uint32 generation_; +}; + +const uint32 PORTALLOCATOR_DISABLE_UDP = 0x01; +const uint32 PORTALLOCATOR_DISABLE_STUN = 0x02; +const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04; +const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08; +const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10; + +const uint32 kDefaultPortAllocatorFlags = 0; + +class PortAllocator { +public: + PortAllocator() : flags_(kDefaultPortAllocatorFlags) {} + + virtual PortAllocatorSession *CreateSession(const std::string &name) = 0; + + uint32 flags() const { return flags_; } + void set_flags(uint32 flags) { flags_ = flags; } + + const ProxyInfo& proxy() const { return proxy_; } + void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; } + +protected: + uint32 flags_; + ProxyInfo proxy_; +}; + +} // namespace cricket + +#endif // _PORTALLOCATOR_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc new file mode 100644 index 00000000..4ba12be3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc @@ -0,0 +1,640 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/base/asynctcpsocket.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/helpers.h" +#include <iostream> +#include <cassert> +#ifdef OSX +#include <errno.h> +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +const int KEEPALIVE_DELAY = 10 * 60 * 1000; +const int RETRY_DELAY = 50; // 50ms, from ICE spec +const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs + +const uint32 MSG_DISPOSE_SOCKET = 100; // needs to be more than ID used by Port +typedef TypedMessageData<AsyncPacketSocket *> DisposeSocketData; + +class AsyncTCPSocket; + +// Manages a single connection to the relayserver. We aim to use each +// connection for only a specific destination address so that we can avoid +// wrapping every packet in a STUN send / data indication. +class RelayEntry : public sigslot::has_slots<> { +public: + RelayEntry(RelayPort* port, const SocketAddress& ext_addr, const SocketAddress& local_addr); + ~RelayEntry(); + + RelayPort* port() { return port_; } + + const SocketAddress& address() { return ext_addr_; } + void set_address(const SocketAddress& addr) { ext_addr_ = addr; } + + AsyncPacketSocket* socket() { return socket_; } + + bool connected() { return connected_; } + void set_connected(bool connected) { connected_ = connected; } + + bool locked() { return locked_; } + + // Returns the last error on the socket of this entry. + int GetError() { return socket_->GetError(); } + + // Sends the STUN requests to the server to initiate this connection. + void Connect(); + + // Called when this entry becomes connected. The address given is the one + // exposed to the outside world on the relay server. + void OnConnect(const SocketAddress& mapped_addr); + + // Sends a packet to the given destination address using the socket of this + // entry. This will wrap the packet in STUN if necessary. + int SendTo(const void* data, size_t size, const SocketAddress& addr); + + // Schedules a keep-alive allocate request. + void ScheduleKeepAlive(); + + void SetServerIndex(size_t sindex) { server_index_ = sindex; } + size_t ServerIndex() const { return server_index_; } + + // Try a different server address + void HandleConnectFailure(); + +private: + RelayPort* port_; + SocketAddress ext_addr_, local_addr_; + size_t server_index_; + AsyncPacketSocket* socket_; + bool connected_; + bool locked_; + StunRequestManager requests_; + + // Called when a TCP connection is established or fails + void OnSocketConnect(AsyncTCPSocket* socket); + void OnSocketClose(AsyncTCPSocket* socket, int error); + + // Called when a packet is received on this socket. + void OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + + // Called on behalf of a StunRequest to write data to the socket. This is + // already STUN intended for the server, so no wrapping is necessary. + void OnSendPacket(const void* data, size_t size); + + // Sends the given data on the socket to the server with no wrapping. This + // returns the number of bytes written or -1 if an error occurred. + int SendPacket(const void* data, size_t size); +}; + +// Handles an allocate request for a particular RelayEntry. +class AllocateRequest : public StunRequest { +public: + AllocateRequest(RelayEntry* entry); + virtual ~AllocateRequest() {} + + virtual void Prepare(StunMessage* request); + + virtual int GetNextDelay(); + + virtual void OnResponse(StunMessage* response); + virtual void OnErrorResponse(StunMessage* response); + virtual void OnTimeout(); + +private: + RelayEntry* entry_; + uint32 start_time_; +}; + +const std::string RELAY_PORT_TYPE("relay"); + +RelayPort::RelayPort( + Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& local_addr, const std::string& username, + const std::string& password, const std::string& magic_cookie) + : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr), + ready_(false), magic_cookie_(magic_cookie), error_(0) { + + entries_.push_back(new RelayEntry(this, SocketAddress(), local_addr_)); + + set_username_fragment(username); + set_password(password); + + if (magic_cookie_.size() == 0) + magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4); +} + +RelayPort::~RelayPort() { + for (unsigned i = 0; i < entries_.size(); ++i) + delete entries_[i]; + thread_->Clear(this); +} + +void RelayPort::AddServerAddress(const ProtocolAddress& addr) { + // Since HTTP proxies usually only allow 443, let's up the priority on PROTO_SSLTCP + if ((addr.proto == PROTO_SSLTCP) + && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) { + server_addr_.push_front(addr); + } else { + server_addr_.push_back(addr); + } +} + +void RelayPort::AddExternalAddress(const ProtocolAddress& addr) { + std::string proto_name = ProtoToString(addr.proto); + for (std::vector<Candidate>::const_iterator it = candidates().begin(); it != candidates().end(); ++it) { + if ((it->address() == addr.address) && (it->protocol() == proto_name)) { + LOG(INFO) << "Redundant relay address: " << proto_name << " @ " << addr.address.ToString(); + return; + } + } + add_address(addr.address, proto_name, false); +} + +void RelayPort::SetReady() { + if (!ready_) { + ready_ = true; + SignalAddressReady(this); + } +} + +const ProtocolAddress * RelayPort::ServerAddress(size_t index) const { + if ((index >= 0) && (index < server_addr_.size())) + return &server_addr_[index]; + return 0; +} + +bool RelayPort::HasMagicCookie(const char* data, size_t size) { + if (size < 24 + magic_cookie_.size()) { + return false; + } else { + return 0 == std::memcmp(data + 24, + magic_cookie_.c_str(), + magic_cookie_.size()); + } +} + +void RelayPort::PrepareAddress() { + // We initiate a connect on the first entry. If this completes, it will fill + // in the server address as the address of this port. + assert(entries_.size() == 1); + entries_[0]->Connect(); + ready_ = false; +} + +Connection* RelayPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + // We only create connections to non-udp sockets if they are incoming on this port + if ((address.protocol() != "udp") && (origin != ORIGIN_THIS_PORT)) + return 0; + + // We don't support loopback on relays + if (address.type() == type()) + return 0; + + size_t index = 0; + for (size_t i = 0; i < candidates().size(); ++i) { + const Candidate& local = candidates()[i]; + if (local.protocol() == address.protocol()) { + index = i; + break; + } + } + + Connection * conn = new ProxyConnection(this, index, address); + AddConnection(conn); + return conn; +} + +int RelayPort::SendTo(const void* data, + size_t size, + const SocketAddress& addr, bool payload) { + + // Try to find an entry for this specific address. Note that the first entry + // created was not given an address initially, so it can be set to the first + // address that comes along. + + RelayEntry* entry = 0; + + for (unsigned i = 0; i < entries_.size(); ++i) { + if (entries_[i]->address().IsAny() && payload) { + entry = entries_[i]; + entry->set_address(addr); + break; + } else if (entries_[i]->address() == addr) { + entry = entries_[i]; + break; + } + } + + // If we did not find one, then we make a new one. This will not be useable + // until it becomes connected, however. + if (!entry && payload) { + entry = new RelayEntry(this, addr, local_addr_); + if (!entries_.empty()) { + // Use the same port to connect to relay server + entry->SetServerIndex(entries_[0]->ServerIndex()); + } + entry->Connect(); + entries_.push_back(entry); + } + + // If the entry is connected, then we can send on it (though wrapping may + // still be necessary). Otherwise, we can't yet use this connection, so we + // default to the first one. + if (!entry || !entry->connected()) { + assert(!entries_.empty()); + entry = entries_[0]; + if (!entry->connected()) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + } + + // Send the actual contents to the server using the usual mechanism. + int sent = entry->SendTo(data, size, addr); + if (sent <= 0) { + assert(sent < 0); + error_ = entry->GetError(); + return SOCKET_ERROR; + } + + // The caller of the function is expecting the number of user data bytes, + // rather than the size of the packet. + return (int)size; +} + +void RelayPort::OnMessage(Message *pmsg) { + switch (pmsg->message_id) { + case MSG_DISPOSE_SOCKET: { + DisposeSocketData * data = static_cast<DisposeSocketData *>(pmsg->pdata); + delete data->data(); + delete data; + break; } + default: + Port::OnMessage(pmsg); + } +} + +int RelayPort::SetOption(Socket::Option opt, int value) { + int result = 0; + for (unsigned i = 0; i < entries_.size(); ++i) { + if (entries_[i]->socket()->SetOption(opt, value) < 0) { + result = -1; + error_ = entries_[i]->socket()->GetError(); + } + } + options_.push_back(OptionValue(opt, value)); + return result; +} + +int RelayPort::GetError() { + return error_; +} + +void RelayPort::OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr) { + if (Connection* conn = GetConnection(remote_addr)) { + conn->OnReadPacket(data, size); + } else { + Port::OnReadPacket(data, size, remote_addr); + } +} + +void RelayPort::DisposeSocket(AsyncPacketSocket * socket) { + thread_->Post(this, MSG_DISPOSE_SOCKET, new DisposeSocketData(socket)); +} + +RelayEntry::RelayEntry(RelayPort* port, const SocketAddress& ext_addr, + const SocketAddress& local_addr) + : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), server_index_(0), + socket_(0), connected_(false), locked_(false), requests_(port->thread()) { + + requests_.SignalSendPacket.connect(this, &RelayEntry::OnSendPacket); +} + +RelayEntry::~RelayEntry() { + delete socket_; +} + +void RelayEntry::Connect() { + assert(socket_ == 0); + const ProtocolAddress * ra = port()->ServerAddress(server_index_); + if (!ra) { + LOG(INFO) << "Out of relay server connections"; + return; + } + + LOG(INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString(); + + socket_ = port_->CreatePacketSocket(ra->proto); + assert(socket_ != 0); + + socket_->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket); + if (socket_->Bind(local_addr_) < 0) + LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); + + for (unsigned i = 0; i < port_->options().size(); ++i) + socket_->SetOption(port_->options()[i].first, port_->options()[i].second); + + if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) { + AsyncTCPSocket * tcp = static_cast<AsyncTCPSocket *>(socket_); + tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose); + tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect); + tcp->Connect(ra->address); + } else { + requests_.Send(new AllocateRequest(this)); + } +} + +void RelayEntry::OnConnect(const SocketAddress& mapped_addr) { + ProtocolType proto = PROTO_UDP; + LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto) << " @ " << mapped_addr.ToString(); + connected_ = true; + + port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto)); + port_->SetReady(); +} + +int RelayEntry::SendTo(const void* data, + size_t size, + const SocketAddress& addr) { + + // If this connection is locked to the address given, then we can send the + // packet with no wrapper. + if (locked_ && (ext_addr_ == addr)) + return SendPacket(data, size); + + // Otherwise, we must wrap the given data in a STUN SEND request so that we + // can communicate the destination address to the server. + // + // Note that we do not use a StunRequest here. This is because there is + // likely no reason to resend this packet. If it is late, we just drop it. + // The next send to this address will try again. + + StunMessage request; + request.SetType(STUN_SEND_REQUEST); + request.SetTransactionID(CreateRandomString(16)); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(), + (uint16)port_->magic_cookie().size()); + request.AddAttribute(magic_cookie_attr); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username_attr->CopyBytes(port_->username_fragment().c_str(), + (uint16)port_->username_fragment().size()); + request.AddAttribute(username_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetIP(addr.ip()); + addr_attr->SetPort(addr.port()); + request.AddAttribute(addr_attr); + + // Attempt to lock + if (ext_addr_ == addr) { + StunUInt32Attribute* options_attr = + StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS); + options_attr->SetValue(0x1); + request.AddAttribute(options_attr); + } + + StunByteStringAttribute* data_attr = + StunAttribute::CreateByteString(STUN_ATTR_DATA); + data_attr->CopyBytes(data, (uint16)size); + request.AddAttribute(data_attr); + + // TODO: compute the HMAC. + + ByteBuffer buf; + request.Write(&buf); + + return SendPacket(buf.Data(), buf.Length()); +} + +void RelayEntry::ScheduleKeepAlive() { + requests_.SendDelayed(new AllocateRequest(this), KEEPALIVE_DELAY); +} + +void RelayEntry::HandleConnectFailure() { + //if (GetMillisecondCount() - start_time_ > RETRY_TIMEOUT) + // return; + //ScheduleKeepAlive(); + + connected_ = false; + port()->DisposeSocket(socket_); + socket_ = 0; + server_index_ += 1; + Connect(); +} + +void RelayEntry::OnSocketConnect(AsyncTCPSocket* socket) { + assert(socket == socket_); + LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString(); + requests_.Send(new AllocateRequest(this)); +} + +void RelayEntry::OnSocketClose(AsyncTCPSocket* socket, int error) { + assert(socket == socket_); + PLOG(LERROR, error) << "relay tcp connect failed"; + HandleConnectFailure(); +} + +void RelayEntry::OnReadPacket(const char* data, + size_t size, + const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + //assert(remote_addr == port_->server_addr()); TODO: are we worried about this? + + // If the magic cookie is not present, then this is an unwrapped packet sent + // by the server, The actual remote address is the one we recorded. + if (!port_->HasMagicCookie(data, size)) { + if (locked_) { + port_->OnReadPacket(data, size, ext_addr_); + } else { + LOG(WARNING) << "Dropping packet: entry not locked"; + } + return; + } + + ByteBuffer buf(data, size); + StunMessage msg; + if (!msg.Read(&buf)) { + LOG(INFO) << "Incoming packet was not STUN"; + return; + } + + // The incoming packet should be a STUN ALLOCATE response, SEND response, or + // DATA indication. + if (requests_.CheckResponse(&msg)) { + return; + } else if (msg.type() == STUN_SEND_RESPONSE) { + if (const StunUInt32Attribute* options_attr = msg.GetUInt32(STUN_ATTR_OPTIONS)) { + if (options_attr->value() & 0x1) { + locked_ = true; + } + } + return; + } else if (msg.type() != STUN_DATA_INDICATION) { + LOG(INFO) << "Received BAD stun type from server: " << msg.type() + ; + return; + } + + // This must be a data indication. + + const StunAddressAttribute* addr_attr = + msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2); + if (!addr_attr) { + LOG(INFO) << "Data indication has no source address"; + return; + } else if (addr_attr->family() != 1) { + LOG(INFO) << "Source address has bad family"; + return; + } + + SocketAddress remote_addr2(addr_attr->ip(), addr_attr->port()); + + const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA); + if (!data_attr) { + LOG(INFO) << "Data indication has no data"; + return; + } + + // Process the actual data and remote address in the normal manner. + port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2); +} + +void RelayEntry::OnSendPacket(const void* data, size_t size) { + SendPacket(data, size); +} + +int RelayEntry::SendPacket(const void* data, size_t size) { + const ProtocolAddress * ra = port_->ServerAddress(server_index_); + if (!ra) { + socket_->SetError(ENOTCONN); + return SOCKET_ERROR; + } + int sent = socket_->SendTo(data, size, ra->address); + if (sent <= 0) { + LOG(LS_VERBOSE) << "sendto: " << std::strerror(socket_->GetError()); + assert(sent < 0); + } + return sent; +} + +AllocateRequest::AllocateRequest(RelayEntry* entry) : entry_(entry) { + start_time_ = GetMillisecondCount(); +} + +void AllocateRequest::Prepare(StunMessage* request) { + request->SetType(STUN_ALLOCATE_REQUEST); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes( + entry_->port()->magic_cookie().c_str(), + (uint16)entry_->port()->magic_cookie().size()); + request->AddAttribute(magic_cookie_attr); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username_attr->CopyBytes( + entry_->port()->username_fragment().c_str(), + (uint16)entry_->port()->username_fragment().size()); + request->AddAttribute(username_attr); +} + +int AllocateRequest::GetNextDelay() { + int delay = 100 * _max(1 << count_, 2); + count_ += 1; + if (count_ == 5) + timeout_ = true; + return delay; +} + +void AllocateRequest::OnResponse(StunMessage* response) { + const StunAddressAttribute* addr_attr = + response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + if (!addr_attr) { + LOG(INFO) << "Allocate response missing mapped address."; + } else if (addr_attr->family() != 1) { + LOG(INFO) << "Mapped address has bad family"; + } else { + SocketAddress addr(addr_attr->ip(), addr_attr->port()); + entry_->OnConnect(addr); + } + + // We will do a keep-alive regardless of whether this request suceeds. + // This should have almost no impact on network usage. + entry_->ScheduleKeepAlive(); +} + +void AllocateRequest::OnErrorResponse(StunMessage* response) { + const StunErrorCodeAttribute* attr = response->GetErrorCode(); + if (!attr) { + LOG(INFO) << "Bad allocate response error code"; + } else { + LOG(INFO) << "Allocate error response:" + << " code=" << static_cast<int>(attr->error_code()) + << " reason='" << attr->reason() << "'"; + } + + if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + entry_->ScheduleKeepAlive(); +} + +void AllocateRequest::OnTimeout() { + LOG(INFO) << "Allocate request timed out"; + entry_->HandleConnectFailure(); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h new file mode 100644 index 00000000..7cfdc015 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h @@ -0,0 +1,93 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __RELAYPORT_H__ +#define __RELAYPORT_H__ + +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/stunrequest.h" +#include <vector> + +namespace cricket { + +extern const std::string RELAY_PORT_TYPE; +class RelayEntry; + +// Communicates using an allocated port on the relay server. +class RelayPort: public Port { +public: + RelayPort( + Thread* thread, SocketFactory* factory, Network*, + const SocketAddress& local_addr, + const std::string& username, const std::string& password, + const std::string& magic_cookie); + virtual ~RelayPort(); + + void AddServerAddress(const ProtocolAddress& addr); + void AddExternalAddress(const ProtocolAddress& addr); + + typedef std::pair<Socket::Option, int> OptionValue; + const std::vector<OptionValue>& options() const { return options_; } + + const std::string& magic_cookie() const { return magic_cookie_; } + bool HasMagicCookie(const char* data, size_t size); + + virtual void PrepareAddress(); + virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin); + + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError(); + + const ProtocolAddress * ServerAddress(size_t index) const; + + void DisposeSocket(AsyncPacketSocket * socket); + +protected: + void SetReady(); + + virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload); + virtual void OnMessage(Message *pmsg); + + // Dispatches the given packet to the port or connection as appropriate. + void OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr); + +private: + friend class RelayEntry; + + SocketAddress local_addr_; + std::deque<ProtocolAddress> server_addr_; + bool ready_; + std::vector<RelayEntry*> entries_; + std::vector<OptionValue> options_; + std::string magic_cookie_; + int error_; +}; + +} // namespace cricket + +#endif // __RELAYPORT_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc new file mode 100644 index 00000000..bb52a1d5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc @@ -0,0 +1,657 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/p2p/base/relayserver.h" +#include "talk/p2p/base/helpers.h" +#include <algorithm> +#include <cassert> +#include <cstring> +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +// By default, we require a ping every 90 seconds. +const int MAX_LIFETIME = 15 * 60 * 1000; + +// The number of bytes in each of the usernames we use. +const uint32 USERNAME_LENGTH = 16; + +// Calls SendTo on the given socket and logs any bad results. +void Send(AsyncPacketSocket* socket, const char* bytes, size_t size, + const SocketAddress& addr) { + int result = socket->SendTo(bytes, size, addr); + if (result < int(size)) { + std::cerr << "SendTo wrote only " << result << " of " << int(size) + << " bytes" << std::endl; + } else if (result < 0) { + std::cerr << "SendTo: " << std::strerror(errno) << std::endl; + } +} + +// Sends the given STUN message on the given socket. +void SendStun(const StunMessage& msg, + AsyncPacketSocket* socket, + const SocketAddress& addr) { + ByteBuffer buf; + msg.Write(&buf); + Send(socket, buf.Data(), buf.Length(), addr); +} + +// Constructs a STUN error response and sends it on the given socket. +void SendStunError(const StunMessage& msg, AsyncPacketSocket* socket, + const SocketAddress& remote_addr, int error_code, + const char* error_desc, const std::string& magic_cookie) { + + StunMessage err_msg; + err_msg.SetType(GetStunErrorResponseType(msg.type())); + err_msg.SetTransactionID(msg.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + if (magic_cookie.size() == 0) + magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4); + else + magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size()); + err_msg.AddAttribute(magic_cookie_attr); + + StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); + err_code->SetErrorClass(error_code / 100); + err_code->SetNumber(error_code % 100); + err_code->SetReason(error_desc); + err_msg.AddAttribute(err_code); + + SendStun(err_msg, socket, remote_addr); +} + +RelayServer::RelayServer(Thread* thread) : thread_(thread) { +} + +RelayServer::~RelayServer() { + for (unsigned i = 0; i < internal_sockets_.size(); i++) + delete internal_sockets_[i]; + for (unsigned i = 0; i < external_sockets_.size(); i++) + delete external_sockets_[i]; +} + +void RelayServer::AddInternalSocket(AsyncPacketSocket* socket) { + assert(internal_sockets_.end() == + std::find(internal_sockets_.begin(), internal_sockets_.end(), socket)); + internal_sockets_.push_back(socket); + socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket); +} + +void RelayServer::RemoveInternalSocket(AsyncPacketSocket* socket) { + SocketList::iterator iter = + std::find(internal_sockets_.begin(), internal_sockets_.end(), socket); + assert(iter != internal_sockets_.end()); + internal_sockets_.erase(iter); + socket->SignalReadPacket.disconnect(this); +} + +void RelayServer::AddExternalSocket(AsyncPacketSocket* socket) { + assert(external_sockets_.end() == + std::find(external_sockets_.begin(), external_sockets_.end(), socket)); + external_sockets_.push_back(socket); + socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket); +} + +void RelayServer::RemoveExternalSocket(AsyncPacketSocket* socket) { + SocketList::iterator iter = + std::find(external_sockets_.begin(), external_sockets_.end(), socket); + assert(iter != external_sockets_.end()); + external_sockets_.erase(iter); + socket->SignalReadPacket.disconnect(this); +} + +void RelayServer::OnInternalPacket( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + // Get the address of the connection we just received on. + SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + assert(!ap.destination().IsAny()); + + // If this did not come from an existing connection, it should be a STUN + // allocate request. + ConnectionMap::iterator piter = connections_.find(ap); + if (piter == connections_.end()) { + HandleStunAllocate(bytes, size, ap, socket); + return; + } + + RelayServerConnection* int_conn = piter->second; + + // Handle STUN requests to the server itself. + if (int_conn->binding()->HasMagicCookie(bytes, size)) { + HandleStun(int_conn, bytes, size); + return; + } + + // Otherwise, this is a non-wrapped packet that we are to forward. Make sure + // that this connection has been locked. (Otherwise, we would not know what + // address to forward to.) + if (!int_conn->locked()) { + std::cerr << "Dropping packet: connection not locked" << std::endl; + return; + } + + // Forward this to the destination address into the connection. + RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection( + int_conn->default_destination()); + if (ext_conn) { + // TODO: Check the HMAC. + ext_conn->Send(bytes, size); + } else { + // This happens very often and is not an error. + //std::cerr << "Dropping packet: no external connection" << std::endl; + } +} + +void RelayServer::OnExternalPacket( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + // Get the address of the connection we just received on. + SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + assert(!ap.destination().IsAny()); + + // If this connection already exists, then forward the traffic. + ConnectionMap::iterator piter = connections_.find(ap); + if (piter != connections_.end()) { + // TODO: Check the HMAC. + RelayServerConnection* ext_conn = piter->second; + RelayServerConnection* int_conn = + ext_conn->binding()->GetInternalConnection( + ext_conn->addr_pair().source()); + assert(int_conn); + int_conn->Send(bytes, size, ext_conn->addr_pair().source()); + return; + } + + // The first packet should always be a STUN / TURN packet. If it isn't, then + // we should just ignore this packet. + StunMessage msg; + ByteBuffer buf = ByteBuffer(bytes, size); + if (!msg.Read(&buf)) { + std::cerr << "Dropping packet: first packet not STUN" << std::endl; + return; + } + + // The initial packet should have a username (which identifies the binding). + const StunByteStringAttribute* username_attr = + msg.GetByteString(STUN_ATTR_USERNAME); + if (!username_attr) { + std::cerr << "Dropping packet: no username" << std::endl; + return; + } + + uint32 length = _min(uint32(username_attr->length()), USERNAME_LENGTH); + std::string username(username_attr->bytes(), length); + // TODO: Check the HMAC. + + // The binding should already be present. + BindingMap::iterator biter = bindings_.find(username); + if (biter == bindings_.end()) { + // TODO: Turn this back on. This is the sign of a client bug. + //std::cerr << "Dropping packet: no binding with username" << std::endl; + return; + } + + // Add this authenticted connection to the binding. + RelayServerConnection* ext_conn = + new RelayServerConnection(biter->second, ap, socket); + ext_conn->binding()->AddExternalConnection(ext_conn); + AddConnection(ext_conn); + + // We always know where external packets should be forwarded, so we can lock + // them from the beginning. + ext_conn->Lock(); + + // Send this message on the appropriate internal connection. + RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection( + ext_conn->addr_pair().source()); + assert(int_conn); + int_conn->Send(bytes, size, ext_conn->addr_pair().source()); +} + +bool RelayServer::HandleStun( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket, std::string* username, StunMessage* msg) { + + // Parse this into a stun message. + ByteBuffer buf = ByteBuffer(bytes, size); + if (!msg->Read(&buf)) { + SendStunError(*msg, socket, remote_addr, 400, "Bad Request", ""); + return false; + } + + // The initial packet should have a username (which identifies the binding). + const StunByteStringAttribute* username_attr = + msg->GetByteString(STUN_ATTR_USERNAME); + if (!username_attr) { + SendStunError(*msg, socket, remote_addr, 432, "Missing Username", ""); + return false; + } + + // Record the username if requested. + if (username) + username->append(username_attr->bytes(), username_attr->length()); + + // TODO: Check for unknown attributes (<= 0x7fff) + + return true; +} + +void RelayServer::HandleStunAllocate( + const char* bytes, size_t size, const SocketAddressPair& ap, + AsyncPacketSocket* socket) { + + // Make sure this is a valid STUN request. + StunMessage request; + std::string username; + if (!HandleStun(bytes, size, ap.source(), socket, &username, &request)) + return; + + // Make sure this is a an allocate request. + if (request.type() != STUN_ALLOCATE_REQUEST) { + SendStunError(request, + socket, + ap.source(), + 600, + "Operation Not Supported", + ""); + return; + } + + // TODO: Check the HMAC. + + // Find or create the binding for this username. + + RelayServerBinding* binding; + + BindingMap::iterator biter = bindings_.find(username); + if (biter != bindings_.end()) { + + binding = biter->second; + + } else { + + // NOTE: In the future, bindings will be created by the bot only. This + // else-branch will then disappear. + + // Compute the appropriate lifetime for this binding. + uint32 lifetime = MAX_LIFETIME; + const StunUInt32Attribute* lifetime_attr = + request.GetUInt32(STUN_ATTR_LIFETIME); + if (lifetime_attr) + lifetime = _min(lifetime, lifetime_attr->value() * 1000); + + binding = new RelayServerBinding(this, username, "0", lifetime); + binding->SignalTimeout.connect(this, &RelayServer::OnTimeout); + bindings_[username] = binding; + + std::cout << "Added new binding: " << bindings_.size() << " total" << std::endl; + } + + // Add this connection to the binding. It starts out unlocked. + RelayServerConnection* int_conn = + new RelayServerConnection(binding, ap, socket); + binding->AddInternalConnection(int_conn); + AddConnection(int_conn); + + // Now that we have a connection, this other method takes over. + HandleStunAllocate(int_conn, request); +} + +void RelayServer::HandleStun( + RelayServerConnection* int_conn, const char* bytes, size_t size) { + + // Make sure this is a valid STUN request. + StunMessage request; + std::string username; + if (!HandleStun(bytes, size, int_conn->addr_pair().source(), + int_conn->socket(), &username, &request)) + return; + + // Make sure the username is the one were were expecting. + if (username != int_conn->binding()->username()) { + int_conn->SendStunError(request, 430, "Stale Credentials"); + return; + } + + // TODO: Check the HMAC. + + // Send this request to the appropriate handler. + if (request.type() == STUN_SEND_REQUEST) + HandleStunSend(int_conn, request); + else if (request.type() == STUN_ALLOCATE_REQUEST) + HandleStunAllocate(int_conn, request); + else + int_conn->SendStunError(request, 600, "Operation Not Supported"); +} + +void RelayServer::HandleStunAllocate( + RelayServerConnection* int_conn, const StunMessage& request) { + + // Create a response message that includes an address with which external + // clients can communicate. + + StunMessage response; + response.SetType(STUN_ALLOCATE_RESPONSE); + response.SetTransactionID(request.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), + int_conn->binding()->magic_cookie().size()); + response.AddAttribute(magic_cookie_attr); + + size_t index = rand() % external_sockets_.size(); + SocketAddress ext_addr = external_sockets_[index]->GetLocalAddress(); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetIP(ext_addr.ip()); + addr_attr->SetPort(ext_addr.port()); + response.AddAttribute(addr_attr); + + StunUInt32Attribute* res_lifetime_attr = + StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME); + res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000); + response.AddAttribute(res_lifetime_attr); + + // TODO: Support transport-prefs (preallocate RTCP port). + // TODO: Support bandwidth restrictions. + // TODO: Add message integrity check. + + // Send a response to the caller. + int_conn->SendStun(response); +} + +void RelayServer::HandleStunSend( + RelayServerConnection* int_conn, const StunMessage& request) { + + const StunAddressAttribute* addr_attr = + request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS); + if (!addr_attr) { + int_conn->SendStunError(request, 400, "Bad Request"); + return; + } + + const StunByteStringAttribute* data_attr = + request.GetByteString(STUN_ATTR_DATA); + if (!data_attr) { + int_conn->SendStunError(request, 400, "Bad Request"); + return; + } + + SocketAddress ext_addr(addr_attr->ip(), addr_attr->port()); + RelayServerConnection* ext_conn = + int_conn->binding()->GetExternalConnection(ext_addr); + if (!ext_conn) { + // This happens very often and is not an error. + //std::cerr << "Dropping packet: no external connection" << std::endl; + return; + } + + ext_conn->Send(data_attr->bytes(), data_attr->length()); + + const StunUInt32Attribute* options_attr = + request.GetUInt32(STUN_ATTR_OPTIONS); + if (options_attr && (options_attr->value() & 0x01 != 0)) { + int_conn->set_default_destination(ext_addr); + int_conn->Lock(); + + StunMessage response; + response.SetType(STUN_SEND_RESPONSE); + response.SetTransactionID(request.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), + int_conn->binding()->magic_cookie().size()); + response.AddAttribute(magic_cookie_attr); + + StunUInt32Attribute* options2_attr = + StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS); + options2_attr->SetValue(0x01); + response.AddAttribute(options2_attr); + + int_conn->SendStun(response); + } +} + +void RelayServer::AddConnection(RelayServerConnection* conn) { + assert(connections_.find(conn->addr_pair()) == connections_.end()); + connections_[conn->addr_pair()] = conn; +} + +void RelayServer::RemoveConnection(RelayServerConnection* conn) { + ConnectionMap::iterator iter = connections_.find(conn->addr_pair()); + assert(iter != connections_.end()); + connections_.erase(iter); +} + +void RelayServer::RemoveBinding(RelayServerBinding* binding) { + BindingMap::iterator iter = bindings_.find(binding->username()); + assert(iter != bindings_.end()); + bindings_.erase(iter); + + std::cout << "Removed a binding: " << bindings_.size() << " remaining" << std::endl; +} + +void RelayServer::OnTimeout(RelayServerBinding* binding) { + // This call will result in all of the necessary clean-up. + delete binding; +} + +RelayServerConnection::RelayServerConnection( + RelayServerBinding* binding, const SocketAddressPair& addrs, + AsyncPacketSocket* socket) + : binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) { + + // The creation of a new connection constitutes a use of the binding. + binding_->NoteUsed(); +} + +RelayServerConnection::~RelayServerConnection() { + // Remove this connection from the server's map (if it exists there). + binding_->server()->RemoveConnection(this); +} + +void RelayServerConnection::Send(const char* data, size_t size) { + // Note that the binding has been used again. + binding_->NoteUsed(); + + cricket::Send(socket_, data, size, addr_pair_.source()); +} + +void RelayServerConnection::Send( + const char* data, size_t size, const SocketAddress& from_addr) { + // If the from address is known to the client, we don't need to send it. + if (locked() && (from_addr == default_dest_)) { + Send(data, size); + return; + } + + // Wrap the given data in a data-indication packet. + + StunMessage msg; + msg.SetType(STUN_DATA_INDICATION); + msg.SetTransactionID("0000000000000000"); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(), + binding_->magic_cookie().size()); + msg.AddAttribute(magic_cookie_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2); + addr_attr->SetFamily(1); + addr_attr->SetIP(from_addr.ip()); + addr_attr->SetPort(from_addr.port()); + msg.AddAttribute(addr_attr); + + StunByteStringAttribute* data_attr = + StunAttribute::CreateByteString(STUN_ATTR_DATA); + assert(size <= 65536); + data_attr->CopyBytes(data, uint16(size)); + msg.AddAttribute(data_attr); + + SendStun(msg); +} + +void RelayServerConnection::SendStun(const StunMessage& msg) { + // Note that the binding has been used again. + binding_->NoteUsed(); + + cricket::SendStun(msg, socket_, addr_pair_.source()); +} + +void RelayServerConnection::SendStunError( + const StunMessage& request, int error_code, const char* error_desc) { + // An error does not indicate use. If no legitimate use off the binding + // occurs, we want it to be cleaned up even if errors are still occuring. + + cricket::SendStunError( + request, socket_, addr_pair_.source(), error_code, error_desc, + binding_->magic_cookie()); +} + +void RelayServerConnection::Lock() { + locked_ = true; +} + +void RelayServerConnection::Unlock() { + locked_ = false; +} + +// IDs used for posted messages: +const uint32 MSG_LIFETIME_TIMER = 1; + +RelayServerBinding::RelayServerBinding( + RelayServer* server, const std::string& username, + const std::string& password, uint32 lifetime) + : server_(server), username_(username), password_(password), + lifetime_(lifetime) { + + // For now, every connection uses the standard magic cookie value. + magic_cookie_.append( + reinterpret_cast<const char*>(STUN_MAGIC_COOKIE_VALUE), 4); + + // Initialize the last-used time to now. + NoteUsed(); + + // Set the first timeout check. + server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); +} + +RelayServerBinding::~RelayServerBinding() { + // Clear the outstanding timeout check. + server_->thread()->Clear(this); + + // Clean up all of the connections. + for (size_t i = 0; i < internal_connections_.size(); ++i) + delete internal_connections_[i]; + for (size_t i = 0; i < external_connections_.size(); ++i) + delete external_connections_[i]; + + // Remove this binding from the server's map. + server_->RemoveBinding(this); +} + +void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) { + internal_connections_.push_back(conn); +} + +void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) { + external_connections_.push_back(conn); +} + +void RelayServerBinding::NoteUsed() { + last_used_ = GetMillisecondCount(); +} + +bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const { + if (size < 24 + magic_cookie_.size()) { + return false; + } else { + return 0 == std::memcmp( + bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()); + } +} + +RelayServerConnection* RelayServerBinding::GetInternalConnection( + const SocketAddress& ext_addr) { + + // Look for an internal connection that is locked to this address. + for (size_t i = 0; i < internal_connections_.size(); ++i) { + if (internal_connections_[i]->locked() && + (ext_addr == internal_connections_[i]->default_destination())) + return internal_connections_[i]; + } + + // If one was not found, we send to the first connection. + assert(internal_connections_.size() > 0); + return internal_connections_[0]; +} + +RelayServerConnection* RelayServerBinding::GetExternalConnection( + const SocketAddress& ext_addr) { + for (size_t i = 0; i < external_connections_.size(); ++i) { + if (ext_addr == external_connections_[i]->addr_pair().source()) + return external_connections_[i]; + } + return 0; +} + +void RelayServerBinding::OnMessage(Message *pmsg) { + if (pmsg->message_id == MSG_LIFETIME_TIMER) { + assert(!pmsg->pdata); + + // If the lifetime timeout has been exceeded, then send a signal. + // Otherwise, just keep waiting. + if (GetMillisecondCount() >= last_used_ + lifetime_) { + SignalTimeout(this); + } else { + server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); + } + + } else { + assert(false); + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h new file mode 100644 index 00000000..01dc3678 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h @@ -0,0 +1,210 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __RELAYSERVER_H__ +#define __RELAYSERVER_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/socketaddresspair.h" +#include "talk/base/thread.h" +#include "talk/base/jtime.h" +#include "talk/p2p/base/stun.h" + +#include <string> +#include <vector> +#include <map> + +namespace cricket { + +class RelayServerBinding; +class RelayServerConnection; + +// Relays traffic between connections to the server that are "bound" together. +// All connections created with the same username/password are bound together. +class RelayServer : public sigslot::has_slots<> { +public: + // Creates a server, which will use this thread to post messages to itself. + RelayServer(Thread* thread); + ~RelayServer(); + + Thread* thread() { return thread_; } + + // Updates the set of sockets that the server uses to talk to "internal" + // clients. These are clients that do the "port allocations". + void AddInternalSocket(AsyncPacketSocket* socket); + void RemoveInternalSocket(AsyncPacketSocket* socket); + + // Updates the set of sockets that the server uses to talk to "external" + // clients. These are the clients that do not do allocations. They do not + // know that these addresses represent a relay server. + void AddExternalSocket(AsyncPacketSocket* socket); + void RemoveExternalSocket(AsyncPacketSocket* socket); + +private: + typedef std::vector<AsyncPacketSocket*> SocketList; + typedef std::map<std::string,RelayServerBinding*> BindingMap; + typedef std::map<SocketAddressPair,RelayServerConnection*> ConnectionMap; + + Thread* thread_; + SocketList internal_sockets_; + SocketList external_sockets_; + BindingMap bindings_; + ConnectionMap connections_; + + // Called when a packet is received by the server on one of its sockets. + void OnInternalPacket( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + void OnExternalPacket( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + + // Processes the relevant STUN request types from the client. + bool HandleStun(const char* bytes, size_t size, + const SocketAddress& remote_addr, AsyncPacketSocket* socket, + std::string* username, StunMessage* msg); + void HandleStunAllocate(const char* bytes, size_t size, + const SocketAddressPair& ap, + AsyncPacketSocket* socket); + void HandleStun(RelayServerConnection* int_conn, const char* bytes, + size_t size); + void HandleStunAllocate(RelayServerConnection* int_conn, + const StunMessage& msg); + void HandleStunSend(RelayServerConnection* int_conn, const StunMessage& msg); + + // Adds/Removes the a connection or binding. + void AddConnection(RelayServerConnection* conn); + void RemoveConnection(RelayServerConnection* conn); + void RemoveBinding(RelayServerBinding* binding); + + // Called when the timer for checking lifetime times out. + void OnTimeout(RelayServerBinding* binding); + + friend class RelayServerConnection; + friend class RelayServerBinding; +}; + +// Maintains information about a connection to the server. Each connection is +// part of one and only one binding. +class RelayServerConnection { +public: + RelayServerConnection(RelayServerBinding* binding, + const SocketAddressPair& addrs, + AsyncPacketSocket* socket); + ~RelayServerConnection(); + + RelayServerBinding* binding() { return binding_; } + AsyncPacketSocket* socket() { return socket_; } + + // Returns a pair where the source is the remote address and the destination + // is the local address. + const SocketAddressPair& addr_pair() { return addr_pair_; } + + // Sends a packet to the connected client. If an address is provided, then + // we make sure the internal client receives it, wrapping if necessary. + void Send(const char* data, size_t size); + void Send(const char* data, size_t size, const SocketAddress& ext_addr); + + // Sends a STUN message to the connected client with no wrapping. + void SendStun(const StunMessage& msg); + void SendStunError(const StunMessage& request, int code, const char* desc); + + // A locked connection is one for which we know the intended destination of + // any raw packet received. + bool locked() const { return locked_; } + void Lock(); + void Unlock(); + + // Records the address that raw packets should be forwarded to (for internal + // packets only; for external, we already know where they go). + const SocketAddress& default_destination() const { return default_dest_; } + void set_default_destination(const SocketAddress& addr) { + default_dest_ = addr; + } + +private: + RelayServerBinding* binding_; + SocketAddressPair addr_pair_; + AsyncPacketSocket* socket_; + bool locked_; + SocketAddress default_dest_; +}; + +// Records a set of internal and external connections that we relay between, +// or in other words, that are "bound" together. +class RelayServerBinding : public MessageHandler { +public: + RelayServerBinding( + RelayServer* server, const std::string& username, + const std::string& password, uint32 lifetime); + virtual ~RelayServerBinding(); + + RelayServer* server() { return server_; } + uint32 lifetime() { return lifetime_; } + const std::string& username() { return username_; } + const std::string& password() { return password_; } + const std::string& magic_cookie() { return magic_cookie_; } + + // Adds/Removes a connection into the binding. + void AddInternalConnection(RelayServerConnection* conn); + void AddExternalConnection(RelayServerConnection* conn); + + // We keep track of the use of each binding. If we detect that it was not + // used for longer than the lifetime, then we send a signal. + void NoteUsed(); + sigslot::signal1<RelayServerBinding*> SignalTimeout; + + // Determines whether the given packet has the magic cookie present (in the + // right place). + bool HasMagicCookie(const char* bytes, size_t size) const; + + // Determines the connection to use to send packets to or from the given + // external address. + RelayServerConnection* GetInternalConnection(const SocketAddress& ext_addr); + RelayServerConnection* GetExternalConnection(const SocketAddress& ext_addr); + + // MessageHandler: + void OnMessage(Message *pmsg); + +private: + RelayServer* server_; + + std::string username_; + std::string password_; + std::string magic_cookie_; + + std::vector<RelayServerConnection*> internal_connections_; + std::vector<RelayServerConnection*> external_connections_; + + uint32 lifetime_; + uint32 last_used_; + // TODO: bandwidth +}; + +} // namespace cricket + +#endif // __RELAYSERVER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro new file mode 100644 index 00000000..41bc6b63 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +INCLUDEPATH = ../../.. +DEFINES += POSIX + +include(../../../../../conf.pri) + +# Input +SOURCES += \ + relayserver.cc \ + relayserver_main.cc \ + ../../base/host.cc \ + ../../base/socketaddresspair.cc + +LIBS += ../../../liblibjingle.a diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc new file mode 100644 index 00000000..5f624f37 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc @@ -0,0 +1,75 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/relayserver.h" +#include <iostream> +#include <assert.h> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +using namespace cricket; + +int main(int argc, char **argv) { + if (argc != 1) { + std::cerr << "usage: relayserver" << std::endl; + return 1; + } + + assert(LocalHost().networks().size() >= 2); + SocketAddress int_addr(LocalHost().networks()[1]->ip(), 5000); + SocketAddress ext_addr(LocalHost().networks()[1]->ip(), 5001); + + Thread *pthMain = Thread::Current(); + + AsyncUDPSocket* int_socket = CreateAsyncUDPSocket(pthMain->socketserver()); + if (int_socket->Bind(int_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + AsyncUDPSocket* ext_socket = CreateAsyncUDPSocket(pthMain->socketserver()); + if (ext_socket->Bind(ext_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + RelayServer server(pthMain); + server.AddInternalSocket(int_socket); + server.AddExternalSocket(ext_socket); + + std::cout << "Listening internally at " << int_addr.ToString() << std::endl; + std::cout << "Listening externally at " << ext_addr.ToString() << std::endl; + + pthMain->Loop(); + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc new file mode 100644 index 00000000..73873338 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc @@ -0,0 +1,421 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/p2p/base/helpers.h" +#include "talk/p2p/base/session.h" + +namespace cricket { + +const uint32 MSG_TIMEOUT = 1; +const uint32 MSG_ERROR = 2; +const uint32 MSG_STATE = 3; + +Session::Session(SessionManager *session_manager, const std::string &name, + const SessionID& id) { + session_manager_ = session_manager; + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + name_ = name; + id_ = id; + error_ = ERROR_NONE; + state_ = STATE_INIT; + initiator_ = false; + description_ = NULL; + remote_description_ = NULL; + socket_manager_ = new SocketManager(session_manager_); + socket_manager_->SignalCandidatesReady.connect(this, &Session::OnCandidatesReady); + socket_manager_->SignalNetworkError.connect(this, &Session::OnNetworkError); + socket_manager_->SignalState.connect(this, &Session::OnSocketState); + socket_manager_->SignalRequestSignaling.connect(this, &Session::OnRequestSignaling); +} + +Session::~Session() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + delete description_; + delete remote_description_; + delete socket_manager_; + session_manager_->signaling_thread()->Clear(this); +} + +P2PSocket *Session::CreateSocket(const std::string &name) { + return socket_manager_->CreateSocket(name); +} + +void Session::DestroySocket(P2PSocket *socket) { + socket_manager_->DestroySocket(socket); +} + +void Session::OnCandidatesReady(const std::vector<Candidate>& candidates) { + SendSessionMessage(SessionMessage::TYPE_CANDIDATES, NULL, &candidates, NULL); +} + +void Session::OnNetworkError() { + // Socket manager is experiencing a network error trying to allocate + // network resources (usually port allocation) + + set_error(ERROR_NETWORK); +} + +void Session::OnSocketState() { + // If the call is not in progress, then we don't care about writability. + // We have separate timers for making sure we transition back to the in- + // progress state in time. + if (state_ != STATE_INPROGRESS) + return; + + // Put the timer into the write state. This is called when the state changes, + // so we will restart the timer each time we lose writability. + if (socket_manager_->writable()) { + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + } else { + session_manager_->signaling_thread()->PostDelayed( + session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + } +} + +void Session::OnRequestSignaling() { + SignalRequestSignaling(); +} + +void Session::OnSignalingReady() { + socket_manager_->OnSignalingReady(); +} + +void Session::SendSessionMessage(SessionMessage::Type type, + const SessionDescription* description, + const std::vector<Candidate>* candidates, + SessionMessage::Cookie* redirect_cookie) { + SessionMessage m; + m.set_type(type); + m.set_to(remote_address_); + m.set_name(name_); + m.set_description(description); + m.set_session_id(id_); + if (candidates) + m.set_candidates(*candidates); + m.set_redirect_target(redirect_target_); + m.set_redirect_cookie(redirect_cookie); + SignalOutgoingMessage(this, m); +} + +bool Session::Initiate(const std::string &to, const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only from STATE_INIT + if (state_ != STATE_INIT) + return false; + + // Setup for signaling. Initiate is asynchronous. It occurs once the address + // candidates are ready. + initiator_ = true; + remote_address_ = to; + description_ = description; + SendSessionMessage(SessionMessage::TYPE_INITIATE, description, NULL, NULL); + set_state(Session::STATE_SENTINITIATE); + + // Let the socket manager know we now want the candidates + socket_manager_->StartProcessingCandidates(); + + // Start the session timeout + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + return true; +} + +bool Session::Accept(const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only if just received initiate + if (state_ != STATE_RECEIVEDINITIATE) + return false; + + // Setup for signaling. Accept is asynchronous. It occurs once the address + // candidates are ready. + initiator_ = false; + description_ = description; + SendSessionMessage(SessionMessage::TYPE_ACCEPT, description, NULL, NULL); + set_state(Session::STATE_SENTACCEPT); + + return true; +} + +bool Session::Modify(const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only if session already STATE_INPROGRESS + if (state_ != STATE_INPROGRESS) + return false; + + // Modify is asynchronous. It occurs once the address candidates are ready. + // Either side can send a modify. It is only valid in an already accepted + // session. + description_ = description; + SendSessionMessage(SessionMessage::TYPE_MODIFY, description, NULL, NULL); + set_state(Session::STATE_SENTMODIFY); + + // Start the session timeout + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + return true; +} + +bool Session::Redirect(const std::string& target) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Redirect is sent in response to an initiate or modify, to redirect the + // request + if (state_ != STATE_RECEIVEDINITIATE) + return false; + + initiator_ = false; + redirect_target_ = target; + SendSessionMessage(SessionMessage::TYPE_REDIRECT, NULL, NULL, NULL); + + // A redirect puts us in the same state as reject. It just sends a different + // kind of reject message, if you like. + set_state(STATE_SENTREDIRECT); + + return true; +} + +bool Session::Reject() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Reject is sent in response to an initiate or modify, to reject the + // request + if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY) + return false; + + initiator_ = false; + SendSessionMessage(SessionMessage::TYPE_REJECT, NULL, NULL, NULL); + set_state(STATE_SENTREJECT); + + return true; +} + +bool Session::Terminate() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Either side can terminate, at any time. + if (state_ == STATE_SENTTERMINATE && state_ != STATE_RECEIVEDTERMINATE) + return false; + + // But we don't need to terminate if we already rejected. The other client + // already knows that we're done with this session. + if (state_ != STATE_SENTREDIRECT) + SendSessionMessage(SessionMessage::TYPE_TERMINATE, NULL, NULL, NULL); + + set_state(STATE_SENTTERMINATE); + + return true; +} + +void Session::OnIncomingError(const SessionMessage &m) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // If a candidate message errors out or gets dropped for some reason we + // ignore the error. + if (m.type() != SessionMessage::TYPE_CANDIDATES) { + set_error(ERROR_RESPONSE); + } +} + +void Session::OnIncomingMessage(const SessionMessage &m) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + switch (m.type()) { + case SessionMessage::TYPE_INITIATE: + remote_description_ = m.description(); + remote_address_ = m.from(); + name_ = m.name(); + initiator_ = false; + set_state(STATE_RECEIVEDINITIATE); + + // Let the socket manager know we now want the initial candidates + socket_manager_->StartProcessingCandidates(); + break; + + case SessionMessage::TYPE_ACCEPT: + remote_description_ = m.description(); + set_state(STATE_RECEIVEDACCEPT); + break; + + case SessionMessage::TYPE_MODIFY: + remote_description_ = m.description(); + set_state(STATE_RECEIVEDMODIFY); + break; + + case SessionMessage::TYPE_CANDIDATES: + socket_manager_->AddRemoteCandidates(m.candidates()); + break; + + case SessionMessage::TYPE_REJECT: + set_state(STATE_RECEIVEDREJECT); + break; + + case SessionMessage::TYPE_REDIRECT: + OnRedirectMessage(m); + break; + + case SessionMessage::TYPE_TERMINATE: + set_state(STATE_RECEIVEDTERMINATE); + break; + } +} + +void Session::OnRedirectMessage(const SessionMessage &m) { + ASSERT(state_ == STATE_SENTINITIATE); + if (state_ != STATE_SENTINITIATE) + return; + + ASSERT(m.redirect_target().size() != 0); + remote_address_ = m.redirect_target(); + + SendSessionMessage(SessionMessage::TYPE_INITIATE, description_, NULL, + m.redirect_cookie()->Copy()); + + // Restart the session timeout. + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + + // Reset all of the sockets back into the initial state. + socket_manager_->ResetSockets(); +} + +Session::State Session::state() { + return state_; +} + +void Session::set_state(State state) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (state != state_) { + state_ = state; + SignalState(this, state); + session_manager_->signaling_thread()->Post(this, MSG_STATE); + } +} + +Session::Error Session::error() { + return error_; +} + +void Session::set_error(Error error) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (error != error_) { + error_ = error; + SignalError(this, error); + session_manager_->signaling_thread()->Post(this, MSG_ERROR); + } +} + +const std::string &Session::name() { + return name_; +} + +const std::string &Session::remote_address() { + return remote_address_; +} + +bool Session::initiator() { + return initiator_; +} + +const SessionID& Session::id() { + return id_; +} + +const SessionDescription *Session::description() { + return description_; +} + +const SessionDescription *Session::remote_description() { + return remote_description_; +} + +SessionManager *Session::session_manager() { + return session_manager_; +} + +void Session::OnMessage(Message *pmsg) { + switch(pmsg->message_id) { + case MSG_TIMEOUT: + // Session timeout has occured. Check to see if the session is still trying + // to signal. If so, the session has timed out. + // The Sockets have their own timeout for connectivity. + set_error(ERROR_TIME); + break; + + case MSG_ERROR: + switch (error_) { + case ERROR_RESPONSE: + // This state could be reached if we get an error in response to an IQ + // or if the network is so slow we time out on an individual IQ exchange. + // In either case, Terminate (send more messages) and ignore the likely + // cascade of more errors. + + // fall through + case ERROR_NETWORK: + case ERROR_TIME: + // Time ran out - no response + Terminate(); + break; + + default: + break; + } + break; + + case MSG_STATE: + switch (state_) { + case STATE_SENTACCEPT: + case STATE_RECEIVEDACCEPT: + set_state(STATE_INPROGRESS); + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + OnSocketState(); // Update the writability timeout state. + break; + + case STATE_SENTREJECT: + case STATE_SENTREDIRECT: + case STATE_RECEIVEDREJECT: + Terminate(); + break; + + case STATE_SENTTERMINATE: + case STATE_RECEIVEDTERMINATE: + session_manager_->DestroySession(this); + break; + + default: + // explicitly ignoring some states here + break; + } + break; + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h new file mode 100644 index 00000000..1414a375 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h @@ -0,0 +1,140 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SESSION_H_ +#define _SESSION_H_ + +#include "talk/base/socketaddress.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/sessionmessage.h" +#include "talk/p2p/base/socketmanager.h" +#include "talk/p2p/base/p2psocket.h" +#include "talk/p2p/base/port.h" +#include <string> + +namespace cricket { + +class SessionManager; +class SocketManager; + +// A specific Session created by the SessionManager +// A Session manages signaling for session setup and tear down, and connectivity +// with P2PSockets + +class Session : public MessageHandler, public sigslot::has_slots<> { +public: + enum State { + STATE_INIT = 0, + STATE_SENTINITIATE, // sent initiate, waiting for Accept or Reject + STATE_RECEIVEDINITIATE, // received an initiate. Call Accept or Reject + STATE_SENTACCEPT, // sent accept. begin connectivity establishment + STATE_RECEIVEDACCEPT, // received accept. begin connectivity establishment + STATE_SENTMODIFY, // sent modify, waiting for Accept or Reject + STATE_RECEIVEDMODIFY, // received modify, call Accept or Reject + STATE_SENTREJECT, // sent reject after receiving initiate + STATE_RECEIVEDREJECT, // received reject after sending initiate + STATE_SENTREDIRECT, // sent direct after receiving initiate + STATE_SENTTERMINATE, // sent terminate (any time / either side) + STATE_RECEIVEDTERMINATE, // received terminate (any time / either side) + STATE_INPROGRESS, // session accepted and in progress + }; + + enum Error { + ERROR_NONE = 0, // no error + ERROR_TIME, // no response to signaling + ERROR_RESPONSE, // error during signaling + ERROR_NETWORK, // network error, could not allocate network resources + }; + + Session(SessionManager *session_manager, const std::string &name, const SessionID& id); + ~Session(); + + // From MessageHandler + void OnMessage(Message *pmsg); + + P2PSocket *CreateSocket(const std::string & name); + void DestroySocket(P2PSocket *socket); + + bool Initiate(const std::string &to, const SessionDescription *description); + bool Accept(const SessionDescription *description); + bool Modify(const SessionDescription *description); + bool Reject(); + bool Redirect(const std::string& target); + bool Terminate(); + + SessionManager *session_manager(); + const std::string &name(); + const std::string &remote_address(); + bool initiator(); + const SessionID& id(); + const SessionDescription *description(); + const SessionDescription *remote_description(); + + State state(); + Error error(); + + void OnSignalingReady(); + void OnIncomingMessage(const SessionMessage &m); + void OnIncomingError(const SessionMessage &m); + + sigslot::signal2<Session *, State> SignalState; + sigslot::signal2<Session *, Error> SignalError; + sigslot::signal2<Session *, const SessionMessage &> SignalOutgoingMessage; + sigslot::signal0<> SignalRequestSignaling; + +private: + void SendSessionMessage(SessionMessage::Type type, + const SessionDescription* description, + const std::vector<Candidate>* candidates, + SessionMessage::Cookie* redirect_cookie); + void OnCandidatesReady(const std::vector<Candidate>& candidates); + void OnNetworkError(); + void OnSocketState(); + void OnRequestSignaling(); + void OnRedirectMessage(const SessionMessage &m); + + void set_state(State state); + void set_error(Error error); + + bool initiator_; + SessionManager *session_manager_; + SessionID id_; + SocketManager *socket_manager_; + std::string name_; + std::string remote_address_; + const SessionDescription *description_; + const SessionDescription *remote_description_; + std::string redirect_target_; + State state_; + Error error_; + CriticalSection crit_; +}; + +} // namespace cricket + +#endif // _SESSION_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h new file mode 100644 index 00000000..28b70845 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h @@ -0,0 +1,42 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SESSIONDESCRIPTION_H_ +#define _SESSIONDESCRIPTION_H_ + +namespace cricket { + +// The client overrides this with whatever + +class SessionDescription { +public: + virtual ~SessionDescription() {} +}; + +} // namespace cricket + +#endif // _SESSIONDESCRIPTION_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h new file mode 100644 index 00000000..a12535c0 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SESSIONID_H_ +#define _SESSIONID_H_ + +#include "talk/base/basictypes.h" +#include <string> +#include <sstream> + +namespace cricket { + +// Each session is identified by a pair (from,id), where id is only +// assumed to be unique to the machine identified by from. +class SessionID { +public: + SessionID() : id_str_("0") { + } + SessionID(const std::string& initiator, uint32 id) + : initiator_(initiator) { + set_id(id); + } + SessionID(const SessionID& sid) + : id_str_(sid.id_str_), initiator_(sid.initiator_) { + } + + void set_id(uint32 id) { + std::stringstream st; + st << id; + st >> id_str_; + } + const std::string id_str() const { + return id_str_; + } + void set_id_str(const std::string &id_str) { + id_str_ = id_str; + } + + const std::string &initiator() const { + return initiator_; + } + void set_initiator(const std::string &initiator) { + initiator_ = initiator; + } + + bool operator <(const SessionID& sid) const { + int r = initiator_.compare(sid.initiator_); + if (r == 0) + r = id_str_.compare(sid.id_str_); + return r < 0; + } + + bool operator ==(const SessionID& sid) const { + return (id_str_ == sid.id_str_) && (initiator_ == sid.initiator_); + } + + SessionID& operator =(const SessionID& sid) { + id_str_ = sid.id_str_; + initiator_ = sid.initiator_; + return *this; + } + +private: + std::string id_str_; + std::string initiator_; +}; + +} // namespace cricket + +#endif // _SESSIONID_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc new file mode 100644 index 00000000..4c1c09d9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc @@ -0,0 +1,173 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/common.h" +#include "talk/p2p/base/helpers.h" +#include "sessionmanager.h" + +namespace cricket { + +SessionManager::SessionManager(PortAllocator *allocator, Thread *worker) { + allocator_ = allocator; + signaling_thread_ = Thread::Current(); + if (worker == NULL) { + worker_thread_ = Thread::Current(); + } else { + worker_thread_ = worker; + } + timeout_ = 50; +} + +SessionManager::~SessionManager() { + // Note: Session::Terminate occurs asynchronously, so it's too late to + // delete them now. They better be all gone. + ASSERT(session_map_.empty()); + //TerminateAll(); +} + +Session *SessionManager::CreateSession(const std::string &name, const std::string& initiator) { + return CreateSession(name, SessionID(initiator, CreateRandomId()), false); +} + +Session *SessionManager::CreateSession(const std::string &name, const SessionID& id, bool received_initiate) { + Session *session = new Session(this, name, id); + session_map_[session->id()] = session; + session->SignalRequestSignaling.connect(this, &SessionManager::OnRequestSignaling); + SignalSessionCreate(session, received_initiate); + return session; +} + +void SessionManager::DestroySession(Session *session) { + if (session != NULL) { + std::map<SessionID, Session *>::iterator it = session_map_.find(session->id()); + if (it != session_map_.end()) { + SignalSessionDestroy(session); + session_map_.erase(it); + delete session; + } + } +} + +Session *SessionManager::GetSession(const SessionID& id) { + // If the id isn't present, the [] operator will make a NULL entry + std::map<SessionID, Session *>::iterator it = session_map_.find(id); + if (it != session_map_.end()) + return (*it).second; + return NULL; +} + +void SessionManager::TerminateAll() { + while (session_map_.begin() != session_map_.end()) { + Session *session = session_map_.begin()->second; + session->Terminate(); + } +} + +void SessionManager::OnIncomingError(const SessionMessage &m) { + // Incoming signaling error. This means, as the result of trying + // to send message m, and error was generated. In all cases, a + // session should already exist + + Session *session; + switch (m.type()) { + case SessionMessage::TYPE_INITIATE: + case SessionMessage::TYPE_ACCEPT: + case SessionMessage::TYPE_MODIFY: + case SessionMessage::TYPE_CANDIDATES: + case SessionMessage::TYPE_REJECT: + case SessionMessage::TYPE_TERMINATE: + session = GetSession(m.session_id()); + break; + + default: + return; + } + + if (session != NULL) + session->OnIncomingError(m); + +} + +void SessionManager::OnIncomingMessage(const SessionMessage &m) { + // In the case of an incoming initiate, there is no session yet, and one needs to be created. + // The other cases have sessions already. + + Session *session; + switch (m.type()) { + case SessionMessage::TYPE_INITIATE: + session = CreateSession(m.name(), m.session_id(), true); + break; + + case SessionMessage::TYPE_ACCEPT: + case SessionMessage::TYPE_MODIFY: + case SessionMessage::TYPE_CANDIDATES: + case SessionMessage::TYPE_REJECT: + case SessionMessage::TYPE_REDIRECT: + case SessionMessage::TYPE_TERMINATE: + session = GetSession(m.session_id()); + break; + + default: + return; + } + + if (session != NULL) + session->OnIncomingMessage(m); +} + +void SessionManager::OnSignalingReady() { + for (std::map<SessionID, Session *>::iterator it = session_map_.begin(); + it != session_map_.end(); ++it) { + it->second->OnSignalingReady(); + } +} + +void SessionManager::OnRequestSignaling() { + SignalRequestSignaling(); +} + +PortAllocator *SessionManager::port_allocator() const { + return allocator_; +} + +Thread *SessionManager::worker_thread() const { + return worker_thread_; +} + +Thread *SessionManager::signaling_thread() const { + return signaling_thread_; +} + +int SessionManager::session_timeout() { + return timeout_; +} + +void SessionManager::set_session_timeout(int timeout) { + timeout_ = timeout; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h new file mode 100644 index 00000000..5ce0e4c5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h @@ -0,0 +1,86 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SESSIONMANAGER_H_ +#define _SESSIONMANAGER_H_ + +#include "talk/base/thread.h" +#include "talk/p2p/base/portallocator.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessionmessage.h" +#include "talk/base/sigslot.h" + +#include <string> +#include <utility> +#include <map> + +namespace cricket { + +class Session; + +// SessionManager manages session instances + +class SessionManager : public sigslot::has_slots<> { +public: + SessionManager(PortAllocator *allocator, Thread *worker_thread = NULL); + virtual ~SessionManager(); + + Session *CreateSession(const std::string &name, const std::string& initiator); + void DestroySession(Session *session); + Session *GetSession(const SessionID& id); + void TerminateAll(); + void OnIncomingMessage(const SessionMessage &m); + void OnIncomingError(const SessionMessage &m); + void OnSignalingReady(); + + PortAllocator *port_allocator() const; + Thread *worker_thread() const; + Thread *signaling_thread() const; + int session_timeout(); + void set_session_timeout(int timeout); + + sigslot::signal2<Session *, bool> SignalSessionCreate; + sigslot::signal1<Session *> SignalSessionDestroy; + + // Note: you can connect this directly to OnSignalingReady(), if a signalling + // check is not required. + sigslot::signal0<> SignalRequestSignaling; + +private: + Session *CreateSession(const std::string &name, const SessionID& id, bool received_initiate); + void OnRequestSignaling(); + + int timeout_; + Thread *worker_thread_; + Thread *signaling_thread_; + PortAllocator *allocator_; + std::map<SessionID, Session *> session_map_; +}; + +} // namespace cricket + +#endif // _SESSIONMANAGER_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h new file mode 100644 index 00000000..fc1b0323 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h @@ -0,0 +1,133 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SESSIONMESSAGE_H_ +#define _SESSIONMESSAGE_H_ + +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/base/sessionid.h" +#include "talk/base/basictypes.h" +#include <string> +#include <vector> +#include <sstream> + +namespace cricket { + +class SessionMessage { +public: + enum Type { + TYPE_INITIATE = 0, // Initiate message + TYPE_ACCEPT, // Accept message + TYPE_MODIFY, // Modify message + TYPE_CANDIDATES, // Candidates message + TYPE_REJECT, // Reject message + TYPE_REDIRECT, // Reject message + TYPE_TERMINATE, // Terminate message + }; + + class Cookie { + public: + virtual ~Cookie() {} + + // Returns a copy of this cookie. + virtual Cookie* Copy() = 0; + }; + + Type type() const { + return type_; + } + void set_type(Type type) { + type_ = type; + } + const SessionID& session_id() const { + return id_; + } + SessionID& session_id() { + return id_; + } + void set_session_id(const SessionID& id) { + id_ = id; + } + const std::string &from() const { + return from_; + } + void set_from(const std::string &from) { + from_ = from; + } + const std::string &to() const { + return to_; + } + void set_to(const std::string &to) { + to_ = to; + } + const std::string &name() const { + return name_; + } + void set_name(const std::string &name) { + name_ = name; + } + const std::string &redirect_target() const { + return redirect_target_; + } + void set_redirect_target(const std::string &redirect_target) { + redirect_target_ = redirect_target; + } + Cookie *redirect_cookie() const { + return redirect_cookie_; + } + void set_redirect_cookie(Cookie* redirect_cookie) { + redirect_cookie_ = redirect_cookie; + } + const SessionDescription *description() const { + return description_; + } + void set_description(const SessionDescription *description) { + description_ = description; + } + const std::vector<Candidate> &candidates() const { + return candidates_; + } + void set_candidates(const std::vector<Candidate> &candidates) { + candidates_ = candidates; + } + +private: + Type type_; + SessionID id_; + std::string from_; + std::string to_; + std::string name_; + const SessionDescription *description_; + std::vector<Candidate> candidates_; + std::string redirect_target_; + Cookie* redirect_cookie_; +}; + +} // namespace cricket + +#endif // _SESSIONMESSAGE_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc new file mode 100644 index 00000000..2f0d67b8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc @@ -0,0 +1,273 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "socketmanager.h" +#include <cassert> + +namespace cricket { + +const uint32 MSG_CREATESOCKET = 1; +const uint32 MSG_DESTROYSOCKET = 2; +const uint32 MSG_ONSIGNALINGREADY = 3; +const uint32 MSG_CANDIDATESREADY = 4; +const uint32 MSG_ADDREMOTECANDIDATES = 5; +const uint32 MSG_ONREQUESTSIGNALING = 6; +const uint32 MSG_RESETSOCKETS = 7; + +struct CreateParams { + CreateParams() {} + P2PSocket *socket; + std::string name; +}; + +SocketManager::SocketManager(SessionManager *session_manager) { + session_manager_ = session_manager; + candidates_requested_ = false; + writable_ = false; +} + +SocketManager::~SocketManager() { + assert(Thread::Current() == session_manager_->signaling_thread()); + + // Are the sockets destroyed? If not, destroy them + + critSM_.Enter(); + while (sockets_.size() != 0) { + P2PSocket *socket = sockets_[0]; + critSM_.Leave(); + DestroySocket(socket); + critSM_.Enter(); + } + critSM_.Leave(); + + // Clear queues + + session_manager_->signaling_thread()->Clear(this); + session_manager_->worker_thread()->Clear(this); +} + +P2PSocket *SocketManager::CreateSocket(const std::string &name) { + // Can occur on any thread + CreateParams params; + params.name = name; + params.socket = NULL; + TypedMessageData<CreateParams *> data(¶ms); + session_manager_->worker_thread()->Send(this, MSG_CREATESOCKET, &data); + return data.data()->socket; +} + +P2PSocket *SocketManager::CreateSocket_w(const std::string &name) { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + CritScope cs(&critSM_); + P2PSocket *socket = new P2PSocket(name, session_manager_->port_allocator()); + socket->SignalCandidatesReady.connect(this, &SocketManager::OnCandidatesReady); + socket->SignalState.connect(this, &SocketManager::OnSocketState); + socket->SignalRequestSignaling.connect(this, &SocketManager::OnRequestSignaling); + sockets_.push_back(socket); + socket->StartProcessingCandidates(); + return socket; +} + +void SocketManager::DestroySocket(P2PSocket *socket) { + // Can occur on any thread + TypedMessageData<P2PSocket *> data(socket); + session_manager_->worker_thread()->Send(this, MSG_DESTROYSOCKET, &data); +} + +void SocketManager::DestroySocket_w(P2PSocket *socket) { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + + // Only if socket exists + CritScope cs(&critSM_); + std::vector<P2PSocket *>::iterator it; + it = std::find(sockets_.begin(), sockets_.end(), socket); + if (it == sockets_.end()) + return; + sockets_.erase(it); + delete socket; +} + +void SocketManager::StartProcessingCandidates() { + // Only on signaling thread + assert(Thread::Current() == session_manager_->signaling_thread()); + + // When sockets are created, their candidates are requested. + // When the candidates are ready, the client is signaled + // on the signaling thread + candidates_requested_ = true; + session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY); +} + +void SocketManager::OnSignalingReady() { + session_manager_->worker_thread()->Post(this, MSG_ONSIGNALINGREADY); +} + +void SocketManager::OnSignalingReady_w() { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + for (uint32 i = 0; i < sockets_.size(); ++i) { + sockets_[i]->OnSignalingReady(); + } +} + +void SocketManager::OnCandidatesReady( + P2PSocket *socket, const std::vector<Candidate>& candidates) { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + + // Remember candidates + CritScope cs(&critSM_); + std::vector<Candidate>::const_iterator it; + for (it = candidates.begin(); it != candidates.end(); it++) + candidates_.push_back(*it); + + // If candidates requested, tell signaling thread + if (candidates_requested_) + session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY); +} + +void SocketManager::ResetSockets() { + assert(Thread::Current() == session_manager_->signaling_thread()); + session_manager_->worker_thread()->Post(this, MSG_RESETSOCKETS); +} + +void SocketManager::ResetSockets_w() { + assert(Thread::Current() == session_manager_->worker_thread()); + + for (size_t i = 0; i < sockets_.size(); ++i) + sockets_[i]->Reset(); +} + +void SocketManager::OnSocketState(P2PSocket* socket, P2PSocket::State state) { + assert(Thread::Current() == session_manager_->worker_thread()); + + bool writable = false; + for (uint32 i = 0; i < sockets_.size(); ++i) + if (sockets_[i]->writable()) + writable = true; + + if (writable_ != writable) { + writable_ = writable; + SignalState(); + } +} + +void SocketManager::OnRequestSignaling() { + assert(Thread::Current() == session_manager_->worker_thread()); + session_manager_->signaling_thread()->Post(this, MSG_ONREQUESTSIGNALING); +} + + +void SocketManager::AddRemoteCandidates(const std::vector<Candidate> &remote_candidates) { + assert(Thread::Current() == session_manager_->signaling_thread()); + TypedMessageData<std::vector<Candidate> > *data = new TypedMessageData<std::vector<Candidate> >(remote_candidates); + session_manager_->worker_thread()->Post(this, MSG_ADDREMOTECANDIDATES, data); +} + +void SocketManager::AddRemoteCandidates_w(const std::vector<Candidate> &remote_candidates) { + assert(Thread::Current() == session_manager_->worker_thread()); + + // Local and remote candidates now exist, so connectivity checking can + // commence. Tell the P2PSockets about the remote candidates. + // Group candidates by socket name + + CritScope cs(&critSM_); + std::vector<P2PSocket *>::iterator it_socket; + for (it_socket = sockets_.begin(); it_socket != sockets_.end(); it_socket++) { + // Create a vector of remote candidates for each socket + std::string name = (*it_socket)->name(); + std::vector<Candidate> candidate_bundle; + std::vector<Candidate>::const_iterator it_candidate; + for (it_candidate = remote_candidates.begin(); it_candidate != remote_candidates.end(); it_candidate++) { + if ((*it_candidate).name() == name) + candidate_bundle.push_back(*it_candidate); + } + if (candidate_bundle.size() != 0) + (*it_socket)->AddRemoteCandidates(candidate_bundle); + } +} + +void SocketManager::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CREATESOCKET: + { + assert(Thread::Current() == session_manager_->worker_thread()); + TypedMessageData<CreateParams *> *params = static_cast<TypedMessageData<CreateParams *> *>(message->pdata); + params->data()->socket = CreateSocket_w(params->data()->name); + } + break; + + case MSG_DESTROYSOCKET: + { + assert(Thread::Current() == session_manager_->worker_thread()); + TypedMessageData<P2PSocket *> *data = static_cast<TypedMessageData<P2PSocket *> *>(message->pdata); + DestroySocket_w(data->data()); + } + break; + + case MSG_ONSIGNALINGREADY: + assert(Thread::Current() == session_manager_->worker_thread()); + OnSignalingReady_w(); + break; + + case MSG_ONREQUESTSIGNALING: + assert(Thread::Current() == session_manager_->signaling_thread()); + SignalRequestSignaling(); + break; + + case MSG_CANDIDATESREADY: + assert(Thread::Current() == session_manager_->signaling_thread()); + if (candidates_requested_) { + CritScope cs(&critSM_); + if (candidates_.size() > 0) { + SignalCandidatesReady(candidates_); + candidates_.clear(); + } + } + break; + + case MSG_ADDREMOTECANDIDATES: + { + assert(Thread::Current() == session_manager_->worker_thread()); + TypedMessageData<const std::vector<Candidate> > *data = static_cast<TypedMessageData<const std::vector<Candidate> > *>(message->pdata); + AddRemoteCandidates_w(data->data()); + delete data; + } + break; + + case MSG_RESETSOCKETS: + ResetSockets_w(); + break; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h new file mode 100644 index 00000000..3ca1cf74 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h @@ -0,0 +1,101 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SOCKETMANAGER_H_ +#define _SOCKETMANAGER_H_ + +#include "talk/base/criticalsection.h" +#include "talk/base/messagequeue.h" +#include "talk/base/sigslot.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/p2psocket.h" +#include "talk/p2p/base/socketmanager.h" + +#include <string> +#include <vector> + +namespace cricket { + +class SessionManager; + +// Manages P2PSocket creation/destruction/readiness. +// Provides thread separation between session and sockets. +// This allows session to execute on the signaling thread, +// and sockets to execute on the worker thread, if desired, +// which is good for some media types (audio/video for example). + +class SocketManager : public MessageHandler, public sigslot::has_slots<> { +public: + SocketManager(SessionManager *session_manager); + virtual ~SocketManager(); + + // Determines whether any of the created sockets are currently writable. + bool writable() { return writable_; } + + P2PSocket *CreateSocket(const std::string & name); + void DestroySocket(P2PSocket *socket); + + // Start discovering local candidates + void StartProcessingCandidates(); + + // Adds the given candidates that were sent by the remote side. + void AddRemoteCandidates(const std::vector<Candidate>& candidates); + + // signaling channel is up, ready to transmit candidates as they are discovered + void OnSignalingReady(); + + // Put all of the sockets back into the initial state. + void ResetSockets(); + + sigslot::signal1<const std::vector<Candidate>&> SignalCandidatesReady; + sigslot::signal0<> SignalNetworkError; + sigslot::signal0<> SignalState; + sigslot::signal0<> SignalRequestSignaling; + +private: + P2PSocket *CreateSocket_w(const std::string &name); + void DestroySocket_w(P2PSocket *socket); + void OnSignalingReady_w(); + void AddRemoteCandidates_w(const std::vector<Candidate> &candidates); + virtual void OnMessage(Message *message); + void OnCandidatesReady(P2PSocket *socket, const std::vector<Candidate>&); + void OnSocketState(P2PSocket* socket, P2PSocket::State state); + void OnRequestSignaling(void); + void ResetSockets_w(); + + SessionManager *session_manager_; + std::vector<Candidate> candidates_; + CriticalSection critSM_; + std::vector<P2PSocket *> sockets_; + bool candidates_requested_; + bool writable_; +}; + +} + +#endif // _SOCKETMANAGER_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc new file mode 100644 index 00000000..6a22b238 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc @@ -0,0 +1,576 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/logging.h" +#include "talk/p2p/base/stun.h" +#include <iostream> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcpy; +} +#endif + +namespace cricket { + +const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST"; +const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED"; +const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE"; +const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS"; +const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE"; +const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME"; +const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS"; +const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR"; +const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE"; + +StunMessage::StunMessage() : type_(0), length_(0), + transaction_id_("0000000000000000") { + assert(transaction_id_.size() == 16); + attrs_ = new std::vector<StunAttribute*>(); +} + +StunMessage::~StunMessage() { + for (unsigned i = 0; i < attrs_->size(); i++) + delete (*attrs_)[i]; + delete attrs_; +} + +void StunMessage::SetTransactionID(const std::string& str) { + assert(str.size() == 16); + transaction_id_ = str; +} + +void StunMessage::AddAttribute(StunAttribute* attr) { + attrs_->push_back(attr); + length_ += attr->length() + 4; +} + +const StunAddressAttribute* +StunMessage::GetAddress(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunUInt32Attribute* +StunMessage::GetUInt32(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunByteStringAttribute* +StunMessage::GetByteString(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MESSAGE_INTEGRITY: + case STUN_ATTR_DATA: + case STUN_ATTR_MAGIC_COOKIE: + return reinterpret_cast<const StunByteStringAttribute*>(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunErrorCodeAttribute* StunMessage::GetErrorCode() const { + return reinterpret_cast<const StunErrorCodeAttribute*>( + GetAttribute(STUN_ATTR_ERROR_CODE)); +} + +const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const { + return reinterpret_cast<const StunUInt16ListAttribute*>( + GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES)); +} + +const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const { + return reinterpret_cast<const StunTransportPrefsAttribute*>( + GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES)); +} + +const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const { + for (unsigned i = 0; i < attrs_->size(); i++) { + if ((*attrs_)[i]->type() == type) + return (*attrs_)[i]; + } + return 0; +} + +bool StunMessage::Read(ByteBuffer* buf) { + if (!buf->ReadUInt16(type_)) + return false; + + if (!buf->ReadUInt16(length_)) + return false; + + std::string transaction_id; + if (!buf->ReadString(transaction_id, 16)) + return false; + assert(transaction_id.size() == 16); + transaction_id_ = transaction_id; + + if (length_ > buf->Length()) + return false; + + attrs_->resize(0); + + size_t rest = buf->Length() - length_; + while (buf->Length() > rest) { + uint16 attr_type, attr_length; + if (!buf->ReadUInt16(attr_type)) + return false; + if (!buf->ReadUInt16(attr_length)) + return false; + + StunAttribute* attr = StunAttribute::Create(attr_type, attr_length); + if (!attr || !attr->Read(buf)) + return false; + + attrs_->push_back(attr); + } + + if (buf->Length() != rest) { + // fixme: shouldn't be doing this + LOG(LERROR) << "wrong message length" + << " (" << (int)rest << " != " << (int)buf->Length() << ")"; + return false; + } + + return true; +} + +void StunMessage::Write(ByteBuffer* buf) const { + buf->WriteUInt16(type_); + buf->WriteUInt16(length_); + buf->WriteString(transaction_id_); + + for (unsigned i = 0; i < attrs_->size(); i++) { + buf->WriteUInt16((*attrs_)[i]->type()); + buf->WriteUInt16((*attrs_)[i]->length()); + (*attrs_)[i]->Write(buf); + } +} + +StunAttribute::StunAttribute(uint16 type, uint16 length) + : type_(type), length_(length) { +} + +StunAttribute* StunAttribute::Create(uint16 type, uint16 length) { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + if (length != StunAddressAttribute::SIZE) + return 0; + return new StunAddressAttribute(type); + + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + if (length != StunUInt32Attribute::SIZE) + return 0; + return new StunUInt32Attribute(type); + + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MAGIC_COOKIE: + return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0; + + case STUN_ATTR_MESSAGE_INTEGRITY: + return (length == 20) ? new StunByteStringAttribute(type, length) : 0; + + case STUN_ATTR_DATA: + return new StunByteStringAttribute(type, length); + + case STUN_ATTR_ERROR_CODE: + if (length < StunErrorCodeAttribute::MIN_SIZE) + return 0; + return new StunErrorCodeAttribute(type, length); + + case STUN_ATTR_UNKNOWN_ATTRIBUTES: + return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0; + + case STUN_ATTR_TRANSPORT_PREFERENCES: + if ((length != StunTransportPrefsAttribute::SIZE1) && + (length != StunTransportPrefsAttribute::SIZE2)) + return 0; + return new StunTransportPrefsAttribute(type, length); + + default: + return 0; + } +} + +StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + return new StunAddressAttribute(type); + + default: + assert(false); + return 0; + } +} + +StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) { + switch (type) { + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + return new StunUInt32Attribute(type); + + default: + assert(false); + return 0; + } +} + +StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) { + switch (type) { + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MESSAGE_INTEGRITY: + case STUN_ATTR_DATA: + case STUN_ATTR_MAGIC_COOKIE: + return new StunByteStringAttribute(type, 0); + + default: + assert(false); + return 0; + } +} + +StunErrorCodeAttribute* StunAttribute::CreateErrorCode() { + return new StunErrorCodeAttribute( + STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE); +} + +StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() { + return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0); +} + +StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() { + return new StunTransportPrefsAttribute( + STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1); +} + +StunAddressAttribute::StunAddressAttribute(uint16 type) + : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) { +} + +bool StunAddressAttribute::Read(ByteBuffer* buf) { + uint8 dummy; + if (!buf->ReadUInt8(dummy)) + return false; + if (!buf->ReadUInt8(family_)) + return false; + if (!buf->ReadUInt16(port_)) + return false; + if (!buf->ReadUInt32(ip_)) + return false; + return true; +} + +void StunAddressAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt8(0); + buf->WriteUInt8(family_); + buf->WriteUInt16(port_); + buf->WriteUInt32(ip_); +} + +StunUInt32Attribute::StunUInt32Attribute(uint16 type) + : StunAttribute(type, SIZE), bits_(0) { +} + +bool StunUInt32Attribute::GetBit(int index) const { + assert((0 <= index) && (index < 32)); + return static_cast<bool>((bits_ >> index) & 0x1); +} + +void StunUInt32Attribute::SetBit(int index, bool value) { + assert((0 <= index) && (index < 32)); + bits_ &= ~(1 << index); + bits_ |= value ? (1 << index) : 0; +} + +bool StunUInt32Attribute::Read(ByteBuffer* buf) { + if (!buf->ReadUInt32(bits_)) + return false; + return true; +} + +void StunUInt32Attribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32(bits_); +} + +StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length) + : StunAttribute(type, length), bytes_(0) { +} + +StunByteStringAttribute::~StunByteStringAttribute() { + delete [] bytes_; +} + +void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) { + delete [] bytes_; + bytes_ = bytes; + SetLength(length); +} + +void StunByteStringAttribute::CopyBytes(const char* bytes) { + CopyBytes(bytes, (uint16)strlen(bytes)); +} + +void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) { + char* new_bytes = new char[length]; + std::memcpy(new_bytes, bytes, length); + SetBytes(new_bytes, length); +} + +uint8 StunByteStringAttribute::GetByte(int index) const { + assert(bytes_); + assert((0 <= index) && (index < length())); + return static_cast<uint8>(bytes_[index]); +} + +void StunByteStringAttribute::SetByte(int index, uint8 value) { + assert(bytes_); + assert((0 <= index) && (index < length())); + bytes_[index] = value; +} + +bool StunByteStringAttribute::Read(ByteBuffer* buf) { + bytes_ = new char[length()]; + if (!buf->ReadBytes(bytes_, length())) + return false; + return true; +} + +void StunByteStringAttribute::Write(ByteBuffer* buf) const { + buf->WriteBytes(bytes_, length()); +} + +StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length) + : StunAttribute(type, length), class_(0), number_(0) { +} + +StunErrorCodeAttribute::~StunErrorCodeAttribute() { +} + +void StunErrorCodeAttribute::SetErrorCode(uint32 code) { + class_ = (uint8)((code >> 8) & 0x7); + number_ = (uint8)(code & 0xff); +} + +void StunErrorCodeAttribute::SetReason(const std::string& reason) { + SetLength(MIN_SIZE + (uint16)reason.size()); + reason_ = reason; +} + +bool StunErrorCodeAttribute::Read(ByteBuffer* buf) { + uint32 val; + if (!buf->ReadUInt32(val)) + return false; + + if ((val >> 11) != 0) + LOG(LERROR) << "error-code bits not zero"; + + SetErrorCode(val); + + if (!buf->ReadString(reason_, length() - 4)) + return false; + + return true; +} + +void StunErrorCodeAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32(error_code()); + buf->WriteString(reason_); +} + +StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length) + : StunAttribute(type, length) { + attr_types_ = new std::vector<uint16>(); +} + +StunUInt16ListAttribute::~StunUInt16ListAttribute() { + delete attr_types_; +} + +size_t StunUInt16ListAttribute::Size() const { + return attr_types_->size(); +} + +uint16 StunUInt16ListAttribute::GetType(int index) const { + return (*attr_types_)[index]; +} + +void StunUInt16ListAttribute::SetType(int index, uint16 value) { + (*attr_types_)[index] = value; +} + +void StunUInt16ListAttribute::AddType(uint16 value) { + attr_types_->push_back(value); + SetLength((uint16)attr_types_->size() * 2); +} + +bool StunUInt16ListAttribute::Read(ByteBuffer* buf) { + for (int i = 0; i < length() / 2; i++) { + uint16 attr; + if (!buf->ReadUInt16(attr)) + return false; + attr_types_->push_back(attr); + } + return true; +} + +void StunUInt16ListAttribute::Write(ByteBuffer* buf) const { + for (unsigned i = 0; i < attr_types_->size(); i++) + buf->WriteUInt16((*attr_types_)[i]); +} + +StunTransportPrefsAttribute::StunTransportPrefsAttribute( + uint16 type, uint16 length) + : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) { +} + +StunTransportPrefsAttribute::~StunTransportPrefsAttribute() { + delete addr_; +} + +void StunTransportPrefsAttribute::SetPreallocateAddress( + StunAddressAttribute* addr) { + if (!addr) { + preallocate_ = false; + addr_ = 0; + SetLength(SIZE1); + } else { + preallocate_ = true; + addr_ = addr; + SetLength(SIZE2); + } +} + +bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) { + uint32 val; + if (!buf->ReadUInt32(val)) + return false; + + if ((val >> 3) != 0) + LOG(LERROR) << "transport-preferences bits not zero"; + + preallocate_ = static_cast<bool>((val >> 2) & 0x1); + prefs_ = (uint8)(val & 0x3); + + if (preallocate_ && (prefs_ == 3)) + LOG(LERROR) << "transport-preferences imcompatible P and Typ"; + + if (!preallocate_) { + if (length() != StunUInt32Attribute::SIZE) + return false; + } else { + if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE) + return false; + + addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS); + addr_->Read(buf); + } + + return true; +} + +void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_); + + if (preallocate_) + addr_->Write(buf); +} + +StunMessageType GetStunResponseType(StunMessageType request_type) { + switch (request_type) { + case STUN_SHARED_SECRET_REQUEST: + return STUN_SHARED_SECRET_RESPONSE; + case STUN_ALLOCATE_REQUEST: + return STUN_ALLOCATE_RESPONSE; + case STUN_SEND_REQUEST: + return STUN_SEND_RESPONSE; + default: + return STUN_BINDING_RESPONSE; + } +} + +StunMessageType GetStunErrorResponseType(StunMessageType request_type) { + switch (request_type) { + case STUN_SHARED_SECRET_REQUEST: + return STUN_SHARED_SECRET_ERROR_RESPONSE; + case STUN_ALLOCATE_REQUEST: + return STUN_ALLOCATE_ERROR_RESPONSE; + case STUN_SEND_REQUEST: + return STUN_SEND_ERROR_RESPONSE; + default: + return STUN_BINDING_ERROR_RESPONSE; + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h new file mode 100644 index 00000000..27a8e4be --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h @@ -0,0 +1,364 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STUN_H__ +#define __STUN_H__ + +// This file contains classes for dealing with the STUN and TURN protocols. +// Both protocols use the same wire format. + +#include "talk/base/basictypes.h" +#include "talk/base/bytebuffer.h" +#include <string> +#include <vector> + +namespace cricket { + +// These are the types of STUN & TURN messages as of last check. +enum StunMessageType { + STUN_BINDING_REQUEST = 0x0001, + STUN_BINDING_RESPONSE = 0x0101, + STUN_BINDING_ERROR_RESPONSE = 0x0111, + STUN_SHARED_SECRET_REQUEST = 0x0002, + STUN_SHARED_SECRET_RESPONSE = 0x0102, + STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112, + STUN_ALLOCATE_REQUEST = 0x0003, + STUN_ALLOCATE_RESPONSE = 0x0103, + STUN_ALLOCATE_ERROR_RESPONSE = 0x0113, + STUN_SEND_REQUEST = 0x0004, + STUN_SEND_RESPONSE = 0x0104, + STUN_SEND_ERROR_RESPONSE = 0x0114, + STUN_DATA_INDICATION = 0x0115 +}; + +// These are the types of attributes defined in STUN & TURN. Next to each is +// the name of the class (T is StunTAttribute) that implements that type. +enum StunAttributeType { + STUN_ATTR_MAPPED_ADDRESS = 0x0001, // Address + STUN_ATTR_RESPONSE_ADDRESS = 0x0002, // Address + STUN_ATTR_CHANGE_REQUEST = 0x0003, // UInt32 + STUN_ATTR_SOURCE_ADDRESS = 0x0004, // Address + STUN_ATTR_CHANGED_ADDRESS = 0x0005, // Address + STUN_ATTR_USERNAME = 0x0006, // ByteString, multiple of 4 bytes + STUN_ATTR_PASSWORD = 0x0007, // ByteString, multiple of 4 bytes + STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, // ByteString, 20 bytes + STUN_ATTR_ERROR_CODE = 0x0009, // ErrorCode + STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, // UInt16List + STUN_ATTR_REFLECTED_FROM = 0x000b, // Address + STUN_ATTR_TRANSPORT_PREFERENCES = 0x000c, // TransportPrefs + STUN_ATTR_LIFETIME = 0x000d, // UInt32 + STUN_ATTR_ALTERNATE_SERVER = 0x000e, // Address + STUN_ATTR_MAGIC_COOKIE = 0x000f, // ByteString, 4 bytes + STUN_ATTR_BANDWIDTH = 0x0010, // UInt32 + STUN_ATTR_DESTINATION_ADDRESS = 0x0011, // Address + STUN_ATTR_SOURCE_ADDRESS2 = 0x0012, // Address + STUN_ATTR_DATA = 0x0013, // ByteString + STUN_ATTR_OPTIONS = 0x8001 // UInt32 +}; + +enum StunErrorCodes { + STUN_ERROR_BAD_REQUEST = 400, + STUN_ERROR_UNAUTHORIZED = 401, + STUN_ERROR_UNKNOWN_ATTRIBUTE = 420, + STUN_ERROR_STALE_CREDENTIALS = 430, + STUN_ERROR_INTEGRITY_CHECK_FAILURE = 431, + STUN_ERROR_MISSING_USERNAME = 432, + STUN_ERROR_USE_TLS = 433, + STUN_ERROR_SERVER_ERROR = 500, + STUN_ERROR_GLOBAL_FAILURE = 600 +}; + +extern const std::string STUN_ERROR_REASON_BAD_REQUEST; +extern const std::string STUN_ERROR_REASON_UNAUTHORIZED; +extern const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE; +extern const std::string STUN_ERROR_REASON_STALE_CREDENTIALS; +extern const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE; +extern const std::string STUN_ERROR_REASON_MISSING_USERNAME; +extern const std::string STUN_ERROR_REASON_USE_TLS; +extern const std::string STUN_ERROR_REASON_SERVER_ERROR; +extern const std::string STUN_ERROR_REASON_GLOBAL_FAILURE; + +class StunAttribute; +class StunAddressAttribute; +class StunUInt32Attribute; +class StunByteStringAttribute; +class StunErrorCodeAttribute; +class StunUInt16ListAttribute; +class StunTransportPrefsAttribute; + +// Records a complete STUN/TURN message. Each message consists of a type and +// any number of attributes. Each attribute is parsed into an instance of an +// appropriate class (see above). The Get* methods will return instances of +// that attribute class. +class StunMessage { +public: + StunMessage(); + ~StunMessage(); + + StunMessageType type() const { return static_cast<StunMessageType>(type_); } + uint16 length() const { return length_; } + const std::string& transaction_id() const { return transaction_id_; } + + void SetType(StunMessageType type) { type_ = type; } + void SetTransactionID(const std::string& str); + + const StunAddressAttribute* GetAddress(StunAttributeType type) const; + const StunUInt32Attribute* GetUInt32(StunAttributeType type) const; + const StunByteStringAttribute* GetByteString(StunAttributeType type) const; + const StunErrorCodeAttribute* GetErrorCode() const; + const StunUInt16ListAttribute* GetUnknownAttributes() const; + const StunTransportPrefsAttribute* GetTransportPrefs() const; + + void AddAttribute(StunAttribute* attr); + + // Parses the STUN/TURN packet in the given buffer and records it here. The + // return value indicates whether this was successful. + bool Read(ByteBuffer* buf); + + // Writes this object into a STUN/TURN packet. Return value is true if + // successful. + void Write(ByteBuffer* buf) const; + +private: + uint16 type_; + uint16 length_; + std::string transaction_id_; + std::vector<StunAttribute*>* attrs_; + + const StunAttribute* GetAttribute(StunAttributeType type) const; +}; + +// Base class for all STUN/TURN attributes. +class StunAttribute { +public: + virtual ~StunAttribute() {} + + StunAttributeType type() const { + return static_cast<StunAttributeType>(type_); + } + uint16 length() const { return length_; } + + // Reads the body (not the type or length) for this type of attribute from + // the given buffer. Return value is true if successful. + virtual bool Read(ByteBuffer* buf) = 0; + + // Writes the body (not the type or length) to the given buffer. Return + // value is true if successful. + virtual void Write(ByteBuffer* buf) const = 0; + + // Creates an attribute object with the given type and len. + static StunAttribute* Create(uint16 type, uint16 length); + + // Creates an attribute object with the given type and smallest length. + static StunAddressAttribute* CreateAddress(uint16 type); + static StunUInt32Attribute* CreateUInt32(uint16 type); + static StunByteStringAttribute* CreateByteString(uint16 type); + static StunErrorCodeAttribute* CreateErrorCode(); + static StunUInt16ListAttribute* CreateUnknownAttributes(); + static StunTransportPrefsAttribute* CreateTransportPrefs(); + +protected: + StunAttribute(uint16 type, uint16 length); + + void SetLength(uint16 length) { length_ = length; } + +private: + uint16 type_; + uint16 length_; +}; + +// Implements STUN/TURN attributes that record an Internet address. +class StunAddressAttribute : public StunAttribute { +public: + StunAddressAttribute(uint16 type); + +#if (_MSC_VER < 1300) + enum { SIZE = 8 }; +#else + static const uint16 SIZE = 8; +#endif + + uint8 family() const { return family_; } + uint16 port() const { return port_; } + uint32 ip() const { return ip_; } + + void SetFamily(uint8 family) { family_ = family; } + void SetIP(uint32 ip) { ip_ = ip; } + void SetPort(uint16 port) { port_ = port; } + + bool Read(ByteBuffer* buf); + void Write(ByteBuffer* buf) const; + +private: + uint8 family_; + uint16 port_; + uint32 ip_; +}; + +// Implements STUN/TURN attributs that record a 32-bit integer. +class StunUInt32Attribute : public StunAttribute { +public: + StunUInt32Attribute(uint16 type); + +#if (_MSC_VER < 1300) + enum { SIZE = 4 }; +#else + static const uint16 SIZE = 4; +#endif + + uint32 value() const { return bits_; } + + void SetValue(uint32 bits) { bits_ = bits; } + + bool GetBit(int index) const; + void SetBit(int index, bool value); + + bool Read(ByteBuffer* buf); + void Write(ByteBuffer* buf) const; + +private: + uint32 bits_; +}; + +// Implements STUN/TURN attributs that record an arbitrary byte string +class StunByteStringAttribute : public StunAttribute { +public: + StunByteStringAttribute(uint16 type, uint16 length); + ~StunByteStringAttribute(); + + const char* bytes() const { return bytes_; } + + void SetBytes(char* bytes, uint16 length); + + void CopyBytes(const char* bytes); // uses strlen + void CopyBytes(const void* bytes, uint16 length); + + uint8 GetByte(int index) const; + void SetByte(int index, uint8 value); + + bool Read(ByteBuffer* buf); + void Write(ByteBuffer* buf) const; + +private: + char* bytes_; +}; + +// Implements STUN/TURN attributs that record an error code. +class StunErrorCodeAttribute : public StunAttribute { +public: + StunErrorCodeAttribute(uint16 type, uint16 length); + ~StunErrorCodeAttribute(); + +#if (_MSC_VER < 1300) + enum { MIN_SIZE = 4 }; +#else + static const uint16 MIN_SIZE = 4; +#endif + + uint32 error_code() const { return (class_ << 8) | number_; } + uint8 error_class() const { return class_; } + uint8 number() const { return number_; } + const std::string& reason() const { return reason_; } + + void SetErrorCode(uint32 code); + void SetErrorClass(uint8 eclass) { class_ = eclass; } + void SetNumber(uint8 number) { number_ = number; } + void SetReason(const std::string& reason); + + bool Read(ByteBuffer* buf); + void Write(ByteBuffer* buf) const; + +private: + uint8 class_; + uint8 number_; + std::string reason_; +}; + +// Implements STUN/TURN attributs that record a list of attribute names. +class StunUInt16ListAttribute : public StunAttribute { +public: + StunUInt16ListAttribute(uint16 type, uint16 length); + ~StunUInt16ListAttribute(); + + size_t Size() const; + uint16 GetType(int index) const; + void SetType(int index, uint16 value); + void AddType(uint16 value); + + bool Read(ByteBuffer* buf); + void Write(ByteBuffer* buf) const; + +private: + std::vector<uint16>* attr_types_; +}; + +// Implements the TURN TRANSPORT-PREFS attribute, which provides information +// about the ports to allocate. +class StunTransportPrefsAttribute : public StunAttribute { +public: + StunTransportPrefsAttribute(uint16 type, uint16 length); + ~StunTransportPrefsAttribute(); + +#if (_MSC_VER < 1300) + enum { SIZE1 = 4, SIZE2 = 12 }; +#else + static const uint16 SIZE1 = 4; + static const uint16 SIZE2 = 12; +#endif + + bool preallocate() const { return preallocate_; } + uint8 preference_type() const { return prefs_; } + const StunAddressAttribute* address() const { return addr_; } + + void SetPreferenceType(uint8 prefs) { prefs_ = prefs; } + + // Sets the preallocate address to the given value, or if 0 is given, it sets + // to not preallocate. + void SetPreallocateAddress(StunAddressAttribute* addr); + + bool Read(ByteBuffer* buf); + void Write(ByteBuffer* buf) const; + +private: + bool preallocate_; + uint8 prefs_; + StunAddressAttribute* addr_; +}; + +// The special MAGIC-COOKIE attribute is used to distinguish TURN packets from +// other kinds of traffic. +const char STUN_MAGIC_COOKIE_VALUE[] = { 0x72, char(0xc6), 0x4b, char(0xc6) }; + +// Returns the (successful) response type for the given request type. +StunMessageType GetStunResponseType(StunMessageType request_type); + +// Returns the error response type for the given request type. +StunMessageType GetStunErrorResponseType(StunMessageType request_type); + +} // namespace cricket + +#endif // __STUN_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc new file mode 100644 index 00000000..6d1dc6b1 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc @@ -0,0 +1,171 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/helpers.h" +#include <iostream> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts +const int RETRY_DELAY = 50; // 50ms, from ICE spec +const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs + +// Handles a binding request sent to the STUN server. +class StunPortBindingRequest : public StunRequest { +public: + StunPortBindingRequest(StunPort* port) : port_(port) { + start_time_ = GetMillisecondCount(); + } + + virtual ~StunPortBindingRequest() { + } + + virtual void Prepare(StunMessage* request) { + request->SetType(STUN_BINDING_REQUEST); + } + + virtual void OnResponse(StunMessage* response) { + const StunAddressAttribute* addr_attr = + response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + if (!addr_attr) { + LOG(LERROR) << "Binding response missing mapped address."; + } else if (addr_attr->family() != 1) { + LOG(LERROR) << "Binding address has bad family"; + } else { + SocketAddress addr(addr_attr->ip(), addr_attr->port()); + if (port_->candidates().empty()) + port_->add_address(addr, "udp"); + } + + // We will do a keep-alive regardless of whether this request suceeds. + // This should have almost no impact on network usage. + port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY); + } + + virtual void OnErrorResponse(StunMessage* response) { + const StunErrorCodeAttribute* attr = response->GetErrorCode(); + if (!attr) { + LOG(LERROR) << "Bad allocate response error code"; + } else { + LOG(LERROR) << "Binding error response:" + << " class=" << attr->error_class() + << " number=" << attr->number() + << " reason='" << attr->reason() << "'"; + } + + if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY); + } + + virtual void OnTimeout() { + LOG(LERROR) << "Binding request timed out"; + if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + port_->requests_.SendDelayed(new StunPortBindingRequest(port_), RETRY_DELAY); + } + +private: + uint32 start_time_; + StunPort* port_; +}; + +const std::string STUN_PORT_TYPE("stun"); + +StunPort::StunPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& local_addr, + const SocketAddress& server_addr) + : UDPPort(thread, STUN_PORT_TYPE, factory, network), + server_addr_(server_addr), requests_(thread), error_(0) { + + socket_ = CreatePacketSocket(PROTO_UDP); + socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket); + if (socket_->Bind(local_addr) < 0) + PLOG(LERROR, socket_->GetError()) << "bind"; + + requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket); +} + +StunPort::~StunPort() { + delete socket_; +} + +void StunPort::PrepareAddress() { + requests_.Send(new StunPortBindingRequest(this)); +} + +int StunPort::SendTo( + const void* data, size_t size, const SocketAddress& addr, bool payload) { + int sent = socket_->SendTo(data, size, addr); + if (sent < 0) + error_ = socket_->GetError(); + return sent; +} + +int StunPort::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int StunPort::GetError() { + return error_; +} + +void StunPort::OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + + // Look for a response to a binding request. + if (requests_.CheckResponse(data, size)) + return; + + // Process this data packet in the normal manner. + UDPPort::OnReadPacket(data, size, remote_addr); +} + +void StunPort::OnSendPacket(const void* data, size_t size) { + if (socket_->SendTo(data, size, server_addr_) < 0) + PLOG(LERROR, socket_->GetError()) << "sendto"; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h new file mode 100644 index 00000000..f042ae14 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h @@ -0,0 +1,72 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STUNPORT_H__ +#define __STUNPORT_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/p2p/base/udpport.h" +#include "talk/p2p/base/stunrequest.h" + +namespace cricket { + +extern const std::string STUN_PORT_TYPE; + +// Communicates using the address on the outside of a NAT. +class StunPort : public UDPPort { +public: + StunPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& local_addr, const SocketAddress& server_addr); + virtual ~StunPort(); + + virtual void PrepareAddress(); + + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError(); + +protected: + virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload); + + void OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + +private: + AsyncPacketSocket* socket_; + SocketAddress server_addr_; + StunRequestManager requests_; + int error_; + + friend class StunPortBindingRequest; + + // Sends STUN requests to the server. + void OnSendPacket(const void* data, size_t size); +}; + +} // namespace cricket + +#endif // __STUNPORT_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc new file mode 100644 index 00000000..14d64735 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc @@ -0,0 +1,198 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/stunrequest.h" +#include "talk/p2p/base/helpers.h" +#include <iostream> +#include <cassert> + +namespace cricket { + +const uint32 MSG_STUN_SEND = 1; + +const int MAX_SENDS = 9; +const int DELAY_UNIT = 100; // 100 milliseconds +const int DELAY_MAX_FACTOR = 16; + +StunRequestManager::StunRequestManager(Thread* thread) : thread_(thread) { +} + +StunRequestManager::~StunRequestManager() { + while (requests_.begin() != requests_.end()) { + StunRequest *request = requests_.begin()->second; + requests_.erase(requests_.begin()); + delete request; + } +} + +void StunRequestManager::Send(StunRequest* request) { + SendDelayed(request, 0); +} + +void StunRequestManager::SendDelayed(StunRequest* request, int delay) { + request->set_manager(this); + assert(requests_.find(request->id()) == requests_.end()); + requests_[request->id()] = request; + thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); +} + +void StunRequestManager::Remove(StunRequest* request) { + assert(request->manager() == this); + RequestMap::iterator iter = requests_.find(request->id()); + if (iter != requests_.end()) { + assert(iter->second == request); + requests_.erase(iter); + thread_->Clear(request); + } +} + +void StunRequestManager::Clear() { + std::vector<StunRequest*> requests; + for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) + requests.push_back(i->second); + + for (uint32 i = 0; i < requests.size(); ++i) + Remove(requests[i]); +} + +bool StunRequestManager::CheckResponse(StunMessage* msg) { + RequestMap::iterator iter = requests_.find(msg->transaction_id()); + if (iter == requests_.end()) + return false; + + StunRequest* request = iter->second; + if (msg->type() == GetStunResponseType(request->type())) { + request->OnResponse(msg); + } else if (msg->type() == GetStunErrorResponseType(request->type())) { + request->OnErrorResponse(msg); + } else { + LOG(LERROR) << "Received response with wrong type: " << msg->type() + << " (expecting " << GetStunResponseType(request->type()) << ")"; + return false; + } + + delete request; + return true; +} + +bool StunRequestManager::CheckResponse(const char* data, size_t size) { + // Check the appropriate bytes of the stream to see if they match the + // transaction ID of a response we are expecting. + + if (size < 20) + return false; + + std::string id; + id.append(data + 4, 16); + + RequestMap::iterator iter = requests_.find(id); + if (iter == requests_.end()) + return false; + + // Parse the STUN message and continue processing as usual. + + ByteBuffer buf(data, size); + StunMessage msg; + if (!msg.Read(&buf)) + return false; + + return CheckResponse(&msg); +} + +StunRequest::StunRequest() + : manager_(0), id_(CreateRandomString(16)), msg_(0), count_(0), + timeout_(false), tstamp_(0) { +} + +StunRequest::StunRequest(StunMessage* request) + : manager_(0), id_(request->transaction_id()), msg_(request), + count_(0), timeout_(false) { +} + +StunRequest::~StunRequest() { + assert(manager_ != NULL); + if (manager_) { + manager_->Remove(this); + manager_->thread_->Clear(this); + } + delete msg_; +} + +const StunMessageType StunRequest::type() { + assert(msg_); + return msg_->type(); +} + +void StunRequest::set_manager(StunRequestManager* manager) { + assert(!manager_); + manager_ = manager; +} + +void StunRequest::OnMessage(Message* pmsg) { + assert(manager_); + assert(pmsg->message_id == MSG_STUN_SEND); + + if (!msg_) { + msg_ = new StunMessage(); + msg_->SetTransactionID(id_); + Prepare(msg_); + assert(msg_->transaction_id() == id_); + } + + if (timeout_) { + OnTimeout(); + delete this; + return; + } + + tstamp_ = GetMillisecondCount(); + + ByteBuffer buf; + msg_->Write(&buf); + manager_->SignalSendPacket(buf.Data(), buf.Length()); + + int delay = GetNextDelay(); + manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL); +} + +uint32 StunRequest::Elapsed() const { + return (GetMillisecondCount() - tstamp_); +} + +int StunRequest::GetNextDelay() { + int delay = DELAY_UNIT * _min(1 << count_, DELAY_MAX_FACTOR); + count_ += 1; + if (count_ == MAX_SENDS) + timeout_ = true; + return delay; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h new file mode 100644 index 00000000..86acff91 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h @@ -0,0 +1,126 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STUNREQUESTMANAGER_H__ +#define __STUNREQUESTMANAGER_H__ + +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/stun.h" +#include <map> +#include <string> + +namespace cricket { + +class StunRequest; + +// Manages a set of STUN requests, sending and resending until we receive a +// response or determine that the request has timed out. +class StunRequestManager { +public: + StunRequestManager(Thread* thread); + ~StunRequestManager(); + + // Starts sending the given request (perhaps after a delay). + void Send(StunRequest* request); + void SendDelayed(StunRequest* request, int delay); + + // Removes a stun request that was added previously. This will happen + // automatically when a request succeeds, fails, or times out. + void Remove(StunRequest* request); + + // Removes all stun requests that were added previously. + void Clear(); + + // Determines whether the given message is a response to one of the + // outstanding requests, and if so, processes it appropriately. + bool CheckResponse(StunMessage* msg); + bool CheckResponse(const char* data, size_t size); + + // Raised when there are bytes to be sent. + sigslot::signal2<const void*, size_t> SignalSendPacket; + +private: + typedef std::map<std::string, StunRequest*> RequestMap; + + Thread* thread_; + RequestMap requests_; + + friend class StunRequest; +}; + +// Represents an individual request to be sent. The STUN message can either be +// constructed beforehand or built on demand. +class StunRequest : public MessageHandler { +public: + StunRequest(); + StunRequest(StunMessage* request); + virtual ~StunRequest(); + + // The manager handling this request (if it has been scheduled for sending). + StunRequestManager* manager() { return manager_; } + + // Returns the transaction ID of this request. + const std::string& id() { return id_; } + + // Returns the STUN type of the request message. + const StunMessageType type(); + + // Handles messages for sending and timeout. + void OnMessage(Message* pmsg); + + // Time elapsed since last send (in ms) + uint32 Elapsed() const; + +protected: + int count_; + bool timeout_; + + // Fills in the actual request to be sent. Note that the transaction ID will + // already be set and cannot be changed. + virtual void Prepare(StunMessage* request) {} + + // Called when the message receives a response or times out. + virtual void OnResponse(StunMessage* response) {} + virtual void OnErrorResponse(StunMessage* response) {} + virtual void OnTimeout() {} + virtual int GetNextDelay(); + +private: + StunRequestManager* manager_; + std::string id_; + StunMessage* msg_; + uint32 tstamp_; + + void set_manager(StunRequestManager* manager); + + friend class StunRequestManager; +}; + +} // namespace cricket + +#endif // __STUNREQUESTMANAGER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc new file mode 100644 index 00000000..6e4f6b66 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc @@ -0,0 +1,160 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/bytebuffer.h" +#include "talk/p2p/base/stunserver.h" +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +StunServer::StunServer(AsyncUDPSocket* socket) : socket_(socket) { + socket_->SignalReadPacket.connect(this, &StunServer::OnPacket); +} + +StunServer::~StunServer() { + socket_->SignalReadPacket.disconnect(this); +} + +void StunServer::OnPacket( + const char* buf, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + // TODO: If appropriate, look for the magic cookie before parsing. + + // Parse the STUN message. + ByteBuffer bbuf(buf, size); + StunMessage msg; + if (!msg.Read(&bbuf)) { + SendErrorResponse(msg, remote_addr, 400, "Bad Request"); + return; + } + + // TODO: If this is UDP, then we shouldn't allow non-fully-parsed messages. + + // TODO: If unknown non-optiional (<= 0x7fff) attributes are found, send a + // 420 "Unknown Attribute" response. + + // TODO: Check that a message-integrity attribute was given (or send 401 + // "Unauthorized"). Check that a username attribute was given (or send + // 432 "Missing Username"). Look up the username and password. If it + // is missing or the HMAC is wrong, send 431 "Integrity Check Failure". + + // Send the message to the appropriate handler function. + switch (msg.type()) { + case STUN_BINDING_REQUEST: + OnBindingRequest(&msg, remote_addr); + return; + + case STUN_ALLOCATE_REQUEST: + OnAllocateRequest(&msg, remote_addr); + return; + + default: + SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported"); + } +} + +void StunServer::OnBindingRequest( + StunMessage* msg, const SocketAddress& remote_addr) { + StunMessage response; + response.SetType(STUN_BINDING_RESPONSE); + response.SetTransactionID(msg->transaction_id()); + + // Tell the user the address that we received their request from. + StunAddressAttribute* mapped_addr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + mapped_addr->SetFamily(1); + mapped_addr->SetPort(remote_addr.port()); + mapped_addr->SetIP(remote_addr.ip()); + response.AddAttribute(mapped_addr); + + // Tell the user the address that we are sending the response from. + SocketAddress local_addr = socket_->GetLocalAddress(); + StunAddressAttribute* source_addr = + StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS); + source_addr->SetFamily(1); + source_addr->SetPort(local_addr.port()); + source_addr->SetIP(local_addr.ip()); + response.AddAttribute(source_addr); + + // TODO: Add username and message-integrity. + + // TODO: Add changed-address. (Keep information about three other servers.) + + SendResponse(response, remote_addr); +} + +void StunServer::OnAllocateRequest( + StunMessage* msg, const SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::OnSharedSecretRequest( + StunMessage* msg, const SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::OnSendRequest(StunMessage* msg, const SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::SendErrorResponse( + const StunMessage& msg, const SocketAddress& addr, int error_code, + const char* error_desc) { + + StunMessage err_msg; + err_msg.SetType(GetStunErrorResponseType(msg.type())); + err_msg.SetTransactionID(msg.transaction_id()); + + StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); + err_code->SetErrorClass(error_code / 100); + err_code->SetNumber(error_code % 100); + err_code->SetReason(error_desc); + err_msg.AddAttribute(err_code); + + SendResponse(err_msg, addr); +} + +void StunServer::SendResponse( + const StunMessage& msg, const SocketAddress& addr) { + + ByteBuffer buf; + msg.Write(&buf); + + // TODO: Allow response addr attribute if sent from another stun server. + + if (socket_->SendTo(buf.Data(), buf.Length(), addr) < 0) + std::cerr << "sendto: " << std::strerror(errno) << std::endl; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h new file mode 100644 index 00000000..3043645d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STUNSERVER_H__ +#define __STUNSERVER_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/p2p/base/stun.h" + +namespace cricket { + +const int STUN_SERVER_PORT = 3478; + +class StunServer : public sigslot::has_slots<> { +public: + // Creates a STUN server, which will listen on the given socket. + StunServer(AsyncUDPSocket* socket); + + // Removes the STUN server from the socket, but does not delete the socket. + ~StunServer(); + +protected: + + // Slot for AsyncSocket.PacketRead: + void OnPacket( + const char* buf, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + + // Handlers for the different types of STUN/TURN requests: + void OnBindingRequest(StunMessage* msg, const SocketAddress& addr); + void OnAllocateRequest(StunMessage* msg, const SocketAddress& addr); + void OnSharedSecretRequest(StunMessage* msg, const SocketAddress& addr); + void OnSendRequest(StunMessage* msg, const SocketAddress& addr); + + // Sends an error response to the given message back to the user. + void SendErrorResponse( + const StunMessage& msg, const SocketAddress& addr, int error_code, + const char* error_desc); + + // Sends the given message to the appropriate destination. + void SendResponse(const StunMessage& msg, const SocketAddress& addr); + +private: + AsyncUDPSocket* socket_; +}; + +} // namespace cricket + +#endif // __STUNSERVER_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro new file mode 100644 index 00000000..dce92ec4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +INCLUDEPATH = ../../.. +DEFINES += POSIX + +include(../../../../../conf.pri) + +# Input +SOURCES += \ + stunserver.cc \ + stunserver_main.cc \ + ../../base/host.cc #\ +# ../../base/socketaddresspair.cc + +LIBS += ../../../liblibjingle.a diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc new file mode 100644 index 00000000..bd8a96e5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc @@ -0,0 +1,66 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/stunserver.h" +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +using namespace cricket; + +int main(int argc, char* argv[]) { + if (argc != 1) { + std::cerr << "usage: stunserver" << std::endl; + return 1; + } + + SocketAddress server_addr(LocalHost().networks()[1]->ip(), 7000); + + Thread *pthMain = Thread::Current(); + + AsyncUDPSocket* server_socket = CreateAsyncUDPSocket(pthMain->socketserver()); + if (server_socket->Bind(server_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + StunServer* server = new StunServer(server_socket); + + std::cout << "Listening at " << server_addr.ToString() << std::endl; + + pthMain->Loop(); + + delete server; + delete server_socket; + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc new file mode 100644 index 00000000..a2d2adc6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc @@ -0,0 +1,250 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/p2p/base/tcpport.h" +#include "talk/base/logging.h" +#ifdef WIN32 +#include "talk/base/winfirewall.h" +#endif // WIN32 +#include <iostream> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +#ifdef WIN32 +static WinFirewall win_firewall; +#endif // WIN32 + +TCPPort::TCPPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& address) + : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { + incoming_only_ = (address.port() != 0); + socket_ = thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); + socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent); + if (socket_->Bind(address) < 0) + LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); +} + +TCPPort::~TCPPort() { + delete socket_; +} + +Connection* TCPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + // We only support TCP protocols + if ((address.protocol() != "tcp") && (address.protocol() != "ssltcp")) + return 0; + + // We can't accept TCP connections incoming on other ports + if (origin == ORIGIN_OTHER_PORT) + return 0; + + // Check if we are allowed to make outgoing TCP connections + if (incoming_only_ && (origin == ORIGIN_MESSAGE)) + return 0; + + // We don't know how to act as an ssl server yet + if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT)) + return 0; + + TCPConnection* conn = 0; + if (AsyncTCPSocket * socket = GetIncoming(address.address(), true)) { + socket->SignalReadPacket.disconnect(this); + conn = new TCPConnection(this, address, socket); + } else { + conn = new TCPConnection(this, address); + } + AddConnection(conn); + return conn; +} + +void TCPPort::PrepareAddress() { + assert(socket_); + + bool allow_listen = true; +#ifdef WIN32 + if (win_firewall.Initialize()) { + char module_path[MAX_PATH + 1] = { 0 }; + ::GetModuleFileNameA(NULL, module_path, MAX_PATH); + if (win_firewall.Enabled() && !win_firewall.Authorized(module_path)) { + allow_listen = false; + } + } +#endif // WIN32 + if (allow_listen) { + if (socket_->Listen(5) < 0) + LOG(INFO) << "listen: " << std::strerror(socket_->GetError()); + } else { + LOG(INFO) << "not listening due to firewall restrictions"; + } + // Note: We still add the address, since otherwise the remote side won't recognize + // our incoming TCP connections. + add_address(socket_->GetLocalAddress(), "tcp"); +} + +int TCPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) { + AsyncTCPSocket * socket = 0; + + if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) { + socket = conn->socket(); + } else { + socket = GetIncoming(addr); + } + if (!socket) { + LOG(INFO) << "Unknown destination for SendTo: " << addr.ToString(); + return -1; // TODO: Set error_ + } + + //LOG(INFO) << "TCPPort::SendTo(" << size << ", " << addr.ToString() << ")"; + + int sent = socket->Send(data, size); + if (sent < 0) + error_ = socket->GetError(); + return sent; +} + +int TCPPort::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int TCPPort::GetError() { + assert(socket_); + return error_; +} + +void TCPPort::OnAcceptEvent(AsyncSocket* socket) { + assert(socket == socket_); + + Incoming incoming; + AsyncSocket * newsocket = static_cast<AsyncSocket *>(socket->Accept(&incoming.addr)); + if (!newsocket) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "accept: " << socket_->GetError() << " " << std::strerror(socket_->GetError()); + return; + } + incoming.socket = new AsyncTCPSocket(newsocket); + incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket); + + LOG(INFO) << "accepted incoming connection from " << incoming.addr.ToString(); + incoming_.push_back(incoming); + + // Prime a read event in case data is waiting + newsocket->SignalReadEvent(newsocket); +} + +AsyncTCPSocket * TCPPort::GetIncoming(const SocketAddress& addr, bool remove) { + AsyncTCPSocket * socket = 0; + for (std::list<Incoming>::iterator it = incoming_.begin(); it != incoming_.end(); ++it) { + if (it->addr == addr) { + socket = it->socket; + if (remove) + incoming_.erase(it); + break; + } + } + return socket; +} + +void TCPPort::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + Port::OnReadPacket(data, size, remote_addr); +} + +TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, AsyncTCPSocket* socket) + : Connection(port, 0, candidate), socket_(socket), error_(0) { + bool outgoing = (socket_ == 0); + if (outgoing) { + socket_ = static_cast<AsyncTCPSocket *>(port->CreatePacketSocket( + (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP)); + } + socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); + socket_->SignalClose.connect(this, &TCPConnection::OnClose); + if (outgoing) { + connected_ = false; + socket_->SignalConnect.connect(this, &TCPConnection::OnConnect); + socket_->Connect(candidate.address()); + LOG(INFO) << "Connecting to " << candidate.address().ToString(); + } +} + +TCPConnection::~TCPConnection() { +} + +int TCPConnection::Send(const void* data, size_t size) { + if (write_state() != STATE_WRITABLE) + return 0; + + int sent = socket_->Send(data, size); + if (sent < 0) { + error_ = socket_->GetError(); + } else { + sent_total_bytes_ += sent; + } + return sent; +} + +int TCPConnection::GetError() { + return error_; +} + +TCPPort* TCPConnection::tcpport() { + return static_cast<TCPPort*>(port_); +} + +void TCPConnection::OnConnect(AsyncTCPSocket* socket) { + assert(socket == socket_); + LOG(INFO) << "tcp connected to " << socket->GetRemoteAddress().ToString(); + set_connected(true); +} + +void TCPConnection::OnClose(AsyncTCPSocket* socket, int error) { + assert(socket == socket_); + LOG(INFO) << "tcp closed with error: " << error; + set_connected(false); +} + +void TCPConnection::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + Connection::OnReadPacket(data, size); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h new file mode 100644 index 00000000..f6a9beb7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h @@ -0,0 +1,116 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __TCPPORT_H__ +#define __TCPPORT_H__ + +#include <list> +#include "talk/base/asynctcpsocket.h" +#include "talk/p2p/base/port.h" + +namespace cricket { + +class TCPConnection; + +extern const std::string LOCAL_PORT_TYPE; // type of TCP ports + +// Communicates using a local TCP port. +// +// This class is designed to allow subclasses to take advantage of the +// connection management provided by this class. A subclass should take of all +// packet sending and preparation, but when a packet is received, it should +// call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection. +class TCPPort : public Port { +public: + TCPPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& address); + virtual ~TCPPort(); + + virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin); + + virtual void PrepareAddress(); + + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError(); + +protected: + // Handles sending using the local TCP socket. + virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload); + + // Creates TCPConnection for incoming sockets + void OnAcceptEvent(AsyncSocket* socket); + + AsyncSocket* socket() { return socket_; } + +private: + bool incoming_only_; + AsyncSocket* socket_; + int error_; + + struct Incoming { + SocketAddress addr; + AsyncTCPSocket * socket; + }; + std::list<Incoming> incoming_; + + AsyncTCPSocket * GetIncoming(const SocketAddress& addr, bool remove = false); + + // Receives packet signal from the local TCP Socket. + void OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + + friend class TCPConnection; +}; + +class TCPConnection : public Connection { +public: + // Connection is outgoing unless socket is specified + TCPConnection(TCPPort* port, const Candidate& candidate, AsyncTCPSocket* socket = 0); + virtual ~TCPConnection(); + + virtual int Send(const void* data, size_t size); + virtual int GetError(); + + AsyncTCPSocket * socket() { return socket_; } + +private: + TCPPort* tcpport(); + AsyncTCPSocket* socket_; + bool connected_; + int error_; + + void OnConnect(AsyncTCPSocket* socket); + void OnClose(AsyncTCPSocket* socket, int error); + void OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + + friend class TCPPort; +}; + +} // namespace cricket + +#endif // __TCPPORT_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc new file mode 100644 index 00000000..fabbb25b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc @@ -0,0 +1,117 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/udpport.h" +#include <iostream> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +const std::string LOCAL_PORT_TYPE("local"); + +UDPPort::UDPPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& address) + : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { + socket_ = CreatePacketSocket(PROTO_UDP); + socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacketSlot); + if (socket_->Bind(address) < 0) + PLOG(LERROR, socket_->GetError()) << "bind"; +} + +UDPPort::UDPPort(Thread* thread, const std::string &type, + SocketFactory* factory, Network* network) + : Port(thread, type, factory, network), socket_(0), error_(0) { +} + +UDPPort::~UDPPort() { + delete socket_; +} + +void UDPPort::PrepareAddress() { + assert(socket_); + add_address(socket_->GetLocalAddress(), "udp"); +} + +Connection* UDPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + if (address.protocol() != "udp") + return 0; + + Connection * conn = new ProxyConnection(this, 0, address); + AddConnection(conn); + return conn; +} + +int UDPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) { + assert(socket_); + int sent = socket_->SendTo(data, size, addr); + if (sent < 0) + error_ = socket_->GetError(); + return sent; +} + +int UDPPort::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int UDPPort::GetError() { + assert(socket_); + return error_; +} + +void UDPPort::OnReadPacketSlot( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + OnReadPacket(data, size, remote_addr); +} + +void UDPPort::OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr) { + if (Connection* conn = GetConnection(remote_addr)) { + conn->OnReadPacket(data, size); + } else { + Port::OnReadPacket(data, size, remote_addr); + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h new file mode 100644 index 00000000..4bcd113e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h @@ -0,0 +1,81 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __UDPPORT_H__ +#define __UDPPORT_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/p2p/base/port.h" + +namespace cricket { + +extern const std::string LOCAL_PORT_TYPE; // type of UDP ports + +// Communicates using a local UDP port. +// +// This class is designed to allow subclasses to take advantage of the +// connection management provided by this class. A subclass should take of all +// packet sending and preparation, but when a packet is received, it should +// call this UDPPort::OnReadPacket (3 arg) to dispatch to a connection. +class UDPPort : public Port { +public: + UDPPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& address); + virtual ~UDPPort(); + + virtual void PrepareAddress(); + virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin); + + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError(); + +protected: + UDPPort(Thread* thread, const std::string &type, SocketFactory* factory, + Network* network); + + // Handles sending using the local UDP socket. + virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload); + + // Dispatches the given packet to the port or connection as appropriate. + void OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr); + + AsyncPacketSocket* socket() { return socket_; } + +private: + AsyncPacketSocket* socket_; + int error_; + + // Receives packet signal from the local UDP Socket. + void OnReadPacketSlot( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); +}; + +} // namespace cricket + +#endif // __UDPPORT_H__ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am new file mode 100644 index 00000000..2bdd95ff --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am @@ -0,0 +1,11 @@ +libcricketp2pclient_la_SOURCES = sessionclient.cc \ + basicportallocator.cc \ + socketmonitor.cc + +noinst_HEADERS = basicportallocator.h \ + sessionclient.h \ + socketmonitor.h + +AM_CPPFLAGS = -I$(srcdir)/../../.. -DLINUX -DPOSIX -DINTERNAL_BUILD + +noinst_LTLIBRARIES = libcricketp2pclient.la diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc new file mode 100644 index 00000000..5192595c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc @@ -0,0 +1,667 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/p2p/client/basicportallocator.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/udpport.h" +#include "talk/p2p/base/tcpport.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/helpers.h" +#include <cassert> + +namespace { + +const uint32 MSG_CONFIG_START = 1; +const uint32 MSG_CONFIG_READY = 2; +const uint32 MSG_ALLOCATE = 3; +const uint32 MSG_ALLOCATION_PHASE = 4; +const uint32 MSG_SHAKE = 5; + +const uint32 ALLOCATE_DELAY = 250; +const uint32 ALLOCATION_STEP_DELAY = 1 * 1000; + +const int PHASE_UDP = 0; +const int PHASE_RELAY = 1; +const int PHASE_TCP = 2; +const int PHASE_SSLTCP = 3; +const int kNumPhases = 4; + +const float PREF_LOCAL_UDP = 1.0f; +const float PREF_LOCAL_STUN = 0.9f; +const float PREF_LOCAL_TCP = 0.8f; +const float PREF_RELAY = 0.5f; + +const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants +const float RELAY_BACKUP_PREF_MODIFIER = -0.2f; + + +// Returns the phase in which a given local candidate (or rather, the port that +// gave rise to that local candidate) would have been created. +int LocalCandidateToPhase(const cricket::Candidate& candidate) { + cricket::ProtocolType proto; + bool result = cricket::StringToProto(candidate.protocol().c_str(), proto); + if (result) { + if (candidate.type() == cricket::LOCAL_PORT_TYPE) { + switch (proto) { + case cricket::PROTO_UDP: return PHASE_UDP; + case cricket::PROTO_TCP: return PHASE_TCP; + default: assert(false); + } + } else if (candidate.type() == cricket::STUN_PORT_TYPE) { + return PHASE_UDP; + } else if (candidate.type() == cricket::RELAY_PORT_TYPE) { + switch (proto) { + case cricket::PROTO_UDP: return PHASE_RELAY; + case cricket::PROTO_TCP: return PHASE_TCP; + case cricket::PROTO_SSLTCP: return PHASE_SSLTCP; + default: assert(false); + } + } else { + assert(false); + } + } else { + assert(false); + } + return PHASE_UDP; // reached only with assert failure +} + +const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds +const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds + +int ShakeDelay() { + int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1; + return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range; +} + +} + +namespace cricket { + +// Performs the allocation of ports, in a sequenced (timed) manner, for a given +// network and IP address. +class AllocationSequence: public MessageHandler { +public: + AllocationSequence(BasicPortAllocatorSession* session, + Network* network, + PortConfiguration* config); + ~AllocationSequence(); + + // Determines whether this sequence is operating on an equivalent network + // setup to the one given. + bool IsEquivalent(Network* network); + + // Starts and stops the sequence. When started, it will continue allocating + // new ports on its own timed schedule. + void Start(); + void Stop(); + + // MessageHandler: + void OnMessage(Message* msg); + + void EnableProtocol(ProtocolType proto); + bool ProtocolEnabled(ProtocolType proto) const; + +private: + BasicPortAllocatorSession* session_; + Network* network_; + uint32 ip_; + PortConfiguration* config_; + bool running_; + int step_; + int step_of_phase_[kNumPhases]; + + typedef std::vector<ProtocolType> ProtocolList; + ProtocolList protocols_; + + void CreateUDPPorts(); + void CreateTCPPorts(); + void CreateStunPorts(); + void CreateRelayPorts(); +}; + + +// BasicPortAllocator + +BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager) + : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(NULL), relay_address_(NULL) { +} + +BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager, SocketAddress* stun_address, SocketAddress *relay_address) + : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(stun_address), relay_address_(relay_address) { +} + +BasicPortAllocator::~BasicPortAllocator() { +} + +int BasicPortAllocator::best_writable_phase() const { + // If we are configured with an HTTP proxy, the best bet is to use the relay + if ((best_writable_phase_ == -1) + && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) { + return PHASE_RELAY; + } + return best_writable_phase_; +} + +PortAllocatorSession *BasicPortAllocator::CreateSession(const std::string &name) { + return new BasicPortAllocatorSession(this, name, stun_address_, relay_address_); +} + +void BasicPortAllocator::AddWritablePhase(int phase) { + if ((best_writable_phase_ == -1) || (phase < best_writable_phase_)) + best_writable_phase_ = phase; +} + +// BasicPortAllocatorSession + +BasicPortAllocatorSession::BasicPortAllocatorSession( + BasicPortAllocator *allocator, + const std::string &name) + : allocator_(allocator), name_(name), network_thread_(NULL), + config_thread_(NULL), allocation_started_(false), running_(false), + stun_address_(NULL), relay_address_(NULL) { +} + +BasicPortAllocatorSession::BasicPortAllocatorSession( + BasicPortAllocator *allocator, + const std::string &name, + SocketAddress *stun_address, + SocketAddress *relay_address) + : allocator_(allocator), name_(name), network_thread_(NULL), + config_thread_(NULL), allocation_started_(false), running_(false), + stun_address_(stun_address), relay_address_(relay_address) { +} + +BasicPortAllocatorSession::~BasicPortAllocatorSession() { + if (config_thread_ != NULL) + config_thread_->Clear(this); + if (network_thread_ != NULL) + network_thread_->Clear(this); + + std::vector<PortData>::iterator it; + for (it = ports_.begin(); it != ports_.end(); it++) + delete it->port; + + for (uint32 i = 0; i < configs_.size(); ++i) + delete configs_[i]; + + for (uint32 i = 0; i < sequences_.size(); ++i) + delete sequences_[i]; +} + +void BasicPortAllocatorSession::GetInitialPorts() { + network_thread_ = Thread::Current(); + if (!config_thread_) + config_thread_ = network_thread_; + + config_thread_->Post(this, MSG_CONFIG_START); + + if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHAKER) + network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); +} + +void BasicPortAllocatorSession::StartGetAllPorts() { + assert(Thread::Current() == network_thread_); + running_ = true; + if (allocation_started_) + network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); + for (uint32 i = 0; i < sequences_.size(); ++i) + sequences_[i]->Start(); + for (size_t i = 0; i < ports_.size(); ++i) + ports_[i].port->Start(); +} + +void BasicPortAllocatorSession::StopGetAllPorts() { + assert(Thread::Current() == network_thread_); + running_ = false; + network_thread_->Clear(this, MSG_ALLOCATE); + for (uint32 i = 0; i < sequences_.size(); ++i) + sequences_[i]->Stop(); +} + +void BasicPortAllocatorSession::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CONFIG_START: + assert(Thread::Current() == config_thread_); + GetPortConfigurations(); + break; + + case MSG_CONFIG_READY: + assert(Thread::Current() == network_thread_); + OnConfigReady(static_cast<PortConfiguration*>(message->pdata)); + break; + + case MSG_ALLOCATE: + assert(Thread::Current() == network_thread_); + OnAllocate(); + break; + + case MSG_SHAKE: + assert(Thread::Current() == network_thread_); + OnShake(); + break; + + default: + assert(false); + } +} + +void BasicPortAllocatorSession::GetPortConfigurations() { + PortConfiguration* config = NULL; + if (stun_address_ != NULL) + config = new PortConfiguration(*stun_address_, + CreateRandomString(16), + CreateRandomString(16), + ""); + PortConfiguration::PortList ports; + if (relay_address_ != NULL) { + ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP)); + config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER); + } + + ConfigReady(config); +} + +void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) { + network_thread_->Post(this, MSG_CONFIG_READY, config); +} + +// Adds a configuration to the list. +void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) { + if (config) + configs_.push_back(config); + + AllocatePorts(); +} + +void BasicPortAllocatorSession::AllocatePorts() { + assert(Thread::Current() == network_thread_); + + if (allocator_->proxy().type != PROXY_NONE) + Port::set_proxy(allocator_->proxy()); + + network_thread_->Post(this, MSG_ALLOCATE); +} + +// For each network, see if we have a sequence that covers it already. If not, +// create a new sequence to create the appropriate ports. +void BasicPortAllocatorSession::OnAllocate() { + std::vector<Network*> networks; + allocator_->network_manager()->GetNetworks(networks); + + for (uint32 i = 0; i < networks.size(); ++i) { + if (HasEquivalentSequence(networks[i])) + continue; + + PortConfiguration* config = NULL; + if (configs_.size() > 0) + config = configs_.back(); + + AllocationSequence* sequence = + new AllocationSequence(this, networks[i], config); + if (running_) + sequence->Start(); + + sequences_.push_back(sequence); + } + + allocation_started_ = true; + if (running_) + network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); +} + +bool BasicPortAllocatorSession::HasEquivalentSequence(Network* network) { + for (uint32 i = 0; i < sequences_.size(); ++i) + if (sequences_[i]->IsEquivalent(network)) + return true; + return false; +} + +void BasicPortAllocatorSession::AddAllocatedPort(Port* port, + AllocationSequence * seq, + float pref, + bool prepare_address) { + if (!port) + return; + + port->set_name(name_); + port->set_preference(pref); + port->set_generation(generation()); + PortData data; + data.port = port; + data.sequence = seq; + data.ready = false; + ports_.push_back(data); + port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady); + port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated); + port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed); + if (prepare_address) + port->PrepareAddress(); + if (running_) + port->Start(); +} + +void BasicPortAllocatorSession::OnAddressReady(Port *port) { + assert(Thread::Current() == network_thread_); + std::vector<PortData>::iterator it = std::find(ports_.begin(), ports_.end(), port); + assert(it != ports_.end()); + assert(!it->ready); + it->ready = true; + SignalPortReady(this, port); + + // Only accumulate the candidates whose protocol has been enabled + std::vector<Candidate> candidates; + const std::vector<Candidate>& potentials = port->candidates(); + for (size_t i=0; i<potentials.size(); ++i) { + ProtocolType pvalue; + if (!StringToProto(potentials[i].protocol().c_str(), pvalue)) + continue; + if (it->sequence->ProtocolEnabled(pvalue)) { + candidates.push_back(potentials[i]); + } + } + if (!candidates.empty()) { + SignalCandidatesReady(this, candidates); + } +} + +void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto) { + std::vector<Candidate> candidates; + for (std::vector<PortData>::iterator it = ports_.begin(); it != ports_.end(); ++it) { + if (!it->ready || (it->sequence != seq)) + continue; + + const std::vector<Candidate>& potentials = it->port->candidates(); + for (size_t i=0; i<potentials.size(); ++i) { + ProtocolType pvalue; + if (!StringToProto(potentials[i].protocol().c_str(), pvalue)) + continue; + if (pvalue == proto) { + candidates.push_back(potentials[i]); + } + } + } + if (!candidates.empty()) { + SignalCandidatesReady(this, candidates); + } +} + +void BasicPortAllocatorSession::OnPortDestroyed(Port* port) { + assert(Thread::Current() == network_thread_); + std::vector<PortData>::iterator iter = + find(ports_.begin(), ports_.end(), port); + assert(iter != ports_.end()); + ports_.erase(iter); + + LOG(INFO) << "Removed port from allocator: " + << static_cast<int>(ports_.size()) << " remaining"; +} + +void BasicPortAllocatorSession::OnConnectionCreated(Port* port, Connection* conn) { + conn->SignalStateChange.connect(this, &BasicPortAllocatorSession::OnConnectionStateChange); +} + +void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) { + if (conn->write_state() == Connection::STATE_WRITABLE) + allocator_->AddWritablePhase(LocalCandidateToPhase(conn->local_candidate())); +} + +void BasicPortAllocatorSession::OnShake() { + LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<"; + + std::vector<Port*> ports; + std::vector<Connection*> connections; + + for (size_t i = 0; i < ports_.size(); ++i) { + if (ports_[i].ready) + ports.push_back(ports_[i].port); + } + + for (size_t i = 0; i < ports.size(); ++i) { + Port::AddressMap::const_iterator iter; + for (iter = ports[i]->connections().begin(); + iter != ports[i]->connections().end(); + ++iter) { + connections.push_back(iter->second); + } + } + + LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and " + << (int)connections.size() << " connections"; + + for (size_t i = 0; i < connections.size(); ++i) + connections[i]->Destroy(); + + if (running_ || (ports.size() > 0) || (connections.size() > 0)) + network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); +} + +// AllocationSequence + +AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, + Network* network, + PortConfiguration* config) + : session_(session), network_(network), ip_(network->ip()), config_(config), + running_(false), step_(0) { + + // All of the phases up until the best-writable phase so far run in step 0. + // The other phases follow sequentially in the steps after that. If there is + // no best-writable so far, then only phase 0 occurs in step 0. + int last_phase_in_step_zero = + _max(0, session->allocator()->best_writable_phase()); + for (int phase = 0; phase < kNumPhases; ++phase) + step_of_phase_[phase] = _max(0, phase - last_phase_in_step_zero); + + // Immediately perform phase 0. + OnMessage(NULL); +} + +AllocationSequence::~AllocationSequence() { + session_->network_thread()->Clear(this); +} + +bool AllocationSequence::IsEquivalent(Network* network) { + return (network == network_) && (ip_ == network->ip()); +} + +void AllocationSequence::Start() { + running_ = true; + session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, + this, + MSG_ALLOCATION_PHASE); +} + +void AllocationSequence::Stop() { + running_ = false; + session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE); +} + +void AllocationSequence::OnMessage(Message* msg) { + assert(Thread::Current() == session_->network_thread()); + if (msg) + assert(msg->message_id == MSG_ALLOCATION_PHASE); + + // Perform all of the phases in the current step. + for (int phase = 0; phase < kNumPhases; phase++) { + if (step_of_phase_[phase] != step_) + continue; + + switch (phase) { + case PHASE_UDP: + LOG(INFO) << "Phase=UDP Step=" << step_; + CreateUDPPorts(); + CreateStunPorts(); + EnableProtocol(PROTO_UDP); + break; + + case PHASE_RELAY: + LOG(INFO) << "Phase=RELAY Step=" << step_; + CreateRelayPorts(); + break; + + case PHASE_TCP: + LOG(INFO) << "Phase=TCP Step=" << step_; + CreateTCPPorts(); + EnableProtocol(PROTO_TCP); + break; + + case PHASE_SSLTCP: + LOG(INFO) << "Phase=SSLTCP Step=" << step_; + EnableProtocol(PROTO_SSLTCP); + break; + + default: + // Nothing else we can do. + return; + } + } + + // TODO: use different delays for each stage + step_ += 1; + if (running_) { + session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, + this, + MSG_ALLOCATION_PHASE); + } +} + +void AllocationSequence::EnableProtocol(ProtocolType proto) { + if (!ProtocolEnabled(proto)) { + protocols_.push_back(proto); + session_->OnProtocolEnabled(this, proto); + } +} + +bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const { + for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) { + if (*it == proto) + return true; + } + return false; +} + +void AllocationSequence::CreateUDPPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_UDP) + return; + + Port* port = new UDPPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0)); + session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP); +} + +void AllocationSequence::CreateTCPPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_TCP) + return; + + Port* port = new TCPPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0)); + session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP); +} + +void AllocationSequence::CreateStunPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_STUN) + return; + + if (!config_ || config_->stun_address.IsAny()) + return; + + Port* port = new StunPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0), config_->stun_address); + session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN); +} + +void AllocationSequence::CreateRelayPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) + return; + + if (!config_) + return; + + PortConfiguration::RelayList::const_iterator relay; + for (relay = config_->relays.begin(); + relay != config_->relays.end(); + ++relay) { + + RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0), + config_->username, config_->password, + config_->magic_cookie); + // Note: We must add the allocated port before we add addresses because + // the latter will create candidates that need name and preference + // settings. However, we also can't prepare the address (normally + // done by AddAllocatedPort) until we have these addresses. So we + // wait to do that until below. + session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier, false); + + // Add the addresses of this protocol. + PortConfiguration::PortList::const_iterator relay_port; + for (relay_port = relay->ports.begin(); + relay_port != relay->ports.end(); + ++relay_port) { + port->AddServerAddress(*relay_port); + port->AddExternalAddress(*relay_port); + } + + // Start fetching an address for this port. + port->PrepareAddress(); + } +} + +// PortConfiguration + +PortConfiguration::PortConfiguration(const SocketAddress& sa, + const std::string& un, + const std::string& pw, + const std::string& mc) + : stun_address(sa), username(un), password(pw), magic_cookie(mc) { +} + +void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) { + RelayServer relay; + relay.ports = ports; + relay.pref_modifier = pref_modifier; + relays.push_back(relay); +} + +bool PortConfiguration::SupportsProtocol( + const PortConfiguration::RelayServer& relay, ProtocolType type) { + PortConfiguration::PortList::const_iterator relay_port; + for (relay_port = relay.ports.begin(); + relay_port != relay.ports.end(); + ++relay_port) { + if (relay_port->proto == type) + return true; + } + return false; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h new file mode 100644 index 00000000..0f7b96b4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h @@ -0,0 +1,172 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BASICPORTALLOCATOR_H_ +#define _BASICPORTALLOCATOR_H_ + +#include "talk/base/thread.h" +#include "talk/base/messagequeue.h" +#include "talk/base/network.h" +#include "talk/p2p/base/portallocator.h" +#include <string> +#include <vector> + +namespace cricket { + +class BasicPortAllocator: public PortAllocator { +public: + BasicPortAllocator(NetworkManager* network_manager); + BasicPortAllocator(NetworkManager* network_manager, SocketAddress *stun_server, SocketAddress *relay_server); + virtual ~BasicPortAllocator(); + + NetworkManager* network_manager() { return network_manager_; } + + // Returns the best (highest preference) phase that has produced a port that + // produced a writable connection. If no writable connections have been + // produced, this returns -1. + int best_writable_phase() const; + + virtual PortAllocatorSession *CreateSession(const std::string &name); + + // Called whenever a connection becomes writable with the argument being the + // phase that the corresponding port was created in. + void AddWritablePhase(int phase); + +private: + NetworkManager* network_manager_; + SocketAddress* stun_address_; + SocketAddress* relay_address_; + int best_writable_phase_; +}; + +struct PortConfiguration; +class AllocationSequence; + +class BasicPortAllocatorSession: public PortAllocatorSession, public MessageHandler { +public: + BasicPortAllocatorSession(BasicPortAllocator *allocator, + const std::string &name); + BasicPortAllocatorSession(BasicPortAllocator *allocator, + const std::string &name, + SocketAddress *stun_address, + SocketAddress *relay_address); + ~BasicPortAllocatorSession(); + + BasicPortAllocator* allocator() { return allocator_; } + const std::string& name() const { return name_; } + Thread* network_thread() { return network_thread_; } + + Thread* config_thread() { return config_thread_; } + void set_config_thread(Thread* thread) { config_thread_ = thread; } + + virtual void GetInitialPorts(); + virtual void StartGetAllPorts(); + virtual void StopGetAllPorts(); + virtual bool IsGettingAllPorts() { return running_; } + +protected: + // Starts the process of getting the port configurations. + virtual void GetPortConfigurations(); + + // Adds a port configuration that is now ready. Once we have one for each + // network (or a timeout occurs), we will start allocating ports. + void ConfigReady(PortConfiguration* config); + + // MessageHandler. Can be overriden if message IDs do not conflict. + virtual void OnMessage(Message *message); + +private: + void OnConfigReady(PortConfiguration* config); + void OnConfigTimeout(); + void AllocatePorts(); + void OnAllocate(); + bool HasEquivalentSequence(Network* network); + void AddAllocatedPort(Port* port, AllocationSequence * seq, float pref, bool prepare_address = true); + void OnAddressReady(Port *port); + void OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto); + void OnPortDestroyed(Port* port); + void OnConnectionCreated(Port* port, Connection* conn); + void OnConnectionStateChange(Connection* conn); + void OnShake(); + + BasicPortAllocator *allocator_; + std::string name_; + Thread* network_thread_; + Thread* config_thread_; + bool configuration_done_; + bool allocation_started_; + bool running_; // set when StartGetAllPorts is called + std::vector<PortConfiguration*> configs_; + std::vector<AllocationSequence*> sequences_; + SocketAddress *stun_address_; + SocketAddress *relay_address_; + + struct PortData { + Port * port; + AllocationSequence * sequence; + bool ready; + + bool operator==(Port * rhs) const { return (port == rhs); } + }; + std::vector<PortData> ports_; + + friend class AllocationSequence; +}; + +// Records configuration information useful in creating ports. +struct PortConfiguration: public MessageData { + SocketAddress stun_address; + std::string username; + std::string password; + std::string magic_cookie; + + typedef std::vector<ProtocolAddress> PortList; + struct RelayServer { + PortList ports; + float pref_modifier; // added to the protocol modifier to get the + // preference for this particular server + }; + + typedef std::vector<RelayServer> RelayList; + RelayList relays; + + PortConfiguration(const SocketAddress& stun_address, + const std::string& username, + const std::string& password, + const std::string& magic_cookie); + + // Adds another relay server, with the given ports and modifier, to the list. + void AddRelay(const PortList& ports, float pref_modifier); + + // Determines whether the given relay server supports the given protocol. + static bool SupportsProtocol(const PortConfiguration::RelayServer& relay, + ProtocolType type); +}; + +} // namespace cricket + +#endif // _BASICPORTALLOCATOR_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc new file mode 100644 index 00000000..09b38a52 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc @@ -0,0 +1,545 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/p2p/client/sessionclient.h" +#include "talk/p2p/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/xmllite/qname.h" +#include "talk/xmpp/constants.h" +#include "talk/xmllite/xmlprinter.h" +#include <iostream> +#undef SetPort + +namespace { + +// We only allow usernames to be this many characters or fewer. +const size_t kMaxUsernameSize = 16; + +} + +namespace cricket { + +#if 0 +>>>>>> +<iq from="..." to="..." type="set" id="27"> + <session xmlns="http://www.google.com/session" type="initiate" id="Dr45JU8A34DF" initiator="..."> + <description xmlns="http://www.whoever.com/whatever"> + ... + </description> + </session> +</iq> + +<<<<<< +<iq from="..." to="..." type="result" id="27"/> + +>>>>>> +<iq from="..." to="..." type="set" id="28"> + <session xmlns="http://www.google.com/session" type="candidates" id="Dr45JU8A34DF" initiator="..."> + <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/> + <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/> + <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/> + </session> +</iq>> + +<<<<<< +<iq from="..." to="..." type="result" id="28"/> + +#endif + +const std::string NS_GOOGLESESSION("http://www.google.com/session"); +const buzz::QName QN_GOOGLESESSION_SESSION(true, NS_GOOGLESESSION, "session"); +const buzz::QName QN_GOOGLESESSION_CANDIDATE(true, NS_GOOGLESESSION, "candidate"); +const buzz::QName QN_GOOGLESESSION_TARGET(true, NS_GOOGLESESSION, "target"); +const buzz::QName QN_GOOGLESESSION_COOKIE(true, NS_GOOGLESESSION, "cookie"); +const buzz::QName QN_GOOGLESESSION_REGARDING(true, NS_GOOGLESESSION, "regarding"); + +const buzz::QName QN_TYPE(true, buzz::STR_EMPTY, "type"); +const buzz::QName QN_ID(true, buzz::STR_EMPTY, "id"); +const buzz::QName QN_INITIATOR(true, buzz::STR_EMPTY, "initiator"); +const buzz::QName QN_NAME(true, buzz::STR_EMPTY, "name"); +const buzz::QName QN_PORT(true, buzz::STR_EMPTY, "port"); +const buzz::QName QN_NETWORK(true, buzz::STR_EMPTY, "network"); +const buzz::QName QN_GENERATION(true, buzz::STR_EMPTY, "generation"); +const buzz::QName QN_ADDRESS(true, buzz::STR_EMPTY, "address"); +const buzz::QName QN_USERNAME(true, buzz::STR_EMPTY, "username"); +const buzz::QName QN_PASSWORD(true, buzz::STR_EMPTY, "password"); +const buzz::QName QN_PREFERENCE(true, buzz::STR_EMPTY, "preference"); +const buzz::QName QN_PROTOCOL(true, buzz::STR_EMPTY, "protocol"); +const buzz::QName QN_KEY(true, buzz::STR_EMPTY, "key"); + +class XmlCookie: public SessionMessage::Cookie { +public: + XmlCookie(const buzz::XmlElement* elem) + : elem_(new buzz::XmlElement(*elem)) { + } + + virtual ~XmlCookie() { + delete elem_; + } + + const buzz::XmlElement* elem() const { return elem_; } + + virtual Cookie* Copy() { + return new XmlCookie(elem_); + } + +private: + buzz::XmlElement* elem_; +}; + +SessionClient::SessionClient(SessionManager *session_manager) { + session_manager_ = session_manager; + session_manager_->SignalSessionCreate.connect(this, &SessionClient::OnSessionCreateSlot); + session_manager_->SignalSessionDestroy.connect(this, &SessionClient::OnSessionDestroySlot); +} + +SessionClient::~SessionClient() { +} + +void SessionClient::OnSessionCreateSlot(Session *session, bool received_initiate) { + // Does this session belong to this session client? + if (session->name() == GetSessionDescriptionName()) { + session->SignalOutgoingMessage.connect(this, &SessionClient::OnOutgoingMessage); + OnSessionCreate(session, received_initiate); + } +} + +void SessionClient::OnSessionDestroySlot(Session *session) { + if (session->name() == GetSessionDescriptionName()) { + session->SignalOutgoingMessage.disconnect(this); + OnSessionDestroy(session); + } +} + +bool SessionClient::IsClientStanza(const buzz::XmlElement *stanza) { + // Is it a IQ set stanza? + if (stanza->Name() != buzz::QN_IQ) + return false; + if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET) + return false; + + // Make sure it has the right child element + const buzz::XmlElement* element + = stanza->FirstNamed(QN_GOOGLESESSION_SESSION); + if (element == NULL) + return false; + + // Is it one of the allowed types? + std::string type; + if (element->HasAttr(QN_TYPE)) { + type = element->Attr(QN_TYPE); + if (type != "initiate" && type != "accept" && type != "modify" && + type != "candidates" && type != "reject" && type != "redirect" && + type != "terminate") { + return false; + } + } + + // Does this client own the session description namespace? + buzz::QName qn_session_desc(GetSessionDescriptionName(), "description"); + const buzz::XmlElement* description = element->FirstNamed(qn_session_desc); + if (type == "initiate" || type == "accept" || type == "modify") { + if (description == NULL) + return false; + } else { + if (description != NULL) + return false; + } + + // It's good + return true; +} + +void SessionClient::OnIncomingStanza(const buzz::XmlElement *stanza) { + SessionMessage message; + if (!ParseIncomingMessage(stanza, message)) + return; + + session_manager_->OnIncomingMessage(message); +} + +void SessionClient::OnFailedSend(const buzz::XmlElement *original_stanza, + const buzz::XmlElement *failure_stanza) { + SessionMessage message; + if (!ParseIncomingMessage(original_stanza, message)) + return; + + // Note the from/to represents the *original* stanza and not the from/to + // on any return path + session_manager_->OnIncomingError(message); +} + +bool SessionClient::ParseIncomingMessage(const buzz::XmlElement *stanza, + SessionMessage& message) { + // Parse stanza into SessionMessage + const buzz::XmlElement* element + = stanza->FirstNamed(QN_GOOGLESESSION_SESSION); + + std::string type = element->Attr(QN_TYPE); + if (type == "initiate" || type == "accept" || type == "modify") { + ParseInitiateAcceptModify(stanza, message); + } else if (type == "candidates") { + ParseCandidates(stanza, message); + } else if (type == "reject" || type == "terminate") { + ParseRejectTerminate(stanza, message); + } else if (type == "redirect") { + ParseRedirect(stanza, message); + } else { + return false; + } + + return true; +} + +void SessionClient::ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message) { + if (stanza->HasAttr(buzz::QN_FROM)) + message.set_from(stanza->Attr(buzz::QN_FROM)); + if (stanza->HasAttr(buzz::QN_TO)) + message.set_to(stanza->Attr(buzz::QN_TO)); + + const buzz::XmlElement *element + = stanza->FirstNamed(QN_GOOGLESESSION_SESSION); + if (element->HasAttr(QN_ID)) + message.session_id().set_id_str(element->Attr(QN_ID)); + + if (element->HasAttr(QN_INITIATOR)) + message.session_id().set_initiator(element->Attr(QN_INITIATOR)); + + std::string type = element->Attr(QN_TYPE); + if (type == "initiate") { + message.set_type(SessionMessage::TYPE_INITIATE); + } else if (type == "accept") { + message.set_type(SessionMessage::TYPE_ACCEPT); + } else if (type == "modify") { + message.set_type(SessionMessage::TYPE_MODIFY); + } else if (type == "candidates") { + message.set_type(SessionMessage::TYPE_CANDIDATES); + } else if (type == "reject") { + message.set_type(SessionMessage::TYPE_REJECT); + } else if (type == "redirect") { + message.set_type(SessionMessage::TYPE_REDIRECT); + } else if (type == "terminate") { + message.set_type(SessionMessage::TYPE_TERMINATE); + } else { + assert(false); + } +} + +void SessionClient::ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message) { + // Pull the standard header pieces out + ParseHeader(stanza, message); + + // Parse session description + const buzz::XmlElement *session + = stanza->FirstNamed(QN_GOOGLESESSION_SESSION); + buzz::QName qn_session_desc(GetSessionDescriptionName(), "description"); + const buzz::XmlElement* desc_elem = session->FirstNamed(qn_session_desc); + const SessionDescription *description = NULL; + if (desc_elem) + description = CreateSessionDescription(desc_elem); + message.set_name(GetSessionDescriptionName()); + message.set_description(description); +} + +void SessionClient::ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message) { + // Pull the standard header pieces out + ParseHeader(stanza, message); + + // Parse candidates and session description + std::vector<Candidate> candidates; + const buzz::XmlElement *element + = stanza->FirstNamed(QN_GOOGLESESSION_SESSION); + const buzz::XmlElement *child = element->FirstElement(); + while (child != NULL) { + if (child->Name() == QN_GOOGLESESSION_CANDIDATE) { + Candidate candidate; + if (ParseCandidate(child, &candidate)) + candidates.push_back(candidate); + } + child = child->NextElement(); + } + message.set_name(GetSessionDescriptionName()); + message.set_candidates(candidates); +} + +void SessionClient::ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message) { + // Reject and terminate are very simple + ParseHeader(stanza, message); +} + +bool SessionClient::ParseCandidate(const buzz::XmlElement *child, + Candidate* candidate) { + // Check for all of the required attributes. + if (!child->HasAttr(QN_NAME) || + !child->HasAttr(QN_ADDRESS) || + !child->HasAttr(QN_PORT) || + !child->HasAttr(QN_USERNAME) || + !child->HasAttr(QN_PREFERENCE) || + !child->HasAttr(QN_PROTOCOL) || + !child->HasAttr(QN_GENERATION)) { + LOG(LERROR) << "Candidate missing required attribute"; + return false; + } + + SocketAddress address; + address.SetIP(child->Attr(QN_ADDRESS)); + std::istringstream ist(child->Attr(QN_PORT)); + int port; + ist >> port; + address.SetPort(port); + + if (address.IsAny()) { + LOG(LERROR) << "Candidate has address 0"; + return false; + } + + // Always disallow addresses that refer to the local host. + if (address.IsLocalIP()) { + LOG(LERROR) << "Candidate has local IP address"; + return false; + } + + // Disallow all ports below 1024, except for 80 and 443 on public addresses. + if (port < 1024) { + if ((port != 80) && (port != 443)) { + LOG(LERROR) << "Candidate has port below 1024, not 80 or 443"; + return false; + } + if (address.IsPrivateIP()) { + LOG(LERROR) << "Candidate has port of 80 or 443 with private IP address"; + return false; + } + } + + candidate->set_name(child->Attr(QN_NAME)); + candidate->set_address(address); + candidate->set_username(child->Attr(QN_USERNAME)); + candidate->set_preference_str(child->Attr(QN_PREFERENCE)); + candidate->set_protocol(child->Attr(QN_PROTOCOL)); + candidate->set_generation_str(child->Attr(QN_GENERATION)); + + // Check that the username is not too long and does not use any bad chars. + if (candidate->username().size() > kMaxUsernameSize) { + LOG(LERROR) << "Candidate username is too long"; + return false; + } + if (!IsBase64Encoded(candidate->username())) { + LOG(LERROR) << "Candidate username has non-base64 encoded characters"; + return false; + } + + // Look for the non-required attributes. + if (child->HasAttr(QN_PASSWORD)) + candidate->set_password(child->Attr(QN_PASSWORD)); + if (child->HasAttr(QN_TYPE)) + candidate->set_type(child->Attr(QN_TYPE)); + if (child->HasAttr(QN_NETWORK)) + candidate->set_network_name(child->Attr(QN_NETWORK)); + + return true; +} + +void SessionClient::ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message) { + // Pull the standard header pieces out + ParseHeader(stanza, message); + const buzz::XmlElement *session = stanza->FirstNamed(QN_GOOGLESESSION_SESSION); + + // Parse the target and cookie. + + const buzz::XmlElement* target = session->FirstNamed(QN_GOOGLESESSION_TARGET); + if (target) + message.set_redirect_target(target->Attr(QN_NAME)); + + const buzz::XmlElement* cookie = session->FirstNamed(QN_GOOGLESESSION_COOKIE); + if (cookie) + message.set_redirect_cookie(new XmlCookie(cookie)); +} + +void SessionClient::OnOutgoingMessage(Session *session, const SessionMessage &message) { + // Translate the message into an XMPP stanza + + buzz::XmlElement *result = NULL; + switch (message.type()) { + case SessionMessage::TYPE_INITIATE: + case SessionMessage::TYPE_ACCEPT: + case SessionMessage::TYPE_MODIFY: + result = TranslateInitiateAcceptModify(message); + break; + + case SessionMessage::TYPE_CANDIDATES: + result = TranslateCandidates(message); + break; + + case SessionMessage::TYPE_REJECT: + case SessionMessage::TYPE_TERMINATE: + result = TranslateRejectTerminate(message); + break; + + case SessionMessage::TYPE_REDIRECT: + result = TranslateRedirect(message); + break; + } + + // Send the stanza. Note that SessionClient is passing on ownership + // of result. + if (result != NULL) { + SignalSendStanza(this, result); + } +} + +buzz::XmlElement *SessionClient::TranslateHeader(const SessionMessage &message) { + buzz::XmlElement *result = new buzz::XmlElement(buzz::QN_IQ); + result->AddAttr(buzz::QN_TO, message.to()); + result->AddAttr(buzz::QN_TYPE, buzz::STR_SET); + buzz::XmlElement *session = new buzz::XmlElement(QN_GOOGLESESSION_SESSION, true); + result->AddElement(session); + switch (message.type()) { + case SessionMessage::TYPE_INITIATE: + session->AddAttr(QN_TYPE, "initiate"); + break; + case SessionMessage::TYPE_ACCEPT: + session->AddAttr(QN_TYPE, "accept"); + break; + case SessionMessage::TYPE_MODIFY: + session->AddAttr(QN_TYPE, "modify"); + break; + case SessionMessage::TYPE_CANDIDATES: + session->AddAttr(QN_TYPE, "candidates"); + break; + case SessionMessage::TYPE_REJECT: + session->AddAttr(QN_TYPE, "reject"); + break; + case SessionMessage::TYPE_REDIRECT: + session->AddAttr(QN_TYPE, "redirect"); + break; + case SessionMessage::TYPE_TERMINATE: + session->AddAttr(QN_TYPE, "terminate"); + break; + } + session->AddAttr(QN_ID, message.session_id().id_str()); + session->AddAttr(QN_INITIATOR, message.session_id().initiator()); + return result; +} + +buzz::XmlElement *SessionClient::TranslateCandidate(const Candidate &candidate) { + buzz::XmlElement *result = new buzz::XmlElement(QN_GOOGLESESSION_CANDIDATE); + result->AddAttr(QN_NAME, candidate.name()); + result->AddAttr(QN_ADDRESS, candidate.address().IPAsString()); + result->AddAttr(QN_PORT, candidate.address().PortAsString()); + result->AddAttr(QN_USERNAME, candidate.username()); + result->AddAttr(QN_PASSWORD, candidate.password()); + result->AddAttr(QN_PREFERENCE, candidate.preference_str()); + result->AddAttr(QN_PROTOCOL, candidate.protocol()); + result->AddAttr(QN_TYPE, candidate.type()); + result->AddAttr(QN_NETWORK, candidate.network_name()); + result->AddAttr(QN_GENERATION, candidate.generation_str()); + return result; +} + +buzz::XmlElement *SessionClient::TranslateInitiateAcceptModify(const SessionMessage &message) { + // Header info common to all message types + buzz::XmlElement *result = TranslateHeader(message); + buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION); + + // Candidates + assert(message.candidates().size() == 0); + + // Session Description + buzz::XmlElement* description = TranslateSessionDescription(message.description()); + assert(description->Name().LocalPart() == "description"); + assert(description->Name().Namespace() == GetSessionDescriptionName()); + session->AddElement(description); + + if (message.redirect_cookie() != NULL) { + const buzz::XmlElement* cookie = + reinterpret_cast<XmlCookie*>(message.redirect_cookie())->elem(); + for (const buzz::XmlElement* elem = cookie->FirstElement(); elem; elem = elem->NextElement()) + session->AddElement(new buzz::XmlElement(*elem)); + } + + return result; +} + +buzz::XmlElement *SessionClient::TranslateCandidates(const SessionMessage &message) { + // Header info common to all message types + buzz::XmlElement *result = TranslateHeader(message); + buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION); + + // Candidates + std::vector<Candidate>::const_iterator it; + for (it = message.candidates().begin(); it != message.candidates().end(); it++) + session->AddElement(TranslateCandidate(*it)); + + return result; +} + +buzz::XmlElement *SessionClient::TranslateRejectTerminate(const SessionMessage &message) { + // These messages are simple, and only have a header + return TranslateHeader(message); +} + +buzz::XmlElement *SessionClient::TranslateRedirect(const SessionMessage &message) { + // Header info common to all message types + buzz::XmlElement *result = TranslateHeader(message); + buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION); + + assert(message.candidates().size() == 0); + assert(message.description() == NULL); + + assert(message.redirect_target().size() > 0); + buzz::XmlElement* target = new buzz::XmlElement(QN_GOOGLESESSION_TARGET); + target->AddAttr(QN_NAME, message.redirect_target()); + session->AddElement(target); + + buzz::XmlElement* cookie = new buzz::XmlElement(QN_GOOGLESESSION_COOKIE); + session->AddElement(cookie); + + // If the message does not have a redirect cookie, then this is a redirect + // initiated by us. We will automatically add a regarding cookie. + if (message.redirect_cookie() == NULL) { + buzz::XmlElement* regarding = new buzz::XmlElement(QN_GOOGLESESSION_REGARDING); + regarding->AddAttr(QN_NAME, GetJid().BareJid().Str()); + cookie->AddElement(regarding); + } else { + const buzz::XmlElement* cookie_elem = + reinterpret_cast<const XmlCookie*>(message.redirect_cookie())->elem(); + const buzz::XmlElement* elem; + for (elem = cookie_elem->FirstElement(); elem; elem = elem->NextElement()) + cookie->AddElement(new buzz::XmlElement(*elem)); + } + + return result; +} + +SessionManager *SessionClient::session_manager() { + return session_manager_; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h new file mode 100644 index 00000000..69a18422 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h @@ -0,0 +1,104 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SESSIONCLIENT_H_ +#define _SESSIONCLIENT_H_ + +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/base/sessionmessage.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/jid.h" +namespace cricket { + +// Generic XMPP session client. This class knows how to translate +// a SessionMessage to and from XMPP stanzas. The SessionDescription +// is a custom description implemented by the client. + +// This class knows how to talk to the session manager, however the +// session manager doesn't have knowledge of a particular SessionClient. + +class SessionClient : public sigslot::has_slots<> { +public: + SessionClient(SessionManager *psm); + virtual ~SessionClient(); + + // Call this method to determine if a stanza is for this session client + bool IsClientStanza(const buzz::XmlElement *stanza); + + // Call this method to deliver a stanza to this session client + void OnIncomingStanza(const buzz::XmlElement *stanza); + + // Call this whenever an error is recieved in response to an outgoing + // session IQ. Include the original stanza and any failure stanza. If + // the failure is due to a time out, the failure_stanza should be NULL + void OnFailedSend(const buzz::XmlElement* original_stanza, + const buzz::XmlElement* failure_stanza); + + SessionManager *session_manager(); + + // Implement this method for stanza sending + sigslot::signal2<SessionClient*, const buzz::XmlElement*> SignalSendStanza; + +protected: + // Override these to know when sessions belonging to this client create/destroy + + virtual void OnSessionCreate(Session * /*session*/, bool /*received_initiate*/) {} + virtual void OnSessionDestroy(Session * /*session*/) {} + + // Implement these methods for a custom session description + virtual const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element) = 0; + virtual buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description) = 0; + virtual const std::string &GetSessionDescriptionName() = 0; + virtual const buzz::Jid &GetJid() const = 0; + + SessionManager *session_manager_; + +private: + void OnSessionCreateSlot(Session *session, bool received_initiate); + void OnSessionDestroySlot(Session *session); + void OnOutgoingMessage(Session *session, const SessionMessage &message); + void ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message); + bool ParseCandidate(const buzz::XmlElement *child, Candidate* candidate); + bool ParseIncomingMessage(const buzz::XmlElement *stanza, + SessionMessage& message); + void ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message); + void ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message); + void ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message); + void ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message); + buzz::XmlElement *TranslateHeader(const SessionMessage &message); + buzz::XmlElement *TranslateCandidate(const Candidate &candidate); + buzz::XmlElement *TranslateInitiateAcceptModify(const SessionMessage &message); + buzz::XmlElement *TranslateCandidates(const SessionMessage &message); + buzz::XmlElement *TranslateRejectTerminate(const SessionMessage &message); + buzz::XmlElement *TranslateRedirect(const SessionMessage &message); + +}; + +} // namespace cricket + +#endif // _SESSIONCLIENT_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc new file mode 100644 index 00000000..dd9fa67c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc @@ -0,0 +1,149 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "socketmonitor.h" +#include <cassert> + +namespace cricket { + +const uint32 MSG_MONITOR_POLL = 1; +const uint32 MSG_MONITOR_START = 2; +const uint32 MSG_MONITOR_STOP = 3; +const uint32 MSG_MONITOR_SIGNAL = 4; + +SocketMonitor::SocketMonitor(P2PSocket *socket, Thread *monitor_thread) { + socket_ = socket; + monitoring_thread_ = monitor_thread; + monitoring_ = false; +} + +SocketMonitor::~SocketMonitor() { + socket_->thread()->Clear(this); + monitoring_thread_->Clear(this); +} + +void SocketMonitor::Start(int milliseconds) { + rate_ = milliseconds; + if (rate_ < 250) + rate_ = 250; + socket_->thread()->Post(this, MSG_MONITOR_START); +} + +void SocketMonitor::Stop() { + socket_->thread()->Post(this, MSG_MONITOR_STOP); +} + +void SocketMonitor::OnMessage(Message *message) { + CritScope cs(&crit_); + + switch (message->message_id) { + case MSG_MONITOR_START: + assert(Thread::Current() == socket_->thread()); + if (!monitoring_) { + monitoring_ = true; + socket_->SignalConnectionMonitor.connect(this, &SocketMonitor::OnConnectionMonitor); + PollSocket(true); + } + break; + + case MSG_MONITOR_STOP: + assert(Thread::Current() == socket_->thread()); + if (monitoring_) { + monitoring_ = false; + socket_->SignalConnectionMonitor.disconnect(this); + socket_->thread()->Clear(this); + } + break; + + case MSG_MONITOR_POLL: + assert(Thread::Current() == socket_->thread()); + PollSocket(true); + break; + + case MSG_MONITOR_SIGNAL: + { + assert(Thread::Current() == monitoring_thread_); + std::vector<ConnectionInfo> infos = connection_infos_; + crit_.Leave(); + SignalUpdate(this, infos); + crit_.Enter(); + } + break; + } +} + +void SocketMonitor::OnConnectionMonitor(P2PSocket *socket) { + CritScope cs(&crit_); + if (monitoring_) + PollSocket(false); +} + +void SocketMonitor::PollSocket(bool poll) { + CritScope cs(&crit_); + assert(Thread::Current() == socket_->thread()); + + // Gather connection infos + + connection_infos_.clear(); + const std::vector<Connection *> &connections = socket_->connections(); + std::vector<Connection *>::const_iterator it; + for (it = connections.begin(); it != connections.end(); it++) { + Connection *connection = *it; + ConnectionInfo info; + info.best_connection = socket_->best_connection() == connection; + info.readable = connection->read_state() == Connection::STATE_READABLE; + info.writable = connection->write_state() == Connection::STATE_WRITABLE; + info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT; + info.new_connection = false; // connection->new_connection(); + info.rtt = connection->rtt(); + info.sent_total_bytes = connection->sent_total_bytes(); + info.sent_bytes_second = connection->sent_bytes_second(); + info.recv_total_bytes = connection->recv_total_bytes(); + info.recv_bytes_second = connection->recv_bytes_second(); + info.local_candidate = connection->local_candidate(); + info.remote_candidate = connection->remote_candidate(); + info.est_quality = connection->port()->network()->quality(); + info.key = reinterpret_cast<void *>(connection); + connection_infos_.push_back(info); + } + + // Signal the monitoring thread, start another poll timer + + monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); + if (poll) + socket_->thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); +} + +P2PSocket *SocketMonitor::socket() { + return socket_; +} + +Thread *SocketMonitor::monitor_thread() { + return monitoring_thread_; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h new file mode 100644 index 00000000..549e90b6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SOCKETMONITOR_H_ +#define _SOCKETMONITOR_H_ + +#include "talk/base/thread.h" +#include "talk/base/sigslot.h" +#include "talk/base/criticalsection.h" +#include "talk/p2p/base/p2psocket.h" +#include "talk/p2p/base/port.h" +#include <vector> + +namespace cricket { + +struct ConnectionInfo { + bool best_connection; + bool writable; + bool readable; + bool timeout; + bool new_connection; + size_t rtt; + size_t sent_total_bytes; + size_t sent_bytes_second; + size_t recv_total_bytes; + size_t recv_bytes_second; + Candidate local_candidate; + Candidate remote_candidate; + double est_quality; + void *key; +}; + +class SocketMonitor : public MessageHandler, public sigslot::has_slots<> { +public: + SocketMonitor(P2PSocket *socket, Thread *monitor_thread); + ~SocketMonitor(); + + void Start(int cms); + void Stop(); + + P2PSocket *socket(); + Thread *monitor_thread(); + + sigslot::signal2<SocketMonitor *, const std::vector<ConnectionInfo> &> SignalUpdate; + +protected: + void OnMessage(Message *message); + void OnConnectionMonitor(P2PSocket *socket); + void PollSocket(bool poll); + + std::vector<ConnectionInfo> connection_infos_; + P2PSocket *socket_; + Thread *monitoring_thread_; + CriticalSection crit_; + uint32 rate_; + bool monitoring_; +}; + +} + +#endif // _SOCKETMONITOR_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am new file mode 100644 index 00000000..6cfc5b24 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am @@ -0,0 +1,3 @@ +noinst_HEADERS = receiver.h sessionsendtask.h +SUBDIRS = phone + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am new file mode 100644 index 00000000..b2acbf81 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am @@ -0,0 +1,18 @@ +libcricketsessionphone_la_SOURCES = audiomonitor.cc \ + channelmanager.cc \ + voicechannel.cc \ + call.cc \ + phonesessionclient.cc \ + linphonemediaengine.cc + +noinst_HEADERS = audiomonitor.h \ + channelmanager.h \ + linphonemediaengine.h \ + mediaengine.h \ + phonesessionclient.h \ + voicechannel.h \ + call.h \ + mediachannel.h + +AM_CPPFLAGS = -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(srcdir)/../../../talk/third_party/mediastreamer -I$(srcdir)/../../.. $(GLIB_CFLAGS) $(SPEEX_CFLAGS) +noinst_LTLIBRARIES = libcricketsessionphone.la diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc new file mode 100644 index 00000000..c1b63d1b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc @@ -0,0 +1,119 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/session/phone/audiomonitor.h" +#include "talk/session/phone/voicechannel.h" +#include <cassert> + +namespace cricket { + +const uint32 MSG_MONITOR_POLL = 1; +const uint32 MSG_MONITOR_START = 2; +const uint32 MSG_MONITOR_STOP = 3; +const uint32 MSG_MONITOR_SIGNAL = 4; + +AudioMonitor::AudioMonitor(VoiceChannel *voice_channel, Thread *monitor_thread) { + voice_channel_ = voice_channel; + monitoring_thread_ = monitor_thread; + monitoring_ = false; +} + +AudioMonitor::~AudioMonitor() { + voice_channel_->worker_thread()->Clear(this); + monitoring_thread_->Clear(this); +} + +void AudioMonitor::Start(int milliseconds) { + rate_ = milliseconds; + if (rate_ < 100) + rate_ = 100; + voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START); +} + +void AudioMonitor::Stop() { + voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP); +} + +void AudioMonitor::OnMessage(Message *message) { + CritScope cs(&crit_); + + switch (message->message_id) { + case MSG_MONITOR_START: + assert(Thread::Current() == voice_channel_->worker_thread()); + if (!monitoring_) { + monitoring_ = true; + PollVoiceChannel(); + } + break; + + case MSG_MONITOR_STOP: + assert(Thread::Current() == voice_channel_->worker_thread()); + if (monitoring_) { + monitoring_ = false; + voice_channel_->worker_thread()->Clear(this); + } + break; + + case MSG_MONITOR_POLL: + assert(Thread::Current() == voice_channel_->worker_thread()); + PollVoiceChannel(); + break; + + case MSG_MONITOR_SIGNAL: + { + assert(Thread::Current() == monitoring_thread_); + AudioInfo info = audio_info_; + crit_.Leave(); + SignalUpdate(this, audio_info_); + crit_.Enter(); + } + break; + } +} + +void AudioMonitor::PollVoiceChannel() { + CritScope cs(&crit_); + assert(Thread::Current() == voice_channel_->worker_thread()); + + // Gather connection infos + audio_info_.input_level = voice_channel_->GetInputLevel_w(); + audio_info_.output_level = voice_channel_->GetOutputLevel_w(); + + // Signal the monitoring thread, start another poll timer + monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); + voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); +} + +VoiceChannel *AudioMonitor::voice_channel() { + return voice_channel_; +} + +Thread *AudioMonitor::monitor_thread() { + return monitoring_thread_; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h new file mode 100644 index 00000000..96b95bd7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CRICKET_PHONE_AUDIOMONITOR_H_ +#define _CRICKET_PHONE_AUDIOMONITOR_H_ + +#include "talk/base/thread.h" +#include "talk/base/sigslot.h" +#include "talk/p2p/base/port.h" +#include <vector> + +namespace cricket { + +class VoiceChannel; + + +struct AudioInfo { + int input_level; + int output_level; +}; + +class AudioMonitor : public MessageHandler, public sigslot::has_slots<> { +public: + AudioMonitor(VoiceChannel* voice_channel, Thread *monitor_thread); + ~AudioMonitor(); + + void Start(int cms); + void Stop(); + + VoiceChannel* voice_channel(); + Thread *monitor_thread(); + + sigslot::signal2<AudioMonitor*, const AudioInfo&> SignalUpdate; + +protected: + void OnMessage(Message *message); + void PollVoiceChannel(); + + AudioInfo audio_info_; + VoiceChannel* voice_channel_; + Thread* monitoring_thread_; + CriticalSection crit_; + uint32 rate_; + bool monitoring_; +}; + +} + +#endif // _CRICKET_PHONE_AUDIOMONITOR_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc new file mode 100644 index 00000000..31b12e92 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc @@ -0,0 +1,258 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/thread.h" +#include "talk/p2p/base/helpers.h" +#include "talk/session/phone/call.h" + +namespace cricket { + +const uint32 MSG_CHECKAUTODESTROY = 1; + +Call::Call(PhoneSessionClient *session_client) : muted_(false) { + session_client_ = session_client; + id_ = CreateRandomId(); +} + +Call::~Call() { + while (sessions_.begin() != sessions_.end()) { + Session *session = sessions_[0]; + RemoveSession(session); + session_client_->session_manager()->DestroySession(session); + } + Thread::Current()->Clear(this); +} + +Session *Call::InitiateSession(const buzz::Jid &jid) { + Session *session = session_client_->CreateSession(this); + AddSession(session); + session->Initiate(jid.Str(), session_client_->CreateOfferSessionDescription()); + return session; +} + +void Call::AcceptSession(Session *session) { + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Accept(session_client_->CreateAcceptSessionDescription(session->remote_description())); +} + +void Call::RedirectSession(Session *session, const buzz::Jid &to) { + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Redirect(to.Str()); +} + +void Call::RejectSession(Session *session) { + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Reject(); +} + +void Call::TerminateSession(Session *session) { + assert(std::find(sessions_.begin(), sessions_.end(), session) != sessions_.end()); + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + if (it != sessions_.end()) + (*it)->Terminate(); +} + +void Call::Terminate() { + // There may be more than one session to terminate + std::vector<Session *>::iterator it = sessions_.begin(); + for (it = sessions_.begin(); it != sessions_.end(); it++) + TerminateSession(*it); +} + +void Call::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CHECKAUTODESTROY: + // If no more sessions for this call, delete it + if (sessions_.size() == 0) + session_client_->DestroyCall(this); + break; + } +} + +const std::vector<Session *> &Call::sessions() { + return sessions_; +} + +void Call::AddSession(Session *session) { + // Add session to list, create voice channel for this session + sessions_.push_back(session); + session->SignalState.connect(this, &Call::OnSessionState); + session->SignalError.connect(this, &Call::OnSessionError); + + VoiceChannel *channel = session_client_->channel_manager()->CreateVoiceChannel(session); + channel_map_[session->id()] = channel; + + // If this call has the focus, enable this channel + if (session_client_->GetFocus() == this) + channel->Enable(true); + + // Signal client + SignalAddSession(this, session); +} + +void Call::RemoveSession(Session *session) { + // Remove session from list + std::vector<Session *>::iterator it_session; + it_session = std::find(sessions_.begin(), sessions_.end(), session); + if (it_session == sessions_.end()) + return; + sessions_.erase(it_session); + + // Destroy session channel + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = it_channel->second; + channel_map_.erase(it_channel); + session_client_->channel_manager()->DestroyVoiceChannel(channel); + } + + // Signal client + SignalRemoveSession(this, session); + + // The call auto destroys when the lass session is removed + Thread::Current()->Post(this, MSG_CHECKAUTODESTROY); +} + +VoiceChannel* Call::GetChannel(Session* session) { + std::map<SessionID, VoiceChannel *>::iterator it = channel_map_.find(session->id()); + assert(it != channel_map_.end()); + return it->second; +} + +void Call::EnableChannels(bool enable) { + std::vector<Session *>::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); it++) { + VoiceChannel *channel = channel_map_[(*it)->id()]; + if (channel != NULL) + channel->Enable(enable); + } +} + +void Call::Mute(bool mute) { + muted_ = mute; + std::vector<Session *>::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); it++) { + VoiceChannel *channel = channel_map_[(*it)->id()]; + if (channel != NULL) + channel->Mute(mute); + } +} + +void Call::Join(Call *call, bool enable) { + while (call->sessions_.size() != 0) { + // Move session + Session *session = call->sessions_[0]; + call->sessions_.erase(call->sessions_.begin()); + sessions_.push_back(session); + session->SignalState.connect(this, &Call::OnSessionState); + session->SignalError.connect(this, &Call::OnSessionError); + + // Move channel + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = call->channel_map_.find(session->id()); + if (it_channel != call->channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + call->channel_map_.erase(it_channel); + channel_map_[session->id()] = channel; + channel->Enable(enable); + } + } +} + +void Call::StartConnectionMonitor(Session *session, int cms) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor); + channel->StartConnectionMonitor(cms); + } +} + +void Call::StopConnectionMonitor(Session *session) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->StopConnectionMonitor(); + channel->SignalConnectionMonitor.disconnect(this); + } +} + +void Call::StartAudioMonitor(Session *session, int cms) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor); + channel->StartAudioMonitor(cms); + } +} + +void Call::StopAudioMonitor(Session *session) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->StopAudioMonitor(); + channel->SignalAudioMonitor.disconnect(this); + } +} + + +void Call::OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos) { + SignalConnectionMonitor(this, channel->session(), infos); +} + +void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) { + SignalAudioMonitor(this, channel->session(), info); +} + +uint32 Call::id() { + return id_; +} + +void Call::OnSessionState(Session *session, Session::State state) { + SignalSessionState(this, session, state); +} + +void Call::OnSessionError(Session *session, Session::Error error) { + SignalSessionError(this, session, error); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h new file mode 100644 index 00000000..209e13c9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h @@ -0,0 +1,97 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CALL_H_ +#define _CALL_H_ + +#include "talk/base/messagequeue.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/client/socketmonitor.h" +#include "talk/xmpp/jid.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/session/phone/voicechannel.h" +#include "talk/session/phone/audiomonitor.h" + +#include <map> +#include <vector> + +namespace cricket { + +class PhoneSessionClient; + +class Call : public MessageHandler, public sigslot::has_slots<> { +public: + Call(PhoneSessionClient *session_client); + ~Call(); + + Session *InitiateSession(const buzz::Jid &jid); + void AcceptSession(Session *session); + void RedirectSession(Session *session, const buzz::Jid &to); + void RejectSession(Session *session); + void TerminateSession(Session *session); + void Terminate(); + void StartConnectionMonitor(Session *session, int cms); + void StopConnectionMonitor(Session *session); + void StartAudioMonitor(Session *session, int cms); + void StopAudioMonitor(Session *session); + void Mute(bool mute); + + const std::vector<Session *> &sessions(); + uint32 id(); + bool muted() const { return muted_; } + + sigslot::signal2<Call *, Session *> SignalAddSession; + sigslot::signal2<Call *, Session *> SignalRemoveSession; + sigslot::signal3<Call *, Session *, Session::State> SignalSessionState; + sigslot::signal3<Call *, Session *, Session::Error> SignalSessionError; + sigslot::signal3<Call *, Session *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor; + sigslot::signal3<Call *, Session *, const AudioInfo&> SignalAudioMonitor; + +private: + void OnMessage(Message *message); + void OnSessionState(Session *session, Session::State state); + void OnSessionError(Session *session, Session::Error error); + void AddSession(Session *session); + void RemoveSession(Session *session); + void EnableChannels(bool enable); + void Join(Call *call, bool enable); + void OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos); + void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info); + VoiceChannel* GetChannel(Session* session); + + uint32 id_; + PhoneSessionClient *session_client_; + std::vector<Session *> sessions_; + std::map<SessionID, VoiceChannel *> channel_map_; + bool muted_; + + friend class PhoneSessionClient; +}; + +} + +#endif // _CALL_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc new file mode 100644 index 00000000..98634b12 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc @@ -0,0 +1,203 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#ifdef HAVE_GIPS +#include "talk/session/phone/gipsmediaengine.h" +#else +#include "talk/session/phone/linphonemediaengine.h" +#endif +#include "channelmanager.h" +#include <cassert> +#include <iostream> +namespace cricket { + +const uint32 MSG_CREATEVOICECHANNEL = 1; +const uint32 MSG_DESTROYVOICECHANNEL = 2; +const uint32 MSG_SETAUDIOOPTIONS = 3; + +ChannelManager::ChannelManager(Thread *worker_thread) { +#ifdef HAVE_GIPS + media_engine_ = new GipsMediaEngine(); +#else + media_engine_ = new LinphoneMediaEngine(); +#endif + worker_thread_ = worker_thread; + initialized_ = false; + Init(); +} + +ChannelManager::~ChannelManager() { + Exit(); +} + +MediaEngine *ChannelManager::media_engine() { + return media_engine_; +} + +bool ChannelManager::Init() { + initialized_ = media_engine_->Init(); + return initialized_; +} + +void ChannelManager::Exit() { + if (!initialized_) + return; + + // Need to destroy the voice channels + + while (true) { + crit_.Enter(); + VoiceChannel *channel = NULL; + if (channels_.begin() != channels_.end()) + channel = channels_[0]; + crit_.Leave(); + if (channel == NULL) + break; + delete channel; + } + media_engine_->Terminate(); +} + +struct CreateParams { + Session *session; + VoiceChannel *channel; +}; + +VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) { + CreateParams params; + params.session = session; + params.channel = NULL; + TypedMessageData<CreateParams *> data(¶ms); + worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data); + return params.channel; +} + +VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) { + CritScope cs(&crit_); + + // This is ok to alloc from a thread other than the worker thread + assert(initialized_); + MediaChannel *channel = media_engine_->CreateChannel(); + if (channel == NULL) + return NULL; + + VoiceChannel *voice_channel = new VoiceChannel(this, session, channel); + channels_.push_back(voice_channel); + return voice_channel; +} + +void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) { + TypedMessageData<VoiceChannel *> data(voice_channel); + worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data); +} + +void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) { + CritScope cs(&crit_); + // Destroy voice channel. + assert(initialized_); + std::vector<VoiceChannel *>::iterator it = std::find(channels_.begin(), + channels_.end(), voice_channel); + assert(it != channels_.end()); + if (it == channels_.end()) + return; + + channels_.erase(it); + MediaChannel *channel = voice_channel->channel(); + delete voice_channel; + delete channel; +} + +void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device) { + AudioOptions options; + options.auto_gain_control = auto_gain_control; + options.wave_in_device = wave_in_device; + options.wave_out_device = wave_out_device; + TypedMessageData<AudioOptions> data(options); + worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data); +} + +void ChannelManager::SetAudioOptions_w(AudioOptions options) { + assert(worker_thread_ == Thread::Current()); + + // Set auto gain control on + if (media_engine_->SetAudioOptions(options.auto_gain_control?MediaEngine::AUTO_GAIN_CONTROL:0) != 0) { + // TODO: We need to log these failures. + } + + // Set the audio devices + // This will fail if audio is already playing. Stop all of the media + // start it up again after changing the setting. + { + CritScope cs(&crit_); + for (VoiceChannels::iterator it = channels_.begin(); + it < channels_.end(); + ++it) { + (*it)->PauseMedia_w(); + } + + if (media_engine_->SetSoundDevices(options.wave_in_device, options.wave_out_device) == -1) { + // TODO: We need to log these failures. + } + + for (VoiceChannels::iterator it = channels_.begin(); + it < channels_.end(); + ++it) { + (*it)->UnpauseMedia_w(); + } + } +} + +Thread *ChannelManager::worker_thread() { + return worker_thread_; +} + +void ChannelManager::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CREATEVOICECHANNEL: + { + TypedMessageData<CreateParams *> *data = static_cast<TypedMessageData<CreateParams *> *>(message->pdata); + data->data()->channel = CreateVoiceChannel_w(data->data()->session); + } + break; + + case MSG_DESTROYVOICECHANNEL: + { + TypedMessageData<VoiceChannel *> *data = static_cast<TypedMessageData<VoiceChannel *> *>(message->pdata); + DestroyVoiceChannel_w(data->data()); + } + break; + case MSG_SETAUDIOOPTIONS: + { + TypedMessageData<AudioOptions> *data = static_cast<TypedMessageData<AudioOptions> *>(message->pdata); + SetAudioOptions_w(data->data()); + } + break; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h new file mode 100644 index 00000000..7200f75e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h @@ -0,0 +1,81 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CHANNELMANAGER_H_ +#define _CHANNELMANAGER_H_ + +#include "talk/base/thread.h" +#include "talk/base/criticalsection.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/p2psocket.h" +#include "talk/session/phone/voicechannel.h" +#include "talk/session/phone/mediaengine.h" +#include <vector> + +namespace cricket { + +class VoiceChannel; + +class ChannelManager : public MessageHandler { +public: + ChannelManager(Thread *worker_thread); + ~ChannelManager(); + + VoiceChannel *CreateVoiceChannel(Session *session); + void DestroyVoiceChannel(VoiceChannel *voice_channel); + void SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device); + + MediaEngine *media_engine(); + Thread *worker_thread(); + +private: + VoiceChannel *CreateVoiceChannel_w(Session *session); + void DestroyVoiceChannel_w(VoiceChannel *voice_channel); + void OnMessage(Message *message); + bool Init(); + void Exit(); + + struct AudioOptions { + bool auto_gain_control; + int wave_in_device; + int wave_out_device; + }; + void SetAudioOptions_w(AudioOptions options); + + Thread *worker_thread_; + MediaEngine *media_engine_; + bool initialized_; + CriticalSection crit_; + + typedef std::vector<VoiceChannel*> VoiceChannels; + VoiceChannels channels_; +}; + +} + +#endif // _CHANNELMANAGER_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc new file mode 100644 index 00000000..7d2305dc --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc @@ -0,0 +1,170 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// LinphoneMediaEngine is a Linphone implementation of MediaEngine +extern "C" { +#include "talk/third_party/mediastreamer/mediastream.h" +#ifdef HAVE_ILBC +#include "talk/third_party/mediastreamer/msilbcdec.h" +#endif +#ifdef HAVE_SPEEX +#include "talk/third_party/mediastreamer/msspeexdec.h" +#endif +} +#include <ortp/ortp.h> +#include <netdb.h> +#include <unistd.h> +#include <fcntl.h> +#include <iostream> +#include "talk/session/phone/linphonemediaengine.h" + +using namespace cricket; + +void *thread_function(void *data) +{ + LinphoneMediaChannel *mc =(LinphoneMediaChannel*) data; + while (mc->dying() == false) { + MediaChannel::NetworkInterface *iface = mc->network_interface(); + char *buf[2048]; + int len; + len = read(mc->fd(), buf, sizeof(buf)); + if (iface && (mc->mute()==FALSE)) + iface->SendPacket(buf, len); + } +} + +LinphoneMediaChannel::LinphoneMediaChannel() { + pt_ = 102; + dying_ = false; + pthread_attr_t attr; + audio_stream_ = NULL; + + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = htons(3000); + fd_ = socket(PF_INET, SOCK_DGRAM, 0); + fcntl(fd_, F_SETFL, 0, O_NONBLOCK); + bind (fd_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); + + pthread_attr_init(&attr); + pthread_create(&thread_, &attr, &thread_function, this); +} + +LinphoneMediaChannel::~LinphoneMediaChannel() { + dying_ = true; + pthread_join(thread_, NULL); + audio_stream_stop(audio_stream_); + close(fd_); +} + +void LinphoneMediaChannel::SetCodec(const char *codec) { + if (!strcmp(codec, "iLBC")) + pt_ = 102; + else if (!strcmp(codec, "speex")) + pt_ = 110; + else + pt_ = 0; + if (audio_stream_) + audio_stream_stop(audio_stream_); + audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, pt_, 250); +} + +void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) { + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + struct hostent *host = gethostbyname("localhost"); + memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); + sockaddr.sin_port = htons(2000); + + char buf[2048]; + memcpy(buf, data, len); + + if (buf[1] == pt_) { + } else if (buf[1] == 13) { + } else if (buf[1] == 102) { + SetCodec("iLBC"); + } else if (buf[1] == 110) { + SetCodec("speex"); + } else if (buf[1] == 0) { + SetCodec("PCMU"); + } + + if (play_ && buf[1] != 13) + sendto(fd_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); +} + +void LinphoneMediaChannel::SetPlayout(bool playout) { + play_ = playout; +} + +void LinphoneMediaChannel::SetSend(bool send) { + mute_ = !send; +} + +float LinphoneMediaChannel::GetCurrentQuality() {} +int LinphoneMediaChannel::GetOutputLevel() {} + +LinphoneMediaEngine::LinphoneMediaEngine() {} +LinphoneMediaEngine::~LinphoneMediaEngine() {} + +static void null_log_handler(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) { +} + +bool LinphoneMediaEngine::Init() { + g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, NULL); + g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, NULL); + g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, NULL); + ortp_init(); + ms_init(); + +#ifdef HAVE_SPEEX + ms_speex_codec_init(); + rtp_profile_set_payload(&av_profile, 110, &speex_wb); + codecs_.push_back(Codec(110, "speex", 8)); +#endif + +#ifdef HAVE_ILBC + ms_ilbc_codec_init(); + rtp_profile_set_payload(&av_profile, 102, &payload_type_ilbc); + codecs_.push_back(Codec(102, "iLBC", 4)); +#endif + + rtp_profile_set_payload(&av_profile, 0, &pcmu8000); + codecs_.push_back(Codec(0, "PCMU", 2)); + +return true; +} + +void LinphoneMediaEngine::Terminate() { + +} + +MediaChannel *LinphoneMediaEngine::CreateChannel() { + return new LinphoneMediaChannel(); +} + +int LinphoneMediaEngine::SetAudioOptions(int options) {} +int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) {} + +float LinphoneMediaEngine::GetCurrentQuality() {} +int LinphoneMediaEngine::GetInputLevel() {} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h new file mode 100644 index 00000000..ee16d2ee --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h @@ -0,0 +1,75 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// LinphoneMediaEngine is a Linphone implementation of MediaEngine + +#ifndef TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_ +#define TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_ + +extern "C" { +#include "talk/third_party/mediastreamer/mediastream.h" +} +#include "talk/session/phone/mediaengine.h" + +namespace cricket { + +class LinphoneMediaChannel : public MediaChannel { + public: + LinphoneMediaChannel(); + virtual ~LinphoneMediaChannel(); + virtual void SetCodec(const char *codec); + virtual void OnPacketReceived(const void *data, int len); + + virtual void SetPlayout(bool playout); + virtual void SetSend(bool send); + + virtual float GetCurrentQuality(); + virtual int GetOutputLevel(); + int fd() {return fd_;} + bool mute() {return mute_;} + bool dying() {return dying_;} + private: + AudioStream *audio_stream_; + pthread_t thread_; + int fd_; + int pt_; + bool dying_; + bool mute_; + bool play_; +}; + +class LinphoneMediaEngine : public MediaEngine { + public: + LinphoneMediaEngine(); + ~LinphoneMediaEngine(); + virtual bool Init(); + virtual void Terminate(); + + virtual MediaChannel *CreateChannel(); + + virtual int SetAudioOptions(int options); + virtual int SetSoundDevices(int wave_in_device, int wave_out_device); + + virtual float GetCurrentQuality(); + virtual int GetInputLevel(); +}; + +} // namespace cricket + +#endif // TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h new file mode 100644 index 00000000..db2f9654 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h @@ -0,0 +1,55 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_SESSION_PHONE_MEDIACHANNEL_H_ +#define TALK_SESSION_PHONE_MEDIACHANNEL_H_ + +namespace cricket { + +class MediaChannel { + public: + class NetworkInterface { + public: + virtual void SendPacket(const void *data, size_t len) = 0; + }; + MediaChannel() {network_interface_ = NULL;} + virtual ~MediaChannel() {}; + void SetInterface(NetworkInterface *iface) {network_interface_ = iface;} + virtual void SetCodec(const char *codec) = 0; + virtual void OnPacketReceived(const void *data, int len) = 0; + virtual void SetPlayout(bool playout) = 0; + virtual void SetSend(bool send) = 0; + virtual float GetCurrentQuality() = 0; + virtual int GetOutputLevel() = 0; + NetworkInterface *network_interface() {return network_interface_;} + protected: + NetworkInterface *network_interface_; +}; + +}; // namespace cricket + +#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h new file mode 100644 index 00000000..fa07d2ec --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h @@ -0,0 +1,95 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +// MediaEngine is an abstraction of a media engine which can be subclassed +// to support different media componentry backends. + +#ifndef TALK_SESSION_PHONE_MEDIAENGINE_H_ +#define TALK_SESSION_PHONE_MEDIAENGINE_H_ + +#include <string> +#include <vector> +#include "mediachannel.h" + +namespace cricket { + +class MediaEngine { + public: + + struct Codec { + int id; + std::string name; + int preference; + // Creates a codec with the given parameters. + Codec(int pt, const std::string& nm, int pr) : id(pt), name(nm), preference(pr) {} + // Ranks codecs by their preferences. + bool operator <(const Codec& c) const { return preference > c.preference; } + }; + + // Bitmask flags for options that may be supported by the media engine implementation + enum MediaEngineOptions { + AUTO_GAIN_CONTROL = 1 << 1, + }; + + MediaEngine() {} + + // Initialize + virtual bool Init() = 0; + virtual void Terminate() = 0; + virtual MediaChannel *CreateChannel() = 0; + + virtual int SetAudioOptions(int options) = 0; + virtual int SetSoundDevices(int wave_in_device, int wave_out_device) = 0; + virtual int GetInputLevel() = 0; + + std::vector<Codec> &codecs() { return codecs_; } + + bool FindCodec(const char* codec) { + for (std::vector<Codec>::iterator i = codecs_.begin(); i < codecs_.end(); i++) { + if ((*i).name == codec) + return true; + } + return false; + } + + bool GetCodecPreference (const char *codec, int & preference) { + for (std::vector<Codec>::iterator i = codecs_.begin(); i < codecs_.end(); i++) { + if ((*i).name == codec) { + preference = (*i).preference; + return true; + } + } + return false; + } + + protected: + std::vector<Codec> codecs_; +}; + +} // namespace cricket + +#endif // TALK_SESSION_PHONE_MEDIAENGINE_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc new file mode 100644 index 00000000..d8a31df2 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc @@ -0,0 +1,267 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/logging.h" +#include "talk/session/receiver.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/xmllite/qname.h" +namespace { + +const std::string NS_PHONE("http://www.google.com/session/phone"); +const std::string NS_EMPTY(""); + +const buzz::QName QN_PHONE_DESCRIPTION(true, NS_PHONE, "description"); +const buzz::QName QN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type"); +const buzz::QName QN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id"); +const buzz::QName QN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name"); + +} + +namespace cricket { + +PhoneSessionClient::PhoneSessionClient(const buzz::Jid& jid, + SessionManager *manager) : jid_(jid), SessionClient(manager) { + + // No call to start, and certainly no call with focus + focus_call_ = NULL; + + // Start up the channel manager on a worker thread + channel_manager_ = new ChannelManager(session_manager_->worker_thread()); +} + +PhoneSessionClient::~PhoneSessionClient() { + // Destroy all calls + std::map<uint32, Call *>::iterator it; + while (calls_.begin() != calls_.end()) { + std::map<uint32, Call *>::iterator it = calls_.begin(); + DestroyCall((*it).second); + } + + // Delete channel manager. This will wait for the channels to exit + delete channel_manager_; +} + +const std::string &PhoneSessionClient::GetSessionDescriptionName() { + return NS_PHONE; +} + +PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() { + PhoneSessionDescription* session_desc = new PhoneSessionDescription(); + + MediaEngine *me = channel_manager_->media_engine(); + std::vector<MediaEngine::Codec> codecs = me->codecs(); + std::vector<MediaEngine::Codec>::iterator i; + for (i = codecs.begin(); i < codecs.end(); i++) + session_desc->AddCodec(*i); + + session_desc->Sort(); + return session_desc; +} + +PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) { + const PhoneSessionDescription* offer_desc = + static_cast<const PhoneSessionDescription*>(offer); + PhoneSessionDescription* accept_desc = new PhoneSessionDescription(); + std::vector<MediaEngine::Codec> codecs = channel_manager_->media_engine()->codecs(); + std::vector<MediaEngine::Codec>::iterator iter; + for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) { + for (iter = codecs.begin(); iter < codecs.end(); iter++) { + if ((*iter).name == offer_desc->codecs()[i].name) + accept_desc->AddCodec(*iter); + } + } + + accept_desc->Sort(); + return accept_desc; +} + +bool PhoneSessionClient::FindMediaCodec(MediaEngine* me, + const PhoneSessionDescription* desc, + const char** codec) { + for (size_t i = 0; i < desc->codecs().size(); ++i) { + if (me->FindCodec(desc->codecs()[i].name.c_str())) + *codec = desc->codecs()[i].name.c_str(); + return true; + } + + return false; +} + +const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) { + PhoneSessionDescription* desc = new PhoneSessionDescription(); + + const buzz::XmlElement* payload_type = element->FirstNamed(QN_PHONE_PAYLOADTYPE); + int num_payload_types = 0; + + while (payload_type) { + if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_ID) && + payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_NAME)) { + int id = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_ID).c_str()); + int pref = 0; + std::string name = payload_type->Attr(QN_PHONE_PAYLOADTYPE_NAME); + desc->AddCodec(MediaEngine::Codec(id, name, 0)); + } + + payload_type = payload_type->NextNamed(QN_PHONE_PAYLOADTYPE); + num_payload_types += 1; + } + + // For backward compatability, we can assume the other client is (an old + // version of Talk) if it has no payload types at all. + if (num_payload_types == 0) { + desc->AddCodec(MediaEngine::Codec(103, "ISAC", 1)); + desc->AddCodec(MediaEngine::Codec(0, "PCMU", 0)); + } + + return desc; +} + +buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) { + const PhoneSessionDescription* session_desc = + static_cast<const PhoneSessionDescription*>(_session_desc); + buzz::XmlElement* description = new buzz::XmlElement(QN_PHONE_DESCRIPTION, true); + + for (size_t i = 0; i < session_desc->codecs().size(); ++i) { + buzz::XmlElement* payload_type = new buzz::XmlElement(QN_PHONE_PAYLOADTYPE, true); + + char buf[32]; + sprintf(buf, "%d", session_desc->codecs()[i].id); + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_ID, buf); + + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_NAME, + session_desc->codecs()[i].name.c_str()); + + description->AddElement(payload_type); + } + + return description; +} + +Call *PhoneSessionClient::CreateCall() { + Call *call = new Call(this); + calls_[call->id()] = call; + SignalCallCreate(call); + return call; +} + +void PhoneSessionClient::OnSessionCreate(Session *session, bool received_initiate) { + if (received_initiate) { + session->SignalState.connect(this, &PhoneSessionClient::OnSessionState); + + Call *call = CreateCall(); + session_map_[session->id()] = call; + call->AddSession(session); + } +} + +void PhoneSessionClient::OnSessionState(Session *session, Session::State state) { + if (state == Session::STATE_RECEIVEDINITIATE) { + // If our accept would have no codecs, then we must reject this call. + PhoneSessionDescription* accept_desc = + CreateAcceptSessionDescription(session->remote_description()); + if (accept_desc->codecs().size() == 0) { + // TODO: include an error description with the rejection. + session->Reject(); + } + delete accept_desc; + } +} + +void PhoneSessionClient::DestroyCall(Call *call) { + // Change focus away, signal destruction + + if (call == focus_call_) + SetFocus(NULL); + SignalCallDestroy(call); + + // Remove it from calls_ map and delete + + std::map<uint32, Call *>::iterator it = calls_.find(call->id()); + if (it != calls_.end()) + calls_.erase(it); + + delete call; +} + +void PhoneSessionClient::OnSessionDestroy(Session *session) { + // Find the call this session is in, remove it + + std::map<SessionID, Call *>::iterator it = session_map_.find(session->id()); + assert(it != session_map_.end()); + if (it != session_map_.end()) { + Call *call = (*it).second; + session_map_.erase(it); + call->RemoveSession(session); + } +} + +Call *PhoneSessionClient::GetFocus() { + return focus_call_; +} + +void PhoneSessionClient::SetFocus(Call *call) { + Call *old_focus_call = focus_call_; + if (focus_call_ != call) { + if (focus_call_ != NULL) + focus_call_->EnableChannels(false); + focus_call_ = call; + if (focus_call_ != NULL) + focus_call_->EnableChannels(true); + SignalFocus(focus_call_, old_focus_call); + } +} + +void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) { + // Move all sessions from call to call_to_join, delete call. + // If call_to_join has focus, added sessions should have enabled channels. + + if (focus_call_ == call) + SetFocus(NULL); + call_to_join->Join(call, focus_call_ == call_to_join); + DestroyCall(call); +} + +Session *PhoneSessionClient::CreateSession(Call *call) { + Session *session = session_manager_->CreateSession( + GetSessionDescriptionName(), jid().Str()); + session_map_[session->id()] = call; + return session; +} + +ChannelManager *PhoneSessionClient::channel_manager() { + return channel_manager_; +} + +const buzz::Jid &PhoneSessionClient::jid() const { + return jid_; +} + +const buzz::Jid &PhoneSessionClient::GetJid() const { + return jid_; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h new file mode 100644 index 00000000..150bf34b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h @@ -0,0 +1,122 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PHONESESSIONCLIENT_H_ +#define _PHONESESSIONCLIENT_H_ + +#include "talk/session/phone/call.h" +#include "talk/session/phone/channelmanager.h" +#include "talk/base/sigslot.h" +#include "talk/base/messagequeue.h" +#include "talk/base/thread.h" +#include "talk/p2p/client/sessionclient.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/xmpp/xmppclient.h" +#include <map> + +namespace cricket { + +class Call; +class PhoneSessionDescription; + +class PhoneSessionClient : public SessionClient { +public: + PhoneSessionClient(const buzz::Jid& jid, SessionManager *manager); + ~PhoneSessionClient(); + + const buzz::Jid &jid() const; + + Call *CreateCall(); + void DestroyCall(Call *call); + + Call *GetFocus(); + void SetFocus(Call *call); + + void JoinCalls(Call *call_to_join, Call *call); + + void SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device) { + if (channel_manager_) + channel_manager_->SetAudioOptions(auto_gain_control, wave_in_device, + wave_out_device); + } + + sigslot::signal2<Call *, Call *> SignalFocus; + sigslot::signal1<Call *> SignalCallCreate; + sigslot::signal1<Call *> SignalCallDestroy; + + PhoneSessionDescription* CreateOfferSessionDescription(); + PhoneSessionDescription* CreateAcceptSessionDescription(const SessionDescription* offer); + + // Returns our preference for the given codec. + static int GetMediaCodecPreference(const char* name); + + // Returns the name of the first codec in the description that + // is found. Return value is false if none was found. + static bool FindMediaCodec(MediaEngine* gips, + const PhoneSessionDescription* desc, + const char **codec); + +private: + void OnSessionCreate(Session *session, bool received_initiate); + void OnSessionState(Session *session, Session::State state); + void OnSessionDestroy(Session *session); + const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element); + buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description); + const std::string &GetSessionDescriptionName(); + const buzz::Jid &GetJid() const; + Session *CreateSession(Call *call); + ChannelManager *channel_manager(); + + buzz::Jid jid_; + Call *focus_call_; + ChannelManager *channel_manager_; + std::map<uint32, Call *> calls_; + std::map<SessionID, Call *> session_map_; + + friend class Call; +}; + +class PhoneSessionDescription: public SessionDescription { +public: + // Returns the list of codecs sorted by our preference. + const std::vector<MediaEngine::Codec>& codecs() const { return codecs_; } + + // Adds another codec to the list. + void AddCodec(const MediaEngine::Codec& codec) { codecs_.push_back(codec); } + // Sorts the list of codecs by preference. + void Sort() { /* std::stable_sort(codecs_.begin(), codecs_.end());*/ } + +private: + std::vector<MediaEngine::Codec> codecs_; +}; + +} + +#endif // _PHONESESSIONCLIENT_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc new file mode 100644 index 00000000..b65c9a20 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc @@ -0,0 +1,331 @@ +#include <portaudio.h> +#include <ortp/ortp.h> +#include <speex.h> + +// Socket stuff +#ifndef _WIN32 +#ifdef INET6 +#include <netdb.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#else +#include <winsock32.h> +#endif + +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/portaudiomediaengine.h" + +// Engine settings +#define ENGINE_BUFFER_SIZE 2048 + +// PortAudio settings +#define FRAMES_PER_BUFFER 256 +#define SAMPLE_RATE 1 + +// Speex settings +//#define SPEEX_QUALITY 8 + +// ORTP settings +#define MAX_RTP_SIZE 1500 // From mediastreamer + + +// ----------------------------------------------------------------------------- + +static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *channel_p ) +{ + PortAudioMediaChannel* channel = (PortAudioMediaChannel*) channel_p; + channel->readOutput((float*) outputBuffer, framesPerBuffer); + channel->writeInput((float*) inputBuffer, framesPerBuffer); + return 0; +} + +// ----------------------------------------------------------------------------- + +PortAudioMediaChannel::PortAudioMediaChannel() : mute_(false), play_(false), stream_(NULL), out_buffer_(NULL), in_buffer_(NULL), speex_frame_(NULL) +{ + // Initialize buffers + out_buffer_ = new float[ENGINE_BUFFER_SIZE]; + out_buffer_read_ = out_buffer_write_ = (float*) out_buffer_; + out_buffer_end_ = (float*) out_buffer_ + ENGINE_BUFFER_SIZE; + in_buffer_ = new float[ENGINE_BUFFER_SIZE]; + in_buffer_read_ = in_buffer_write_ = (float*) in_buffer_; + in_buffer_end_ = (float*) in_buffer_ + ENGINE_BUFFER_SIZE; + + // Initialize PortAudio + int err = Pa_OpenDefaultStream(&stream_, 1, 1, paFloat32, SAMPLE_RATE, FRAMES_PER_BUFFER, 0, portAudioCallback, this ); + if (err != paNoError) + fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err)); + + // Initialize Speex + speex_bits_init(&speex_bits_); + speex_enc_state_ = speex_encoder_init(&speex_nb_mode); + speex_dec_state_ = speex_decoder_init(&speex_nb_mode); + speex_decoder_ctl(speex_dec_state_, SPEEX_GET_FRAME_SIZE, &speex_frame_size_); + speex_frame_ = new float[speex_frame_size_]; + + // int quality = SPEEX_QUALITY; + // speex_encoder_ctl(state, SPEEX_SET_QUALITY, &quality); + + // Initialize ORTP socket + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = htons(3000); + rtp_socket_ = socket(PF_INET, SOCK_DGRAM, 0); + fcntl(rtp_socket_, F_SETFL, 0, O_NONBLOCK); + bind (rtp_socket_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); + + // Initialize ORTP Session + rtp_session_ = rtp_session_new(RTP_SESSION_SENDRECV); + rtp_session_max_buf_size_set(rtp_session_, MAX_RTP_SIZE); + rtp_session_set_profile(rtp_session_, &av_profile); + rtp_session_set_local_addr(rtp_session_, "127.0.0.1", 2000); + rtp_session_set_remote_addr(rtp_session_, "127.0.0.1", 3000); + rtp_session_set_scheduling_mode(rtp_session_, 0); + rtp_session_set_blocking_mode(rtp_session_, 0); + rtp_session_set_payload_type(rtp_session_, 110); + rtp_session_set_jitter_compensation(rtp_session_, 250); + rtp_session_enable_adaptive_jitter_compensation(rtp_session_, TRUE); + rtp_timestamp_ = 0; + //rtp_session_signal_connect(rtp_session_, "telephone-event", (RtpCallback) ortpTelephoneCallback,this); +} + +PortAudioMediaChannel::~PortAudioMediaChannel() +{ + if (stream_) { + Pa_CloseStream(stream_); + } + + // Clean up other allocated pointers + + close(rtp_socket_); +} + +void PortAudioMediaChannel::SetCodec(const char *codec) +{ + if (strcmp(codec, "speex")) + printf("Unsupported codec: %s\n", codec); +} + +void PortAudioMediaChannel::OnPacketReceived(const void *data, int len) +{ + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + struct hostent *host = gethostbyname("localhost"); + memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); + sockaddr.sin_port = htons(2000); + + char buf[2048]; + memcpy(buf, data, len); + + // Pass packet on to ORTP + if (play_) { + sendto(rtp_socket_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + } +} + +void PortAudioMediaChannel::SetPlayout(bool playout) +{ + if (!stream_) + return; + + if (play_ && !playout) { + int err = Pa_StopStream(stream_); + if (err != paNoError) { + fprintf(stderr, "Error stopping PortAudio stream: %s\n", Pa_GetErrorText(err)); + return; + } + play_ = false; + } + else if (!play_ && playout) { + int err = Pa_StartStream(stream_); + if (err != paNoError) { + fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err)); + return; + } + play_ = true; + } +} + +void PortAudioMediaChannel::SetSend(bool send) +{ + mute_ = !send; +} + + +float PortAudioMediaChannel::GetCurrentQuality() +{ + return 0; +} + +int PortAudioMediaChannel::GetOutputLevel() +{ + return 0; +} + +void PortAudioMediaChannel::readOutput(float* buf, int len) +{ + //readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); + + // Receive a packet (if there is one) + mblk_t *mp; + mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); + while (mp != NULL) { + gint in_len = mp->b_cont->b_wptr-mp->b_cont->b_rptr; + + // Decode speex stream + speex_bits_read_from(&speex_bits_,mp->b_cont->b_rptr, in_len); + speex_decode(speex_dec_state_, &speex_bits_, speex_frame_); + writeBuffer(out_buffer_, out_buffer_read_, &out_buffer_write_, out_buffer_end_, speex_frame_, speex_frame_size_); + rtp_timestamp_++; + mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); + } + + // Read output + readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); +} + +void PortAudioMediaChannel::writeInput(float* buf, int len) +{ + //writeBuffer(in_buffer_, in_buffer_read_, &in_buffer_write_, in_buffer_end_, buf, len); +} + + +void PortAudioMediaChannel::readBuffer(float* buffer, float** buffer_read_p, float*buffer_write, float* buffer_end, float* target_buffer, int target_len) +{ + float *end, *tmp, *buffer_read = *buffer_read_p; + int remaining; + + // First phase + tmp = buffer_read + target_len; + if (buffer_write < buffer_read && tmp > buffer_end) { + end = buffer_end; + remaining = tmp - buffer_end; + } + else { + end = (tmp > buffer_write ? buffer_write : tmp); + remaining = 0; + } + + while (buffer_read < end) { + *target_buffer++ = *buffer_read++; + } + + // Second phase + if (remaining > 0) { + buffer_read = buffer; + tmp = buffer_read + remaining; + end = (tmp > buffer_write ? buffer_write : tmp); + while (buffer_read < end) { + *target_buffer++ = *buffer_read++; + } + } + + // Finish up + *buffer_read_p = buffer_read; +} + +void PortAudioMediaChannel::writeBuffer(float* buffer, float* buffer_read, float**buffer_write_p, float* buffer_end, float* source_buffer, int source_len) +{ + float *end, *tmp, *buffer_write = *buffer_write_p; + int remaining; + + // First phase + tmp = buffer_write + source_len; + if (buffer_write > buffer_read) { + if (tmp > buffer_end) { + end = buffer_end; + remaining = tmp - buffer_end; + } + else { + end = tmp; + remaining = 0; + } + } + else { + if (tmp > buffer_read) { + printf("Warning: Dropping frame(s)\n"); + end = buffer_read; + remaining = 0; + } + else { + end = tmp; + remaining = 0; + } + } + + while (buffer_write < end) { + *buffer_write++ = *source_buffer++; + } + + // Second phase + if (remaining > 0) { + buffer_write = buffer; + tmp = buffer_write + remaining; + if (tmp > buffer_read) { + printf("Warning: Dropping frame(s)\n"); + end = buffer_read; + } + else { + end = tmp; + } + while (buffer_write < end) { + *buffer_write++ = *source_buffer++; + } + } + + // Finish up + *buffer_write_p = buffer_write; +} + +// ----------------------------------------------------------------------------- + +PortAudioMediaEngine::PortAudioMediaEngine() +{ +} + +PortAudioMediaEngine::~PortAudioMediaEngine() +{ + Pa_Terminate(); +} + +bool PortAudioMediaEngine::Init() +{ + ortp_init(); + + int err = Pa_Initialize(); + if (err != paNoError) { + fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err)); + return false; + } + + // Speex + rtp_profile_set_payload(&av_profile, 110, &speex_wb); + codecs_.push_back(Codec(110, "speex", 8)); + + return true; +} + +void PortAudioMediaEngine::Terminate() +{ +} + + +cricket::MediaChannel* PortAudioMediaEngine::CreateChannel() +{ + return new PortAudioMediaChannel(); +} + +int PortAudioMediaEngine::SetAudioOptions(int options) +{ +} + +int PortAudioMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) +{ +} + +int PortAudioMediaEngine::GetInputLevel() +{ +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h new file mode 100644 index 00000000..95c39a1a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h @@ -0,0 +1,69 @@ +#ifndef PORTAUDIOMEDIAENGINE_H +#define PORTAUDIOMEDIAENGINE_H + +#include <portaudio.h> +#include <speex.h> +#include <ortp/ortp.h> + +#include "talk/session/phone/mediaengine.h" + +class PortAudioMediaChannel : public cricket::MediaChannel +{ +public: + PortAudioMediaChannel(); + virtual ~PortAudioMediaChannel(); + virtual void SetCodec(const char *codec); + virtual void OnPacketReceived(const void *data, int len); + + virtual void SetPlayout(bool playout); + virtual void SetSend(bool send); + + virtual float GetCurrentQuality(); + virtual int GetOutputLevel(); + + void readOutput(float*, int); + void writeInput(float*, int); + +protected: + void readBuffer(float*, float**, float*, float*, float*, int); + void writeBuffer(float*, float*, float**, float*, float*, int); + +private: + bool mute_; + bool play_; + PortAudioStream* stream_; + + // Buffers + float *out_buffer_, *out_buffer_read_, *out_buffer_write_, *out_buffer_end_; + float *in_buffer_, *in_buffer_read_, *in_buffer_write_, *in_buffer_end_; + + // Speex + SpeexBits speex_bits_; + void *speex_enc_state_, *speex_dec_state_; + float *speex_frame_; + int speex_frame_size_; + + // ORTP + int rtp_socket_; + RtpSession* rtp_session_; + int rtp_timestamp_; +}; + + +class PortAudioMediaEngine : public cricket::MediaEngine +{ +public: + PortAudioMediaEngine(); + ~PortAudioMediaEngine(); + virtual bool Init(); + virtual void Terminate(); + + virtual cricket::MediaChannel *CreateChannel(); + + virtual int SetAudioOptions(int options); + virtual int SetSoundDevices(int wave_in_device, int wave_out_device); + + virtual int GetInputLevel(); +}; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc new file mode 100644 index 00000000..58e1db60 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc @@ -0,0 +1,331 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/session/phone/voicechannel.h" +#include "talk/session/phone/channelmanager.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/base/logging.h" +#include <cassert> +#undef SetPort + +namespace { + +// Delay before quality estimate is meaningful. +uint32 kQualityDelay = 5000; // in ms + +} + +namespace cricket { + +VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session, MediaChannel *channel) { + channel_manager_ = manager; + assert(channel_manager_->worker_thread() == Thread::Current()); + channel_ = channel; + session_ = session; + socket_monitor_ = NULL; + audio_monitor_ = NULL; + socket_ = session_->CreateSocket("rtp"); + socket_->SignalState.connect(this, &VoiceChannel::OnSocketState); + socket_->SignalReadPacket.connect(this, &VoiceChannel::OnSocketRead); + channel->SetInterface(this); + enabled_ = false; + paused_ = false; + socket_writable_ = false; + muted_ = false; + LOG(INFO) << "Created voice channel"; + start_time_ = 0xFFFFFFFF - kQualityDelay; + + session->SignalState.connect(this, &VoiceChannel::OnSessionState); + OnSessionState(session, session->state()); +} + +VoiceChannel::~VoiceChannel() { + assert(channel_manager_->worker_thread() == Thread::Current()); + enabled_ = false; + ChangeState(); + delete socket_monitor_; + delete audio_monitor_; + Thread::Current()->Clear(this); + if (socket_ != NULL) + session_->DestroySocket(socket_); + LOG(INFO) << "Destroyed voice channel"; +} + +void VoiceChannel::OnMessage(Message *pmsg) { + switch (pmsg->message_id) { + case MSG_ENABLE: + EnableMedia_w(); + break; + + case MSG_DISABLE: + DisableMedia_w(); + break; + + case MSG_MUTE: + MuteMedia_w(); + break; + + case MSG_UNMUTE: + UnmuteMedia_w(); + break; + + case MSG_SETSENDCODEC: + SetSendCodec_w(); + break; + } +} + +void VoiceChannel::Enable(bool enable) { + // Can be called from thread other than worker thread + channel_manager_->worker_thread()->Post(this, enable ? MSG_ENABLE : MSG_DISABLE); +} + +void VoiceChannel::Mute(bool mute) { + // Can be called from thread other than worker thread + channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE); +} + +MediaChannel * VoiceChannel::channel() { + return channel_; +} + +void VoiceChannel::OnSessionState(Session* session, Session::State state) { + if ((state == Session::STATE_RECEIVEDACCEPT) || + (state == Session::STATE_RECEIVEDINITIATE)) { + channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC); + } +} + +void VoiceChannel::SetSendCodec_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + + const PhoneSessionDescription* desc = + static_cast<const PhoneSessionDescription*>(session()->remote_description()); + + const char *codec = NULL; + + if (desc->codecs().size() > 0) + PhoneSessionClient::FindMediaCodec(channel_manager_->media_engine(), desc, &codec); + + // The other client should have returned one of the codecs that we offered. + // If they could not, they should have rejected the session. So, if we get + // into this state, we're dealing with a bad client, so we may as well just + // pick the mostt common format there is: payload type zero. + if (codec == NULL) + codec = "PCMU"; + + channel_->SetCodec(codec); +} + +void VoiceChannel::OnSocketState(P2PSocket *socket, P2PSocket::State state) { + switch (state) { + case P2PSocket::STATE_WRITABLE: + SocketWritable_w(); + break; + + default: + SocketNotWritable_w(); + break; + } +} + +void VoiceChannel::OnSocketRead(P2PSocket *socket, const char *data, size_t len) { + assert(channel_manager_->worker_thread() == Thread::Current()); + // OnSocketRead gets called from P2PSocket; now pass data to MediaEngine + channel_->OnPacketReceived(data, (int)len); +} + +void VoiceChannel::SendPacket(const void *data, size_t len) { + // SendPacket gets called from MediaEngine; send to socket + // MediaEngine will call us on a random thread. The Send operation on the socket is + // special in that it can handle this. + socket_->Send(static_cast<const char *>(data), len); +} + +void VoiceChannel::ChangeState() { + if (paused_ || !enabled_ || !socket_writable_) { + channel_->SetPlayout(false); + channel_->SetSend(false); + } else { + if (muted_) { + channel_->SetSend(false); + channel_->SetPlayout(true); + } else { + channel_->SetSend(true); + channel_->SetPlayout(true); + } + } +} + +void VoiceChannel::PauseMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + assert(!paused_); + + LOG(INFO) << "Voice channel paused"; + paused_ = true; + ChangeState(); +} + +void VoiceChannel::UnpauseMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + assert(paused_); + + LOG(INFO) << "Voice channel unpaused"; + paused_ = false; + ChangeState(); +} + +void VoiceChannel::EnableMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (enabled_) + return; + + LOG(INFO) << "Voice channel enabled"; + enabled_ = true; + start_time_ = Time(); + ChangeState(); +} + +void VoiceChannel::DisableMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (!enabled_) + return; + + LOG(INFO) << "Voice channel disabled"; + enabled_ = false; + ChangeState(); +} + +void VoiceChannel::MuteMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (muted_) + return; + + LOG(INFO) << "Voice channel muted"; + muted_ = true; + ChangeState(); +} + +void VoiceChannel::UnmuteMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (!muted_) + return; + + LOG(INFO) << "Voice channel unmuted"; + muted_ = false; + ChangeState(); +} + +void VoiceChannel::SocketWritable_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (socket_writable_) + return; + + LOG(INFO) << "Voice channel socket writable"; + socket_writable_ = true; + ChangeState(); +} + +void VoiceChannel::SocketNotWritable_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (!socket_writable_) + return; + + LOG(INFO) << "Voice channel socket not writable"; + socket_writable_ = false; + ChangeState(); +} + +void VoiceChannel::StartConnectionMonitor(int cms) { + delete socket_monitor_; + socket_monitor_ = new SocketMonitor(socket_, Thread::Current()); + socket_monitor_ + ->SignalUpdate.connect(this, &VoiceChannel::OnConnectionMonitorUpdate); + socket_monitor_->Start(cms); +} + +void VoiceChannel::StopConnectionMonitor() { + if (socket_monitor_ != NULL) { + socket_monitor_->Stop(); + socket_monitor_->SignalUpdate.disconnect(this); + delete socket_monitor_; + socket_monitor_ = NULL; + } +} + +void VoiceChannel::OnConnectionMonitorUpdate(SocketMonitor *monitor, + const std::vector<ConnectionInfo> &infos) { + SignalConnectionMonitor(this, infos); +} + +void VoiceChannel::StartAudioMonitor(int cms) { + delete audio_monitor_; + audio_monitor_ = new AudioMonitor(this, Thread::Current()); + audio_monitor_ + ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate); + audio_monitor_->Start(cms); +} + +void VoiceChannel::StopAudioMonitor() { + if (audio_monitor_ != NULL) { + audio_monitor_ ->Stop(); + audio_monitor_ ->SignalUpdate.disconnect(this); + delete audio_monitor_ ; + audio_monitor_ = NULL; + } +} + +void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor, + const AudioInfo& info) { + SignalAudioMonitor(this, info); +} + +Session *VoiceChannel::session() { + return session_; +} + +bool VoiceChannel::HasQuality() { + return Time() >= start_time_ + kQualityDelay; +} + +float VoiceChannel::GetCurrentQuality() { + return channel_->GetCurrentQuality(); +} + +int VoiceChannel::GetInputLevel_w() { + return channel_manager_->media_engine()->GetInputLevel(); +} + +int VoiceChannel::GetOutputLevel_w() { + return channel_->GetOutputLevel(); +} + +Thread* VoiceChannel::worker_thread() { + return channel_manager_->worker_thread(); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h new file mode 100644 index 00000000..4cfa0b11 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h @@ -0,0 +1,129 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VOICECHANNEL_H_ +#define _VOICECHANNEL_H_ + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/network.h" +#include "talk/base/sigslot.h" +#include "talk/p2p/client/socketmonitor.h" +#include "talk/p2p/base/p2psocket.h" +#include "talk/p2p/base/session.h" +#include "talk/session/phone/audiomonitor.h" +#include "talk/session/phone/mediaengine.h" + +namespace cricket { + +const uint32 MSG_ENABLE = 1; +const uint32 MSG_DISABLE = 2; +const uint32 MSG_MUTE = 3; +const uint32 MSG_UNMUTE = 4; +const uint32 MSG_SETSENDCODEC = 5; + +class ChannelManager; + +class VoiceChannel + : public MessageHandler, public sigslot::has_slots<>, + public NetworkSession, public MediaChannel::NetworkInterface { + public: + VoiceChannel(ChannelManager *manager, Session *session, MediaChannel *channel); + ~VoiceChannel(); + + void Enable(bool enable); + void Mute(bool mute); + MediaChannel *channel(); + Session *session(); + + // Monitoring + + void StartConnectionMonitor(int cms); + void StopConnectionMonitor(); + sigslot::signal2<VoiceChannel *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor; + + void StartAudioMonitor(int cms); + void StopAudioMonitor(); + sigslot::signal2<VoiceChannel *, const AudioInfo&> SignalAudioMonitor; + Thread* worker_thread(); + + // Pausing so that the ChannelManager can change the audio devices. These + // should only be called from the worker thread + void PauseMedia_w(); + void UnpauseMedia_w(); + + int GetInputLevel_w(); + int GetOutputLevel_w(); + + // Gives a quality estimate to the network quality manager. + virtual bool HasQuality(); + virtual float GetCurrentQuality(); + + // MediaEngine calls this + virtual void SendPacket(const void *data, size_t len); + +private: + void ChangeState(); + void EnableMedia_w(); + void DisableMedia_w(); + void MuteMedia_w(); + void UnmuteMedia_w(); + void SocketWritable_w(); + void SocketNotWritable_w(); + + void OnConnectionMonitorUpdate(SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos); + void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info); + + // From MessageHandler + + void OnMessage(Message *pmsg); + + // Setting the send codec based on the remote description. + void OnSessionState(Session* session, Session::State state); + void SetSendCodec_w(); + + // From P2PSocket + + void OnSocketState(P2PSocket *socket, P2PSocket::State state); + void OnSocketRead(P2PSocket *socket, const char *data, size_t len); + + + bool enabled_; + bool paused_; + bool socket_writable_; + bool muted_; + MediaChannel *channel_; + Session *session_; + P2PSocket *socket_; + ChannelManager *channel_manager_; + SocketMonitor *socket_monitor_; + AudioMonitor *audio_monitor_; + uint32 start_time_; +}; + +} + +#endif // _VOICECHANNEL_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h new file mode 100644 index 00000000..a5326893 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h @@ -0,0 +1,72 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RECEIVER_H_ +#define _RECEIVER_H_ + +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/p2p/client/sessionclient.h" + +namespace cricket { + +class Receiver : public buzz::XmppTask { +public: + Receiver(Task *parent, SessionClient *session_client) + : buzz::XmppTask(parent, buzz::XmppEngine::HL_TYPE) { + session_client_ = session_client; + } + + virtual int ProcessStart() { + const buzz::XmlElement *stanza = NextStanza(); + if (stanza == NULL) + return STATE_BLOCKED; + session_client_->OnIncomingStanza(stanza); + + // Respond right away to the sender to let them know that we received + // this IQ + buzz::XmlElement * result = MakeIqResult(stanza); + SendStanza(result); + + return STATE_START; + } + +protected: + virtual bool HandleStanza(const buzz::XmlElement *stanza) { + if (!session_client_->IsClientStanza(stanza)) + return false; + QueueStanza(stanza); + return true; + } + +private: + SessionClient *session_client_; +}; + +} + +#endif // _RECEIVER_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h new file mode 100644 index 00000000..9dc5384c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h @@ -0,0 +1,111 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CRICKET_PHONE_SESSIONSENDTASK_H_ +#define _CRICKET_PHONE_SESSIONSENDTASK_H_ + +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/p2p/client/sessionclient.h" + +namespace cricket { + +// The job of this task is to send an IQ stanza out (after stamping it with +// an ID attribute) and then wait for a response. If not response happens +// within 5 seconds, it will signal failure on a SessionClient. If an error +// happens it will also signal failure. If, however, the send succeeds this +// task will quietly go away. + +// It is safe for this to hold on to the session client. In the case where +// the xmpp client goes away, this task will automatically be aborted. The +// session_client is guaranteed to outlive the xmpp session. +class SessionSendTask : public buzz::XmppTask { +public: + SessionSendTask(Task *parent, SessionClient *session_client) + : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE), + session_client_(session_client), + timed_out_(false) { + } + + void Send(const buzz::XmlElement* stanza) { + assert(stanza_.get() == NULL); + stanza_.reset(new buzz::XmlElement(*stanza)); + stanza_->SetAttr(buzz::QN_ID, task_id()); + } + +protected: + // This gets called by the task runner every 500 msec + virtual void Poll() { + if (ElapsedTime() > (15 * 1000 * 10000)) { // 15 secs + timed_out_ = true; + Wake(); + } + } + + virtual int ProcessStart() { + SendStanza(stanza_.get()); + return STATE_RESPONSE; + } + + virtual int ProcessResponse() { + if (timed_out_) { + session_client_->OnFailedSend(stanza_.get(), NULL); + return STATE_DONE; + } + + const buzz::XmlElement* next = NextStanza(); + if (next == NULL) + return STATE_BLOCKED; + + if (next->Attr(buzz::QN_TYPE) == "result") { + return STATE_DONE; + } else { + session_client_->OnFailedSend(stanza_.get(), next); + return STATE_DONE; + } + } + + virtual bool HandleStanza(const buzz::XmlElement *stanza) { + if (!MatchResponseIq(stanza, buzz::Jid(stanza_->Attr(buzz::QN_TO)), task_id())) + return false; + if (stanza->Attr(buzz::QN_TYPE) == "result" || + stanza->Attr(buzz::QN_TYPE) == "error") { + QueueStanza(stanza); + return true; + } + return false; + } + +private: + SessionClient *session_client_; + buzz::scoped_ptr<buzz::XmlElement> stanza_; + bool timed_out_; +}; + +} + +#endif // _CRICKET_PHONE_SESSIONSENDTASK_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am new file mode 100644 index 00000000..3186245a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=mediastreamer diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am new file mode 100644 index 00000000..268a52fe --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am @@ -0,0 +1,92 @@ +EXTRA_DIST=Makefile.ms +noinst_LTLIBRARIES = libmediastreamer.la +libmediastreamer_la_SOURCES=msfilter.c msfilter.h msutils.h waveheader.h\ + mscodec.c mscodec.h \ + mssoundread.c mssoundread.h \ + mssoundwrite.c mssoundwrite.h \ + msbuffer.c msbuffer.h \ + msqueue.c msqueue.h \ + msfifo.c msfifo.h \ + ms.c ms.h\ + mssync.c mssync.h \ + msnosync.c msnosync.h \ + msread.c msread.h \ + mswrite.c mswrite.h \ + mscopy.c mscopy.h \ + msosswrite.c msosswrite.h \ + msossread.c msossread.h \ + msringplayer.c msringplayer.h \ + msrtprecv.c msrtprecv.h \ + msrtpsend.c msrtpsend.h \ + msAlawenc.c msAlawenc.h g711common.h \ + msAlawdec.c msAlawdec.h g711common.h \ + msMUlawenc.c msMUlawenc.h g711common.h \ + msMUlawdec.c msMUlawdec.h g711common.h \ + mstimer.c mstimer.h \ + msqdispatcher.c msqdispatcher.h \ + msfdispatcher.c msfdispatcher.h \ + sndcard.c sndcard.h \ + osscard.c osscard.h\ + hpuxsndcard.c \ + alsacard.c alsacard.h \ + jackcard.c jackcard.h \ + audiostream.c mediastream.h \ + msspeexenc.c msspeexenc.h msspeexdec.c msspeexdec.h \ + msilbcdec.c msilbcdec.h msilbcenc.c msilbcenc.h + +noinst_HEADERS = affine.h \ + msAlawenc.h \ + msfdispatcher.h \ + msilbcdec.h \ + msnosync.h \ + msringplayer.h \ + msspeexdec.h \ + msutils.h \ + waveheader.h \ + alsacard.h \ + msavdecoder.h \ + msfifo.h \ + msilbcenc.h \ + msossread.h \ + msrtprecv.h \ + msspeexenc.h \ + msv4l.h \ + g711common.h \ + msavencoder.h \ + msfilter.h \ + msLPC10decoder.h \ + msosswrite.h \ + msrtpsend.h \ + mssync.h \ + msvideosource.h \ + jackcard.h \ + msbuffer.h \ + msGSMdecoder.h \ + msLPC10encoder.h \ + msqdispatcher.h \ + mssdlout.h \ + mstimer.h \ + mswrite.h \ + mediastream.h \ + mscodec.h \ + msGSMencoder.h \ + msMUlawdec.h \ + msqueue.h \ + mssoundread.h \ + mstruespeechdecoder.h \ + osscard.h \ + msAlawdec.h \ + mscopy.h \ + ms.h \ + msMUlawenc.h \ + msread.h \ + mssoundwrite.h \ + mstruespeechencoder.h \ + sndcard.h + + +libmediastreamer_la_LIBADD= $(GLIB_LIBS) $(ORTP_LIBS) $(SPEEX_LIBS) + +AM_CFLAGS=$(GLIB_CFLAGS) -DG_LOG_DOMAIN=\"MediaStreamer\" $(ORTP_CFLAGS) $(IPV6_CFLAGS) $(ILBC_CFLAGS) $(SPEEX_CFLAGS) + +INCLUDES= -I$(srcdir)/../../.. $(ORTP_CFLAGS) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms new file mode 100644 index 00000000..8b7427c3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms @@ -0,0 +1,34 @@ + +OBJEXT=o +AR = ar +RANLIB = ranlib +DEFS= -DG_LOG_DOMAIN=\"MediaStreamer\" +INCLUDES=-I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include/ \ + -I../gsmlib/ -I../lpc10-1.5 -I../oRTP +COMPILE= gcc $(DEFS) $(INCLUDES) +LIBTOOL=libtool +LDFLAGS=-L/usr/local/lib/ -lglib-1.3 -lgthread-1.3 -lpthread +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ + +libmediastreamer_a_OBJECTS = msfilter.$(OBJEXT) msbuffer.$(OBJEXT) \ +msqueue.$(OBJEXT) msfifo.$(OBJEXT) ms.$(OBJEXT) mssync.$(OBJEXT) \ +msnosync.$(OBJEXT) msread.$(OBJEXT) mswrite.$(OBJEXT) mscopy.$(OBJEXT) \ +msv4lsource.$(OBJEXT) msoss.$(OBJEXT) msosswrite.$(OBJEXT) \ +msossread.$(OBJEXT) msringplayer.$(OBJEXT) msGSMencoder.$(OBJEXT) \ +msGSMdecoder.$(OBJEXT) msLPC10encoder.$(OBJEXT) \ +msLPC10decoder.$(OBJEXT) + +all: libmediastreamer.a mstest + + +.c.o: + $(COMPILE) -c $< + +libmediastreamer.a: $(libmediastreamer_a_OBJECTS) + -rm -f libmediastreamer.a + $(AR) cru libmediastreamer.a $(libmediastreamer_a_OBJECTS) + $(RANLIB) libmediastreamer.a + + +mstest: test.o libmediastreamer.a + gcc -o mstest test.o libmediastreamer.a $(LDFLAGS) -Wl,-rpath /usr/local/lib diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README new file mode 100644 index 00000000..1309f534 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README @@ -0,0 +1,3 @@ +Mediastreamer is the library that handle all media operations: rtp streaming +from file, from soundcard, with codec transcoding, and vice-versa;-). +And also video streaming in the future. diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h new file mode 100644 index 00000000..620fdc9d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h @@ -0,0 +1,43 @@ +/* + * affine.h -- Affine Transforms for 2d objects + * Copyright (C) 2002 Charles Yates <charles.yates@pandora.be> + * Portions Copyright (C) 2003 Dan Dennedy <dan@dennedy.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _AFFINE_H +#define _AFFINE_H + +#include <math.h> + +/** Affine transforms for 2d image manipulation. Current provides shearing and + rotating support. +*/ + +typedef struct { + double matrix[2][2]; +} affine_transform_t; + +void affine_transform_init( affine_transform_t *this ); +void affine_transform_rotate( affine_transform_t *this, double angle ); +void affine_transform_shear( affine_transform_t *this, double shear ); +void affine_transform_scale( affine_transform_t *this, double sx, double sy ); +double affine_transform_mapx( affine_transform_t *this, int x, int y ); +double affine_transform_mapy( affine_transform_t *this, int x, int y ); +void affine_scale( const unsigned char *src, unsigned char *dest, int src_width, int src_height, int dest_width, int dest_height, int bpp ); + +#endif + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c new file mode 100644 index 00000000..c240aa72 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c @@ -0,0 +1,640 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "alsacard.h" + +#ifdef HAVE_ALSA_ASOUNDLIB_H + +static gchar *over_pcmdev=NULL; + +#include "msossread.h" +#include "msosswrite.h" + +#include <signal.h> + +int __alsa_card_write(AlsaCard *obj,char *buf,int size); + +int alsa_set_params(AlsaCard *obj, int rw, int bits, int stereo, int rate) +{ + snd_pcm_hw_params_t *hwparams=NULL; + snd_pcm_sw_params_t *swparams=NULL; + snd_pcm_t *pcm_handle; + gint dir,exact_value; + gint channels; + gint fsize=0; + gint periods=8; + gint periodsize=256; + gint err; + int format; + + if (rw) { + pcm_handle=obj->write_handle; + } + else pcm_handle=obj->read_handle; + + /* Allocate the snd_pcm_hw_params_t structure on the stack. */ + snd_pcm_hw_params_alloca(&hwparams); + + /* Init hwparams with full configuration space */ + if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { + g_warning("alsa_set_params: Cannot configure this PCM device.\n"); + return(-1); + } + + if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { + g_warning("alsa_set_params: Error setting access.\n"); + return(-1); + } + /* Set sample format */ +#ifdef WORDS_BIGENDIAN + format=SND_PCM_FORMAT_S16_BE; +#else + format=SND_PCM_FORMAT_S16_LE; +#endif + if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) { + g_warning("alsa_set_params: Error setting format.\n"); + return(-1); + } + /* Set number of channels */ + if (stereo) channels=2; + else channels=1; + if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) { + g_warning("alsa_set_params: Error setting channels.\n"); + return(-1); + } + /* Set sample rate. If the exact rate is not supported */ + /* by the hardware, use nearest possible rate. */ + exact_value=rate; + dir=0; + if ((err=snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_value, &dir))<0){ + g_warning("alsa_set_params: Error setting rate to %i:%s",rate,snd_strerror(err)); + return -1; + } + if (dir != 0) { + g_warning("alsa_set_params: The rate %d Hz is not supported by your hardware.\n " + "==> Using %d Hz instead.\n", rate, exact_value); + } + /* choose greater period size when rate is high */ + periodsize=periodsize*(rate/8000); + + /* Set buffer size (in frames). The resulting latency is given by */ + /* latency = periodsize * periods / (rate * bytes_per_frame) */ + /* + fsize=periodsize * periods; + exact_value=fsize; + if ((err=snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams,&exact_value)) < 0) { + g_warning("alsa_set_params: Error setting buffer size:%s",snd_strerror(err)); + return(-1); + } + if (fsize!= exact_value) { + g_warning("alsa_set_params: The buffer size %d is not supported by your hardware.\n " + "==> Using %d instead.\n", fsize, exact_value); + } + */ + /* set period size */ + exact_value=periodsize; + dir=0; + if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &exact_value, &dir) < 0) { + g_warning("alsa_set_params: Error setting period size.\n"); + return(-1); + } + if (dir != 0) { + g_warning("alsa_set_params: The period size %d is not supported by your hardware.\n " + "==> Using %d instead.\n", periodsize, exact_value); + } + periodsize=exact_value; + /* Set number of periods. Periods used to be called fragments. */ + exact_value=periods; + dir=0; + if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &exact_value, &dir) < 0) { + g_warning("alsa_set_params: Error setting periods.\n"); + return(-1); + } + if (dir != 0) { + g_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n " + "==> Using %d instead.\n", periods, exact_value); + } + /* Apply HW parameter settings to */ + /* PCM device and prepare device */ + if ((err=snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + g_warning("alsa_set_params: Error setting HW params:%s",snd_strerror(err)); + return(-1); + } + /*prepare sw params */ + if (rw){ + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(pcm_handle, swparams); + if ((err=snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,periodsize*2 ))<0){ + g_warning("alsa_set_params: Error setting start threshold:%s",snd_strerror(err)); + return -1; + } + if ((err=snd_pcm_sw_params(pcm_handle, swparams))<0){ + g_warning("alsa_set_params: Error setting SW params:%s",snd_strerror(err)); + return(-1); + } + } + obj->frame_size=channels*(bits/8); + SND_CARD(obj)->bsize=periodsize*obj->frame_size; + /* //SND_CARD(obj)->bsize=4096; */ + obj->frames=periodsize; + g_message("alsa_set_params: blocksize=%i.",SND_CARD(obj)->bsize); + return SND_CARD(obj)->bsize; +} + +int alsa_card_open_r(AlsaCard *obj,int bits,int stereo,int rate) +{ + int bsize; + int err; + snd_pcm_t *pcm_handle; + gchar *pcmdev; + if (over_pcmdev!=NULL) pcmdev=over_pcmdev; + else pcmdev=obj->pcmdev; + + if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK) < 0) { + g_warning("alsa_card_open_r: Error opening PCM device %s\n",obj->pcmdev ); + return -1; + } + g_return_val_if_fail(pcm_handle!=NULL,-1); + obj->read_handle=pcm_handle; + if ((bsize=alsa_set_params(obj,0,bits,stereo,rate))<0){ + snd_pcm_close(pcm_handle); + obj->read_handle=NULL; + return -1; + } + obj->readbuf=g_malloc0(bsize); + + err=snd_pcm_start(obj->read_handle); + if (err<0){ + g_warning("Cannot start read pcm: %s", snd_strerror(err)); + } + obj->readpos=0; + SND_CARD(obj)->bsize=bsize; + SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; + return 0; +} + +int alsa_card_open_w(AlsaCard *obj,int bits,int stereo,int rate) +{ + int err,bsize; + snd_pcm_t *pcm_handle; + gchar *pcmdev; + if (over_pcmdev!=NULL) pcmdev=over_pcmdev; + else pcmdev=obj->pcmdev; + + if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) { + g_warning("alsa_card_open_w: Error opening PCM device %s\n", obj->pcmdev); + return -1; + } + obj->write_handle=pcm_handle; + if ((bsize=alsa_set_params(obj,1,bits,stereo,rate))<0){ + snd_pcm_close(pcm_handle); + obj->write_handle=NULL; + return -1; + } + obj->writebuf=g_malloc0(bsize); + + obj->writepos=0; + SND_CARD(obj)->bsize=bsize; + SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; + return 0; +} + + +void alsa_card_set_blocking_mode(AlsaCard *obj, gboolean yesno){ + if (obj->read_handle!=NULL) snd_pcm_nonblock(obj->read_handle,!yesno); + if (obj->write_handle!=NULL) snd_pcm_nonblock(obj->write_handle,!yesno); +} + +void alsa_card_close_r(AlsaCard *obj) +{ + if (obj->read_handle!=NULL){ + snd_pcm_close(obj->read_handle); + obj->read_handle=NULL; + g_free(obj->readbuf); + obj->readbuf=NULL; + } +} + +void alsa_card_close_w(AlsaCard *obj) +{ + if (obj->write_handle!=NULL){ + snd_pcm_close(obj->write_handle); + obj->write_handle=NULL; + g_free(obj->writebuf); + obj->writebuf=NULL; + } +} + +int alsa_card_probe(AlsaCard *obj,int bits,int stereo,int rate) +{ + int ret; + ret=alsa_card_open_w(obj,bits,stereo,rate); + if (ret<0) return -1; + ret=SND_CARD(obj)->bsize; + alsa_card_close_w(obj); + return ret; +} + + +void alsa_card_destroy(AlsaCard *obj) +{ + snd_card_uninit(SND_CARD(obj)); + g_free(obj->pcmdev); + if (obj->readbuf!=0) g_free(obj->readbuf); + if (obj->writebuf!=0) g_free(obj->writebuf); +} + +gboolean alsa_card_can_read(AlsaCard *obj) +{ + int frames; + g_return_val_if_fail(obj->read_handle!=NULL,0); + if (obj->readpos!=0) return TRUE; + if ( frames=snd_pcm_avail_update(obj->read_handle)>=obj->frames) return 1; + /* //g_message("frames=%i",frames); */ + return 0; +} + + + +int __alsa_card_read(AlsaCard *obj,char *buf,int bsize) +{ + int err; + sigset_t set; + sigemptyset(&set); + sigaddset(&set,SIGALRM); + sigprocmask(SIG_BLOCK,&set,NULL); + err=snd_pcm_readi(obj->read_handle,buf,bsize/obj->frame_size); + if (err<0) { + if (err!=-EPIPE){ + g_warning("alsa_card_read: snd_pcm_readi() failed:%s.",snd_strerror(err)); + } + snd_pcm_prepare(obj->read_handle); + err=snd_pcm_readi(obj->read_handle,buf,bsize/obj->frame_size); + if (err<0) g_warning("alsa_card_read: snd_pcm_readi() failed:%s.",snd_strerror(err)); + } + sigprocmask(SIG_UNBLOCK,&set,NULL); + return err*obj->frame_size; +} + +int alsa_card_read(AlsaCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + g_return_val_if_fail(obj->read_handle!=NULL,-1); + if (size<bsize){ + gint canread=MIN(bsize-obj->readpos,size); + + if (obj->readpos==0){ + err=__alsa_card_read(obj,obj->readbuf,bsize); + } + + memcpy(buf,&obj->readbuf[obj->readpos],canread); + obj->readpos+=canread; + if (obj->readpos>=bsize) obj->readpos=0; + return canread; + }else{ + err=__alsa_card_read(obj,buf,size); + return err; + } + +} + +int __alsa_card_write(AlsaCard *obj,char *buf,int size) +{ + int err; + sigset_t set; + sigemptyset(&set); + sigaddset(&set,SIGALRM); + sigprocmask(SIG_BLOCK,&set,NULL); + if ((err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size))<0){ + if (err!=-EPIPE){ + g_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err)); + } + snd_pcm_prepare(obj->write_handle); + err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size); + if (err<0) g_warning("alsa_card_write: Error writing sound buffer (size=%i):%s",size,snd_strerror(err)); + + } + sigprocmask(SIG_UNBLOCK,&set,NULL); + return err; +} + +int alsa_card_write(AlsaCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + g_return_val_if_fail(obj->write_handle!=NULL,-1); + if (size<bsize){ + gint canwrite; + + canwrite=MIN(bsize-obj->writepos,size); + memcpy(&obj->writebuf[obj->writepos],buf,canwrite); + obj->writepos+=canwrite; + if (obj->writepos>=bsize){ + err=__alsa_card_write(obj,obj->writebuf,bsize); + obj->writepos=0; + } + return canwrite; + }else{ + return __alsa_card_write(obj,buf,bsize); + } +} + +snd_mixer_t *alsa_mixer_open(AlsaCard *obj){ + snd_mixer_t *mixer=NULL; + int err; + err=snd_mixer_open(&mixer,0); + if (err<0){ + g_warning("Could not open alsa mixer: %s",snd_strerror(err)); + return NULL; + } + if ((err = snd_mixer_attach (mixer, obj->mixdev)) < 0){ + g_warning("Could not attach mixer to card: %s",snd_strerror(err)); + snd_mixer_close(mixer); + return NULL; + } + if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){ + g_warning("snd_mixer_selem_register: %s",snd_strerror(err)); + snd_mixer_close(mixer); + return NULL; + } + if ((err = snd_mixer_load (mixer)) < 0){ + g_warning("snd_mixer_load: %s",snd_strerror(err)); + snd_mixer_close(mixer); + return NULL; + } + obj->mixer=mixer; + return mixer; +} + +void alsa_mixer_close(AlsaCard *obj){ + snd_mixer_close(obj->mixer); + obj->mixer=NULL; +} + +typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction; + +static gint get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){ + long value=0; + const char *elemname; + snd_mixer_elem_t *elem; + int err; + long sndMixerPMin; + long sndMixerPMax; + long newvol; + elem=snd_mixer_first_elem(mixer); + while (elem!=NULL){ + elemname=snd_mixer_selem_get_name(elem); + /* //g_message("Found alsa mixer element %s.",elemname); */ + if (strcmp(elemname,name)==0){ + switch (action){ + case CAPTURE: + if (snd_mixer_selem_has_capture_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol); + newvol-=sndMixerPMin; + value=(100*newvol)/(sndMixerPMax-sndMixerPMin); + if (err<0) g_warning("Could not get capture volume for %s:%s",name,snd_strerror(err)); + /* //else g_message("Succesfully get capture level for %s.",elemname); */ + break; + } + break; + case PLAYBACK: + if (snd_mixer_selem_has_playback_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol); + newvol-=sndMixerPMin; + value=(100*newvol)/(sndMixerPMax-sndMixerPMin); + if (err<0) g_warning("Could not get playback volume for %s:%s",name,snd_strerror(err)); + /* //else g_message("Succesfully get playback level for %s.",elemname); */ + break; + } + break; + case CAPTURE_SWITCH: + + break; + } + } + elem=snd_mixer_elem_next(elem); + } + + return value; +} + + +static void set_mixer_element(snd_mixer_t *mixer,const char *name, gint level,MixerAction action){ + const char *elemname; + snd_mixer_elem_t *elem; + int tmp; + long sndMixerPMin; + long sndMixerPMax; + long newvol; + + elem=snd_mixer_first_elem(mixer); + + while (elem!=NULL){ + elemname=snd_mixer_selem_get_name(elem); + /* //g_message("Found alsa mixer element %s.",elemname); */ + if (strcmp(elemname,name)==0){ + switch(action){ + case CAPTURE: + if (snd_mixer_selem_has_capture_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin; + snd_mixer_selem_set_capture_volume_all(elem,newvol); + /* //g_message("Succesfully set capture level for %s.",elemname); */ + return; + } + break; + case PLAYBACK: + if (snd_mixer_selem_has_playback_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin; + snd_mixer_selem_set_playback_volume_all(elem,newvol); + /* //g_message("Succesfully set playback level for %s.",elemname); */ + return; + } + break; + case CAPTURE_SWITCH: + if (snd_mixer_selem_has_capture_switch(elem)){ + snd_mixer_selem_set_capture_switch_all(elem,level); + /* //g_message("Succesfully set capture switch for %s.",elemname); */ + } + break; + case PLAYBACK_SWITCH: + if (snd_mixer_selem_has_playback_switch(elem)){ + snd_mixer_selem_set_playback_switch_all(elem,level); + /* //g_message("Succesfully set capture switch for %s.",elemname); */ + } + break; + + } + } + elem=snd_mixer_elem_next(elem); + } + + return ; +} + + +void alsa_card_set_level(AlsaCard *obj,gint way,gint a) +{ + snd_mixer_t *mixer; + mixer=alsa_mixer_open(obj); + if (mixer==NULL) return ; + switch(way){ + case SND_CARD_LEVEL_GENERAL: + set_mixer_element(mixer,"Master",a,PLAYBACK); + break; + case SND_CARD_LEVEL_INPUT: + set_mixer_element(mixer,"Capture",a,CAPTURE); + break; + case SND_CARD_LEVEL_OUTPUT: + set_mixer_element(mixer,"PCM",a,PLAYBACK); + break; + default: + g_warning("oss_card_set_level: unsupported command."); + } + alsa_mixer_close(obj); +} + +gint alsa_card_get_level(AlsaCard *obj,gint way) +{ + snd_mixer_t *mixer; + gint value; + mixer=alsa_mixer_open(obj); + if (mixer==NULL) return 0; + switch(way){ + case SND_CARD_LEVEL_GENERAL: + value=get_mixer_element(mixer,"Master",PLAYBACK); + break; + case SND_CARD_LEVEL_INPUT: + value=get_mixer_element(mixer,"Capture",CAPTURE); + break; + case SND_CARD_LEVEL_OUTPUT: + value=get_mixer_element(mixer,"PCM",PLAYBACK); + break; + default: + g_warning("oss_card_set_level: unsupported command."); + } + alsa_mixer_close(obj); + return value; +} + +void alsa_card_set_source(AlsaCard *obj,int source) +{ + snd_mixer_t *mixer; + mixer=alsa_mixer_open(obj); + if (mixer==NULL) return; + switch (source){ + case 'm': + set_mixer_element(mixer,"Mic",1,CAPTURE_SWITCH); + set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH); + break; + case 'l': + set_mixer_element(mixer,"Line",1,CAPTURE_SWITCH); + set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH); + break; + } +} + +MSFilter *alsa_card_create_read_filter(AlsaCard *card) +{ + MSFilter *f=ms_oss_read_new(); + ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); + return f; +} + +MSFilter *alsa_card_create_write_filter(AlsaCard *card) +{ + MSFilter *f=ms_oss_write_new(); + ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); + return f; +} + + +SndCard * alsa_card_new(gint devid) +{ + AlsaCard * obj; + SndCard *base; + int err; + gchar *name=NULL; + + /* carefull: this is an alsalib call despite its name! */ + err=snd_card_get_name(devid,&name); + if (err<0) { + return NULL; + } + obj= g_new0(AlsaCard,1); + base= SND_CARD(obj); + snd_card_init(base); + + base->card_name=g_strdup_printf("%s (Advanced Linux Sound Architecture)",name); + base->_probe=(SndCardOpenFunc)alsa_card_probe; + base->_open_r=(SndCardOpenFunc)alsa_card_open_r; + base->_open_w=(SndCardOpenFunc)alsa_card_open_w; + base->_can_read=(SndCardPollFunc)alsa_card_can_read; + base->_set_blocking_mode=(SndCardSetBlockingModeFunc)alsa_card_set_blocking_mode; + base->_read=(SndCardIOFunc)alsa_card_read; + base->_write=(SndCardIOFunc)alsa_card_write; + base->_close_r=(SndCardCloseFunc)alsa_card_close_r; + base->_close_w=(SndCardCloseFunc)alsa_card_close_w; + base->_set_rec_source=(SndCardMixerSetRecSourceFunc)alsa_card_set_source; + base->_set_level=(SndCardMixerSetLevelFunc)alsa_card_set_level; + base->_get_level=(SndCardMixerGetLevelFunc)alsa_card_get_level; + base->_destroy=(SndCardDestroyFunc)alsa_card_destroy; + base->_create_read_filter=(SndCardCreateFilterFunc)alsa_card_create_read_filter; + base->_create_write_filter=(SndCardCreateFilterFunc)alsa_card_create_write_filter; + + + obj->pcmdev=g_strdup_printf("plughw:%i,0",devid); + obj->mixdev=g_strdup_printf("hw:%i",devid); + obj->readbuf=NULL; + obj->writebuf=NULL; + return base; +} + + +gint alsa_card_manager_init(SndCardManager *m, gint index) +{ + gint devindex; + gint i; + gint found=0; + gchar *name=NULL; + for(devindex=0;index<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){ + if (snd_card_get_name(devindex,&name)==0){ + g_message("Found ALSA device: %s",name); + m->cards[index]=alsa_card_new(devindex); + m->cards[index]->index=index; + found++; + index++; + } + } + return found; +} + +void alsa_card_manager_set_default_pcm_device(const gchar *pcmdev){ + if (over_pcmdev!=NULL){ + g_free(over_pcmdev); + } + over_pcmdev=g_strdup(pcmdev); +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h new file mode 100644 index 00000000..df3372fb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h @@ -0,0 +1,50 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <config.h> + +#ifdef HAVE_ALSA_ASOUNDLIB_H + +#include "sndcard.h" +#define ALSA_PCM_NEW_HW_PARAMS_API +#include <alsa/asoundlib.h> +struct _AlsaCard +{ + SndCard parent; + gchar *pcmdev; + gchar *mixdev; + snd_pcm_t *read_handle; + snd_pcm_t *write_handle; + gint frame_size; + gint frames; + gchar *readbuf; + gint readpos; + gchar *writebuf; + gint writepos; + snd_mixer_t *mixer; +}; + +typedef struct _AlsaCard AlsaCard; + +SndCard *alsa_card_new(gint dev_id); +gint alsa_card_manager_init(SndCardManager *m, gint index); +void alsa_card_manager_set_default_pcm_device(const gchar *pcmdev); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c new file mode 100644 index 00000000..f4ff4867 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c @@ -0,0 +1,343 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "mediastream.h" +#ifdef INET6 + #include <sys/types.h> + #include <sys/socket.h> + #include <netdb.h> +#endif + + +#define MAX_RTP_SIZE 1500 + +/* this code is not part of the library itself, it is part of the mediastream program */ +void audio_stream_free(AudioStream *stream) +{ + RtpSession *s; + RtpSession *destroyed=NULL; + if (stream->rtprecv!=NULL) { + s=ms_rtp_recv_get_session(MS_RTP_RECV(stream->rtprecv)); + if (s!=NULL){ + destroyed=s; + rtp_session_destroy(s); + } + ms_filter_destroy(stream->rtprecv); + } + if (stream->rtpsend!=NULL) { + s=ms_rtp_send_get_session(MS_RTP_SEND(stream->rtpsend)); + if (s!=NULL){ + if (s!=destroyed) + rtp_session_destroy(s); + } + ms_filter_destroy(stream->rtpsend); + } + if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread); + if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite); + if (stream->encoder!=NULL) ms_filter_destroy(stream->encoder); + if (stream->decoder!=NULL) ms_filter_destroy(stream->decoder); + if (stream->timer!=NULL) ms_sync_destroy(stream->timer); + g_free(stream); +} + +static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'}; + +static void on_dtmf_received(RtpSession *s,gint dtmf,gpointer user_data) +{ + AudioStream *stream=(AudioStream*)user_data; + if (dtmf>15){ + g_warning("Unsupported telephone-event type."); + return; + } + g_message("Receiving dtmf %c.",dtmf_tab[dtmf]); + if (stream!=NULL){ + if (strcmp(stream->soundwrite->klass->name,"OssWrite")==0) + ms_oss_write_play_dtmf(MS_OSS_WRITE(stream->soundwrite),dtmf_tab[dtmf]); + } +} + +static void on_timestamp_jump(RtpSession *s,guint32* ts, gpointer user_data) +{ + g_warning("The remote sip-phone has send data with a future timestamp: %u," + "resynchronising session.",*ts); + rtp_session_reset(s); +} + +static const char *ip4local="0.0.0.0"; +static const char *ip6local="::"; + +const char *get_local_addr_for(const char *remote) +{ + const char *ret; +#ifdef INET6 + char num[8]; + struct addrinfo hints, *res0; + int err; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + err = getaddrinfo(remote,"8000", &hints, &res0); + if (err!=0) { + g_warning ("get_local_addr_for: %s", gai_strerror(err)); + return ip4local; + } + ret=(res0->ai_addr->sa_family==AF_INET6) ? ip6local : ip4local; + freeaddrinfo(res0); +#else + ret=ip4local; +#endif + return ret; +} + +void create_duplex_rtpsession(RtpProfile *profile, int locport,char *remip,int remport, + int payload,int jitt_comp, + RtpSession **recvsend){ + RtpSession *rtpr; + rtpr=rtp_session_new(RTP_SESSION_SENDRECV); + rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE); + rtp_session_set_profile(rtpr,profile); + rtp_session_set_local_addr(rtpr,get_local_addr_for(remip),locport); + if (remport>0) rtp_session_set_remote_addr(rtpr,remip,remport); + rtp_session_set_scheduling_mode(rtpr,0); + rtp_session_set_blocking_mode(rtpr,0); + rtp_session_set_payload_type(rtpr,payload); + rtp_session_set_jitter_compensation(rtpr,jitt_comp); + rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE); + /*rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)on_timestamp_jump,NULL);*/ + *recvsend=rtpr; +} + +void create_rtp_sessions(RtpProfile *profile, int locport,char *remip,int remport, + int payload,int jitt_comp, + RtpSession **recv, RtpSession **send){ + RtpSession *rtps,*rtpr; + PayloadType *pt; + /* creates two rtp filters to recv send streams (remote part)*/ + + rtps=rtp_session_new(RTP_SESSION_SENDONLY); + rtp_session_max_buf_size_set(rtps,MAX_RTP_SIZE); + rtp_session_set_profile(rtps,profile); +#ifdef INET6 + rtp_session_set_local_addr(rtps,"::",locport+2); +#else + rtp_session_set_local_addr(rtps,"0.0.0.0",locport+2); +#endif + rtp_session_set_remote_addr(rtps,remip,remport); + rtp_session_set_scheduling_mode(rtps,0); + rtp_session_set_blocking_mode(rtps,0); + rtp_session_set_payload_type(rtps,payload); + rtp_session_set_jitter_compensation(rtps,jitt_comp); + + rtpr=rtp_session_new(RTP_SESSION_RECVONLY); + rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE); + rtp_session_set_profile(rtpr,profile); +#ifdef INET6 + rtp_session_set_local_addr(rtpr,"::",locport); +#else + rtp_session_set_local_addr(rtpr,"0.0.0.0",locport); +#endif + rtp_session_set_scheduling_mode(rtpr,0); + rtp_session_set_blocking_mode(rtpr,0); + rtp_session_set_payload_type(rtpr,payload); + rtp_session_set_jitter_compensation(rtpr,jitt_comp); + rtp_session_signal_connect(rtpr,"telephone-event",(RtpCallback)on_dtmf_received,NULL); + rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)on_timestamp_jump,NULL); + *recv=rtpr; + *send=rtps; + +} + + +AudioStream * audio_stream_start_full(RtpProfile *profile, int locport,char *remip,int remport, + int payload,int jitt_comp, gchar *infile, gchar *outfile, SndCard *playcard, SndCard *captcard) +{ + AudioStream *stream=g_new0(AudioStream,1); + RtpSession *rtps,*rtpr; + PayloadType *pt; + + /* //create_rtp_sessions(profile,locport,remip,remport,payload,jitt_comp,&rtpr,&rtps); */ + + create_duplex_rtpsession(profile,locport,remip,remport,payload,jitt_comp,&rtpr); + rtp_session_signal_connect(rtpr,"telephone-event",(RtpCallback)on_dtmf_received,(gpointer)stream); + rtps=rtpr; + + stream->recv_session = rtpr; + stream->send_session = rtps; + stream->rtpsend=ms_rtp_send_new(); + ms_rtp_send_set_session(MS_RTP_SEND(stream->rtpsend),rtps); + stream->rtprecv=ms_rtp_recv_new(); + ms_rtp_recv_set_session(MS_RTP_RECV(stream->rtprecv),rtpr); + + + /* creates the local part */ + if (infile==NULL) stream->soundread=snd_card_create_read_filter(captcard); + else stream->soundread=ms_read_new(infile); + if (outfile==NULL) stream->soundwrite=snd_card_create_write_filter(playcard); + else stream->soundwrite=ms_write_new(outfile); + + /* creates the couple of encoder/decoder */ + pt=rtp_profile_get_payload(profile,payload); + if (pt==NULL){ + g_error("audiostream.c: undefined payload type."); + return NULL; + } + stream->encoder=ms_encoder_new_with_string_id(pt->mime_type); + stream->decoder=ms_decoder_new_with_string_id(pt->mime_type); + if ((stream->encoder==NULL) || (stream->decoder==NULL)){ + /* big problem: we have not a registered codec for this payload...*/ + audio_stream_free(stream); + g_error("mediastream.c: No decoder available for payload %i.",payload); + return NULL; + } + /* give the sound filters some properties */ + ms_filter_set_property(stream->soundread,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate); + ms_filter_set_property(stream->soundwrite,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate); + + /* give the encoder/decoder some parameters*/ + ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate); + ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate); + ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate); + ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate); + + ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FMTP, (void*)pt->fmtp); + ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FMTP,(void*)pt->fmtp); + /* create the synchronisation source */ + stream->timer=ms_timer_new(); + + /* and then connect all */ + ms_filter_add_link(stream->soundread,stream->encoder); + ms_filter_add_link(stream->encoder,stream->rtpsend); + ms_filter_add_link(stream->rtprecv,stream->decoder); + ms_filter_add_link(stream->decoder,stream->soundwrite); + + ms_sync_attach(stream->timer,stream->soundread); + ms_sync_attach(stream->timer,stream->rtprecv); + + /* and start */ + ms_start(stream->timer); + + return stream; +} + +static int defcard=0; + +void audio_stream_set_default_card(int cardindex){ + defcard=cardindex; +} + +AudioStream * audio_stream_start_with_files(RtpProfile *prof,int locport,char *remip, + int remport,int profile,int jitt_comp,gchar *infile, gchar*outfile) +{ + return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,infile,outfile,NULL,NULL); +} + +AudioStream * audio_stream_start(RtpProfile *prof,int locport,char *remip,int remport,int profile,int jitt_comp) +{ + SndCard *sndcard; + sndcard=snd_card_manager_get_card(snd_card_manager,defcard); + return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,NULL,NULL,sndcard,sndcard); +} + +AudioStream *audio_stream_start_with_sndcards(RtpProfile *prof,int locport,char *remip,int remport,int profile,int jitt_comp,SndCard *playcard, SndCard *captcard) +{ + g_return_val_if_fail(playcard!=NULL,NULL); + g_return_val_if_fail(captcard!=NULL,NULL); + return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,NULL,NULL,playcard,captcard); +} + +void audio_stream_set_rtcp_information(AudioStream *st, const char *cname){ + if (st->send_session!=NULL){ + rtp_session_set_source_description(st->send_session,cname,NULL,NULL,NULL, NULL,"linphone", + "This is free software (GPL) !"); + } +} + +void audio_stream_stop(AudioStream * stream) +{ + + ms_stop(stream->timer); + ortp_global_stats_display(); + ms_sync_detach(stream->timer,stream->soundread); + ms_sync_detach(stream->timer,stream->rtprecv); + + ms_filter_remove_links(stream->soundread,stream->encoder); + ms_filter_remove_links(stream->encoder,stream->rtpsend); + ms_filter_remove_links(stream->rtprecv,stream->decoder); + ms_filter_remove_links(stream->decoder,stream->soundwrite); + + audio_stream_free(stream); +} + +RingStream * ring_start(gchar *file,gint interval,SndCard *sndcard) +{ + return ring_start_with_cb(file,interval,sndcard,NULL,NULL); +} + +RingStream * ring_start_with_cb(gchar *file,gint interval,SndCard *sndcard, MSFilterNotifyFunc func,gpointer user_data) +{ + RingStream *stream; + int tmp; + g_return_val_if_fail(sndcard!=NULL,NULL); + stream=g_new0(RingStream,1); + stream->source=ms_ring_player_new(file,interval); + if (stream->source==NULL) { + g_warning("Could not create ring player. Probably the ring file (%s) does not exist.",file); + return NULL; + } + if (func!=NULL) ms_filter_set_notify_func(MS_FILTER(stream->source),func,user_data); + stream->sndwrite=snd_card_create_write_filter(sndcard); + ms_filter_get_property(stream->source,MS_FILTER_PROPERTY_FREQ,&tmp); + ms_filter_set_property(stream->sndwrite,MS_FILTER_PROPERTY_FREQ,&tmp); + ms_filter_get_property(stream->source,MS_FILTER_PROPERTY_CHANNELS,&tmp); + ms_filter_set_property(stream->sndwrite,MS_FILTER_PROPERTY_CHANNELS,&tmp); + stream->timer=ms_timer_new(); + ms_filter_add_link(stream->source,stream->sndwrite); + ms_sync_attach(stream->timer,stream->source); + ms_start(stream->timer); + return stream; +} + +void ring_stop(RingStream *stream) +{ + ms_stop(stream->timer); + ms_sync_detach(stream->timer,stream->source); + ms_sync_destroy(stream->timer); + ms_filter_remove_links(stream->source,stream->sndwrite); + ms_filter_destroy(stream->source); + ms_filter_destroy(stream->sndwrite); + g_free(stream); +} + +/* returns the latency in samples if the audio device with id dev_id is openable in full duplex mode, else 0 */ +gint test_audio_dev(int dev_id) +{ + gint err; + SndCard *sndcard=snd_card_manager_get_card(snd_card_manager,dev_id); + if (sndcard==NULL) return -1; + err=snd_card_probe(sndcard,16,0,8000); + return err; /* return latency in number of sample */ +} + +gint audio_stream_send_dtmf(AudioStream *stream, gchar dtmf) +{ + ms_rtp_send_dtmf(MS_RTP_SEND(stream->rtpsend), dtmf); + ms_oss_write_play_dtmf(MS_OSS_WRITE(stream->soundwrite),dtmf); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h new file mode 100644 index 00000000..3f5ad16f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h @@ -0,0 +1,171 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * Wrapper for linphone Codec class by Simon Morlat <simon.morlat@free.fr> + */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static inline unsigned char s16_to_alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; + } else { + mask = 0x55; + pcm_val = -pcm_val; + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + } + + if (pcm_val < 256) + aval = pcm_val >> 4; + else { + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + } + return aval ^ mask; +} + +/* + * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM + * + */ +static inline int alaw_to_s16(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + t = a_val & 0x7f; + if (t < 16) + t = (t << 4) + 8; + else { + seg = (t >> 4) & 0x07; + t = ((t & 0x0f) << 4) + 0x108; + t <<= seg -1; + } + return ((a_val & 0x80) ? t : -t); +} +/* + * s16_to_ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + if (pcm_val < 0) { + pcm_val = 0x84 - pcm_val; + mask = 0x7f; + } else { + pcm_val += 0x84; + mask = 0xff; + } + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + return uval ^ mask; +} + +/* + * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static inline int ulaw_to_s16(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & 0x0f) << 3) + 0x84; + t <<= (u_val & 0x70) >> 4; + + return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c new file mode 100644 index 00000000..8210e29d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c @@ -0,0 +1,301 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "sndcard.h" +#include "osscard.h" + +#ifdef HAVE_SYS_AUDIO_H +#include <sys/audio.h> + + +#include "msossread.h" +#include "msosswrite.h" + +#include <errno.h> +#include <fcntl.h> + + +int hpuxsnd_open(HpuxSndCard *obj, int bits,int stereo, int rate) +{ + int fd; + int p=0,cond=0; + int i=0; + int min_size=0,blocksize=512; + /* do a quick non blocking open to be sure that we are not going to be blocked here + for the eternity */ + fd=open(obj->dev_name,O_RDWR|O_NONBLOCK); + if (fd<0) return -EWOULDBLOCK; + close(fd); + /* open the device */ + fd=open(obj->dev_name,O_RDWR); + + g_return_val_if_fail(fd>0,-errno); + + ioctl(fd,AUDIO_RESET,0); + ioctl(fd,AUDIO_SET_SAMPLE_RATE,rate); + ioctl(fd,AUDIO_SET_CHANNELS,stereo); + p=AUDIO_FORMAT_LINEAR16BIT; + ioctl(fd,AUDIO_SET_DATA_FORMAT,p); + /* ioctl(fd,AUDIO_GET_RXBUFSIZE,&min_size); does not work ? */ + min_size=2048; + + g_message("dsp blocksize is %i.",min_size); + obj->fd=fd; + obj->readpos=0; + obj->writepos=0; + SND_CARD(obj)->bits=bits; + SND_CARD(obj)->stereo=stereo; + SND_CARD(obj)->rate=rate; + SND_CARD(obj)->bsize=min_size; + return fd; +} + +int hpux_snd_card_probe(HpuxSndCard *obj,int bits,int stereo,int rate) +{ + return 2048; +} + + +int hpux_snd_card_open(HpuxSndCard *obj,int bits,int stereo,int rate) +{ + int fd; + obj->ref++; + if (obj->fd==0){ + fd=hpuxsnd_open(obj,bits,stereo,rate); + if (fd<0) { + obj->fd=0; + obj->ref--; + return -1; + } + } + SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; + return 0; +} + +void hpux_snd_card_close(HpuxSndCard *obj) +{ + int i; + obj->ref--; + if (obj->ref==0) { + close(obj->fd); + obj->fd=0; + SND_CARD(obj)->flags&=~SND_CARD_FLAGS_OPENED; + + } +} + +void hpux_snd_card_destroy(HpuxSndCard *obj) +{ + snd_card_uninit(SND_CARD(obj)); + g_free(obj->dev_name); + g_free(obj->mixdev_name); +} + +gboolean hpux_snd_card_can_read(HpuxSndCard *obj) +{ + struct timeval tout={0,0}; + int err; + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(obj->fd,&fdset); + err=select(obj->fd+1,&fdset,NULL,NULL,&tout); + if (err>0) return TRUE; + else return FALSE; +} + +int hpux_snd_card_read(HpuxSndCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + if (size<bsize){ + gint canread=MIN(bsize-obj->readpos,size); + if (obj->readbuf==NULL) obj->readbuf=g_malloc0(bsize); + if (obj->readpos==0){ + err=read(obj->fd,obj->readbuf,bsize); + if (err<0) { + g_warning("hpux_snd_card_read: read() failed:%s.",strerror(errno)); + return -1; + } + } + + memcpy(buf,&obj->readbuf[obj->readpos],canread); + obj->readpos+=canread; + if (obj->readpos>=bsize) obj->readpos=0; + return canread; + }else{ + err=read(obj->fd,buf,size); + if (err<0) { + g_warning("hpux_snd_card_read: read-2() failed:%s.",strerror(errno)); + } + return err; + } + +} + +int hpux_snd_card_write(HpuxSndCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + if (size<bsize){ + gint canwrite=MIN(bsize-obj->writepos,size); + if (obj->writebuf==NULL) obj->writebuf=g_malloc0(bsize); + + memcpy(&obj->writebuf[obj->writepos],buf,canwrite); + obj->writepos+=canwrite; + if (obj->writepos>=bsize){ + err=write(obj->fd,obj->writebuf,bsize); + } + return canwrite; + }else{ + return write(obj->fd,buf,bsize); + } +} + +#define SND_CARD_LEVEL_TO_HPUX_LEVEL(a) (((a)*2) - 100) +#define HPUX_LEVEL_TO_SND_CARD_LEVEL(a) (((a)+200)/2) +void hpux_snd_card_set_level(HpuxSndCard *obj,gint way,gint a) +{ + struct audio_gain gain; + int error,mix_fd; + + g_return_if_fail(obj->mixdev_name!=NULL); + memset(&gain,0,sizeof(struct audio_gain)); + switch(way){ + case SND_CARD_LEVEL_GENERAL: + gain.cgain[0].monitor_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a); + gain.cgain[1].monitor_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a); + break; + case SND_CARD_LEVEL_INPUT: + gain.cgain[0].receive_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a); + gain.cgain[1].receive_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a); + break; + case SND_CARD_LEVEL_OUTPUT: + gain.cgain[0].transmit_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a); + gain.cgain[1].transmit_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a); + break; + default: + g_warning("hpux_snd_card_set_level: unsupported command."); + return; + } + gain.channel_mask=AUDIO_CHANNEL_RIGHT|AUDIO_CHANNEL_LEFT; + mix_fd = open(obj->mixdev_name, O_WRONLY); + g_return_if_fail(mix_fd>0); + error=ioctl(mix_fd,AUDIO_SET_GAINS,&gain); + if (error<0){ + g_warning("hpux_snd_card_set_level: Could not set gains: %s",strerror(errno)); + } + close(mix_fd); +} + +gint hpux_snd_card_get_level(HpuxSndCard *obj,gint way) +{ + struct audio_gain gain; + int p=0,mix_fd,error; + g_return_if_fail(obj->mixdev_name!=NULL); + + gain.channel_mask=AUDIO_CHANNEL_RIGHT|AUDIO_CHANNEL_LEFT; + mix_fd = open(obj->mixdev_name, O_RDONLY); + g_return_if_fail(mix_fd>0); + error=ioctl(mix_fd,AUDIO_GET_GAINS,&gain); + if (error<0){ + g_warning("hpux_snd_card_set_level: Could not get gains: %s",strerror(errno)); + } + close(mix_fd); + + switch(way){ + case SND_CARD_LEVEL_GENERAL: + p=gain.cgain[0].monitor_gain; + break; + case SND_CARD_LEVEL_INPUT: + p=gain.cgain[0].receive_gain; + break; + case SND_CARD_LEVEL_OUTPUT: + p=gain.cgain[0].transmit_gain; + break; + default: + g_warning("hpux_snd_card_get_level: unsupported command."); + return -1; + } + return HPUX_LEVEL_TO_SND_CARD_LEVEL(p); +} + +void hpux_snd_card_set_source(HpuxSndCard *obj,int source) +{ + gint p=0; + gint mix_fd; + gint error=0; + g_return_if_fail(obj->mixdev_name!=NULL); + + mix_fd=open("/dev/audio",O_WRONLY); + g_return_if_fail(mix_fd>0); + switch(source){ + case 'm': + error=ioctl(mix_fd,AUDIO_SET_INPUT,AUDIO_IN_MIKE); + break; + case 'l': + error=ioctl(mix_fd,AUDIO_SET_INPUT,AUDIO_IN_LINE); + break; + default: + g_warning("hpux_snd_card_set_source: unsupported source."); + } + close(mix_fd); +} + +MSFilter *hpux_snd_card_create_read_filter(HpuxSndCard *card) +{ + MSFilter *f=ms_oss_read_new(); + ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); + return f; +} + +MSFilter *hpux_snd_card_create_write_filter(HpuxSndCard *card) +{ + MSFilter *f=ms_oss_write_new(); + ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); + return f; +} + + +SndCard * hpux_snd_card_new(char *devname, char *mixdev_name) +{ + HpuxSndCard * obj= g_new0(HpuxSndCard,1); + SndCard *base= SND_CARD(obj); + snd_card_init(base); + obj->dev_name=g_strdup(devname); + obj->mixdev_name=g_strdup( mixdev_name); + base->card_name=g_strdup(devname); + base->_probe=(SndCardOpenFunc)hpux_snd_card_probe; + base->_open_r=(SndCardOpenFunc)hpux_snd_card_open; + base->_open_w=(SndCardOpenFunc)hpux_snd_card_open; + base->_can_read=(SndCardPollFunc)hpux_snd_card_can_read; + base->_read=(SndCardIOFunc)hpux_snd_card_read; + base->_write=(SndCardIOFunc)hpux_snd_card_write; + base->_close_r=(SndCardCloseFunc)hpux_snd_card_close; + base->_close_w=(SndCardCloseFunc)hpux_snd_card_close; + base->_set_rec_source=(SndCardMixerSetRecSourceFunc)hpux_snd_card_set_source; + base->_set_level=(SndCardMixerSetLevelFunc)hpux_snd_card_set_level; + base->_get_level=(SndCardMixerGetLevelFunc)hpux_snd_card_get_level; + base->_destroy=(SndCardDestroyFunc)hpux_snd_card_destroy; + base->_create_read_filter=(SndCardCreateFilterFunc)hpux_snd_card_create_read_filter; + base->_create_write_filter=(SndCardCreateFilterFunc)hpux_snd_card_create_write_filter; + return base; +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c new file mode 100644 index 00000000..b929cce9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c @@ -0,0 +1,574 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + JACK support + Copyright (C) 2004 Tobias Gehrig tobias@gehrig.tk +*/ + +#include "jackcard.h" + +#ifdef __JACK_ENABLED__ + +#include "msossread.h" +#include "msosswrite.h" + +#include <signal.h> + +#define READBUFFERSIZE 524288 +#define WRITEBUFFERSIZE 524288 +#define BSIZE 512 + +/** + * jack_shutdown: + * @arg: + * + * This is the shutdown callback for this JACK application. + * It is called by JACK if the server ever shuts down or + * decides to disconnect the client. + * + */ +void +jack_shutdown (void *arg) +{ + JackCard* obj = (JackCard*) arg; + + obj->jack_running = FALSE; + obj->jack_active = FALSE; + obj->read.port = NULL; + if (obj->read.open) + obj->read.init = TRUE; + obj->write.port = NULL; + if (obj->write.open) + obj->write.init = TRUE; +} + +int samplerate(jack_nframes_t rate, void *arg) +{ + JackCard* obj = (JackCard*) arg; + int error; + + obj->rate = rate; + if (obj->read.open) { + obj->read.data.src_ratio = (double)obj->read.rate / (double)obj->rate; + obj->read.data.input_frames = (long)((double)obj->read.frames/obj->read.data.src_ratio); + g_free(obj->read.data.data_in); + obj->read.data.data_in = malloc(obj->read.data.input_frames*sizeof(float)); + if (obj->read.src_state) + if ((error = src_set_ratio(obj->read.src_state, obj->read.data.src_ratio)) != 0) + g_warning("Error while resetting the write samplerate: %s", src_strerror(error)); + } + if (obj->write.open) { + obj->write.data.src_ratio = (double)obj->rate / (double)obj->write.rate; + obj->write.data.output_frames = (long)((double)obj->write.frames*obj->write.data.src_ratio); + g_free(obj->write.data.data_out); + obj->write.data.data_out = malloc(obj->write.data.output_frames*sizeof(float)); + if (obj->write.src_state) + if ((error = src_set_ratio(obj->write.src_state, obj->write.data.src_ratio)) != 0) + g_warning("Error while resetting the write samplerate: %s", src_strerror(error)); + } + return 0; +} + +/* + * The process callback for this JACK application. + * It is called by JACK at the appropriate times. + * @nframes : + * @arg : + */ +int +process (jack_nframes_t nframes, void *arg) +{ + JackCard* obj = (JackCard*) arg; + sample_t *out; + sample_t *in; + + if (obj->clear && !obj->write.can_process) { + out = (sample_t *) jack_port_get_buffer (obj->write.port, nframes); + memset (out, 0, nframes * sizeof(sample_t)); + obj->clear = FALSE; + } + + if (!obj->can_process) + return 0; + + if(obj->read.can_process) { + in = (sample_t *) jack_port_get_buffer (obj->read.port, nframes); + jack_ringbuffer_write (obj->read.buffer, (void *) in, sizeof(sample_t) * nframes); + } + + if (obj->write.can_process) { + out = (sample_t *) jack_port_get_buffer (obj->write.port, nframes); + memset (out, 0, nframes * sizeof(sample_t)); + if (obj->clear && jack_ringbuffer_read_space(obj->write.buffer) == 0) { + obj->write.can_process = FALSE; + if (!obj->read.open) + obj->can_process = FALSE; + obj->clear = FALSE; + return 0; + } + jack_ringbuffer_read (obj->write.buffer, (void *) out, sizeof(sample_t) * nframes); + } + return 0; +} + +int jack_init(JackCard* obj) +{ + char* client_name; + int error; + + if (!obj->jack_running) { + obj->client = NULL; + client_name = g_strdup_printf("linphone-%u", g_random_int()); + if ((obj->client = jack_client_new (client_name)) == NULL) { + g_warning("cannot create jack client"); + g_free(client_name); + return -1; + } + g_message("Found Jack Daemon"); + g_free(client_name); + + /* tell the JACK server to call `process()' whenever + there is work to be done. + */ + jack_set_process_callback (obj->client, process, obj); + + /* tell the JACK server to call `jack_shutdown()' if + it ever shuts down, either entirely, or if it + just decides to stop calling us. + */ + jack_on_shutdown (obj->client, jack_shutdown, obj); + jack_set_sample_rate_callback (obj->client, samplerate, obj); + obj->rate = jack_get_sample_rate (obj->client); + if (obj->rate == 0) { + g_warning ("rate is 0???"); + if (jack_client_close(obj->client) != 0) + g_warning("could not close client"); + return -1; + } + obj->buffer_size = jack_get_buffer_size(obj->client); + obj->jack_running = TRUE; + } + + if (!obj->jack_active) { + if (jack_activate (obj->client)) { + g_warning("cannot activate jack client"); + return -1; + } else obj->jack_active = TRUE; + } + + if (obj->read.init) { + if (!obj->read.port && (obj->read.port = jack_port_register (obj->client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0))==NULL) { + g_warning("error while trying to register input port"); + return -1; + } + if (!obj->read.phys_ports && (obj->read.phys_ports = jack_get_ports (obj->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) { + g_warning("Cannot find any physical capture ports\n"); + jack_port_unregister(obj->client, obj->read.port); + obj->read.port = NULL; + return -1; + } + if (!jack_port_connected(obj->read.port)) + if ((error = jack_connect (obj->client, obj->read.phys_ports[0], jack_port_name (obj->read.port))) != 0) { + g_warning("cannot connect input ports: %s -> %s\n", jack_port_name (obj->read.port), obj->read.phys_ports[0]); + if (error == EEXIST) g_warning("connection already made"); + else { + jack_port_unregister(obj->client, obj->read.port); + obj->read.port = NULL; + return -1; + } + } + obj->read.init = FALSE; + } + + if (obj->write.init) { + if (!obj->write.port && (obj->write.port = jack_port_register (obj->client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0))==NULL) { + g_warning("error while trying to register output port"); + return -1; + } + if (!obj->write.phys_ports && (obj->write.phys_ports = jack_get_ports (obj->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) { + g_warning("Cannot find any physical playback ports\n"); + jack_port_unregister(obj->client, obj->write.port); + obj->write.port = NULL; + return -1; + } + if (!jack_port_connected(obj->write.port)) { + if ((error = jack_connect (obj->client, jack_port_name (obj->write.port), obj->write.phys_ports[0])) != 0) { + g_warning("cannot connect output ports: %s -> %s\n", jack_port_name (obj->write.port), obj->write.phys_ports[0]); + if (error == EEXIST) g_warning("connection already made"); + else { + jack_port_unregister(obj->client, obj->write.port); + obj->write.port = NULL; + return -1; + } + } + if ((error = jack_connect (obj->client, jack_port_name (obj->write.port), obj->write.phys_ports[1])) != 0) { + g_warning("cannot connect output ports: %s -> %s\n", jack_port_name (obj->write.port), obj->write.phys_ports[1]); + if (error == EEXIST) g_warning("connection already made"); + else { + jack_port_unregister(obj->client, obj->write.port); + obj->write.port = NULL; + return -1; + } + } + } + obj->write.init = FALSE; + } + return 0; +} + +int jack_card_open_r(JackCard *obj,int bits,int stereo,int rate) +{ + int channels = stereo + 1, bsize, error; + obj->read.init = TRUE; + if (jack_init(obj) != 0) return -1; + + obj->read.rate = rate; + obj->sample_size = bits / 8; + obj->frame_size = channels * obj->sample_size; + bsize = BSIZE; + obj->read.frames = bsize / 2; + SND_CARD(obj)->bsize = bsize; + SND_CARD(obj)->flags |= SND_CARD_FLAGS_OPENED; + obj->read.channels = channels; + if ((obj->read.src_state = src_new (SRC_SINC_FASTEST, channels, &error)) == NULL) + g_warning("Error while initializing the samplerate converter: %s", src_strerror(error)); + obj->read.data.src_ratio = (double)rate / (double)obj->rate; + obj->read.data.input_frames = (long)((double)obj->read.frames/obj->read.data.src_ratio); + obj->read.data.data_in = malloc(obj->read.data.input_frames*sizeof(float)); + obj->read.data.data_out = malloc(obj->read.frames*sizeof(float)); + obj->read.data.end_of_input = 0; + if (!obj->read.buffer) + obj->read.buffer = jack_ringbuffer_create(READBUFFERSIZE); + obj->read.can_process = TRUE; + obj->can_process = TRUE; + obj->read.open = TRUE; + obj->read.init = FALSE; + return 0; +} + +int jack_card_open_w(JackCard *obj,int bits,int stereo,int rate) +{ + int channels = stereo + 1, bsize, err; + obj->write.init = TRUE; + if (jack_init(obj) != 0) return -1; + + obj->write.rate = rate; + obj->sample_size = bits / 8; + obj->frame_size = channels * obj->sample_size; + bsize = BSIZE; + obj->write.frames = bsize / 2; + SND_CARD(obj)->bsize = bsize; + SND_CARD(obj)->flags |= SND_CARD_FLAGS_OPENED; + obj->write.channels = channels; + if ((obj->write.src_state = src_new (SRC_SINC_FASTEST, channels, &err)) == NULL) + g_warning("Error while initializing the samplerate converter: %s", src_strerror(err)); + obj->write.data.src_ratio = (double)obj->rate / (double)rate; + obj->write.data.data_in = malloc(obj->write.frames*sizeof(float)); + obj->write.data.end_of_input = 0; + obj->write.data.output_frames = (long)((double)obj->write.frames*obj->write.data.src_ratio); + obj->write.data.data_out = malloc(obj->write.data.output_frames*sizeof(float)); + if (!obj->write.buffer) + obj->write.buffer = jack_ringbuffer_create(WRITEBUFFERSIZE); + obj->write.can_process = TRUE; + obj->can_process = TRUE; + obj->write.open = TRUE; + obj->write.init = FALSE; + return 0; +} + +void jack_card_set_blocking_mode(JackCard *obj, gboolean yesno) +{ +} + +void jack_card_close_r(JackCard *obj) +{ + obj->read.open = FALSE; + obj->read.init = FALSE; + obj->read.can_process = FALSE; + if (!obj->write.open) + obj->can_process = FALSE; + if (obj->read.src_state) + obj->read.src_state = src_delete (obj->read.src_state); + g_free(obj->read.data.data_in); + g_free(obj->read.data.data_out); +} + +void jack_card_close_w(JackCard *obj) +{ + obj->write.open = FALSE; + obj->write.init = FALSE; + obj->clear = TRUE; + if (!obj->jack_running) { + obj->write.can_process = FALSE; + obj->can_process = FALSE; + } + if (obj->write.src_state) + obj->write.src_state = src_delete (obj->write.src_state); + g_free(obj->write.data.data_in); + g_free(obj->write.data.data_out); +} + +int jack_card_probe(JackCard *obj,int bits,int stereo,int rate) +{ + if (obj->jack_running) return BSIZE; + else if (jack_init(obj) == 0) return BSIZE; + else return -1; +} + +void jack_card_destroy(JackCard *obj) +{ + if (obj->jack_running) jack_client_close (obj->client); + snd_card_uninit(SND_CARD(obj)); + if (obj->read.buffer) { + jack_ringbuffer_free(obj->read.buffer); + obj->read.buffer = NULL; + } + if (obj->write.buffer) { + jack_ringbuffer_free(obj->write.buffer); + obj->write.buffer = NULL; + } + if (obj->read.phys_ports) { + g_free(obj->read.phys_ports); + obj->read.phys_ports = NULL; + } + if (obj->write.phys_ports) { + g_free(obj->write.phys_ports); + obj->write.phys_ports = NULL; + } +} + +gboolean jack_card_can_read(JackCard *obj) +{ + g_return_val_if_fail(obj->read.buffer!=NULL,0); + if (jack_ringbuffer_read_space(obj->read.buffer)>=(long)((double)obj->read.frames/obj->read.data.src_ratio)*sizeof(sample_t)) return TRUE; + else return FALSE; +} + +int jack_card_read(JackCard *obj,char *buf,int size) +{ + size_t bytes, can_read, i; + int error; + float norm, value; + + g_return_val_if_fail((obj->read.buffer!=NULL)&&(obj->read.src_state!=NULL),-1); + if (jack_init(obj) != 0) return -1; + size /= 2; + can_read = MIN(size, obj->read.frames); + // can_read = MIN(((long)((double)can_read / obj->read.data.src_ratio))*sizeof(sample_t), jack_ringbuffer_read_space(obj->read.buffer)); + can_read = ((long)((double)can_read / obj->read.data.src_ratio))*sizeof(sample_t); + obj->read.can_process = FALSE; + bytes = jack_ringbuffer_read (obj->read.buffer, (void *)obj->read.data.data_in, can_read); + obj->read.can_process = TRUE; + obj->read.data.input_frames = bytes / sizeof(sample_t); + can_read = MIN(size, obj->read.frames); + obj->read.data.output_frames = can_read; + if ((error = src_process(obj->read.src_state, &(obj->read.data))) != 0) + g_warning("error while samplerate conversion. error: %s", src_strerror(error)); + norm = obj->read.level*obj->level*(float)0x8000; + for (i=0; i < obj->read.data.output_frames_gen; i++) { + value = obj->read.data.data_out[i]*norm; + if (value >= 32767.0) + ((short*)buf)[i] = 32767; + else if (value <= -32768.0) + ((short*)buf)[i] = -32768; + else + ((short*)buf)[i] = (short)value; + } + bytes = obj->read.data.output_frames_gen * 2; + return bytes; +} + +int jack_card_write(JackCard *obj,char *buf,int size) +{ + size_t bytes, can_write, i; + int error; + float norm; + + g_return_val_if_fail((obj->write.buffer!=NULL)&&(obj->write.src_state!=NULL),-1); + if (jack_init(obj) != 0) return -1; + size /= 2; + can_write = MIN(size, obj->write.frames); + norm = obj->write.level*obj->level/(float)0x8000; + for (i=0; i<can_write; i++) { + obj->write.data.data_in[i] = (float)((short*)buf)[i]*norm; + } + obj->write.data.input_frames = can_write; + if ((error = src_process(obj->write.src_state, &(obj->write.data))) != 0) + g_warning("error while samplerate conversion. error: %s", src_strerror(error)); + obj->write.can_process = FALSE; + bytes = jack_ringbuffer_write (obj->write.buffer, (void *) obj->write.data.data_out, sizeof(sample_t)*obj->write.data.output_frames_gen); + obj->write.can_process = TRUE; + return bytes; +} + +void jack_card_set_level(JackCard *obj,gint way,gint a) +{ + switch(way){ + case SND_CARD_LEVEL_GENERAL: + obj->level = (float)a / 100.0; + break; + case SND_CARD_LEVEL_INPUT: + obj->read.level = (float)a / 100.0; + break; + case SND_CARD_LEVEL_OUTPUT: + obj->write.level = (float)a / 100.0; + break; + default: + g_warning("jack_card_set_level: unsupported command."); + } +} + +gint jack_card_get_level(JackCard *obj,gint way) +{ + gint value = 0; + + switch(way){ + case SND_CARD_LEVEL_GENERAL: + value = (gint)(obj->level*100.0); + break; + case SND_CARD_LEVEL_INPUT: + value = (gint)(obj->read.level*100.0); + break; + case SND_CARD_LEVEL_OUTPUT: + value = (gint)(obj->write.level*100.0); + break; + default: + g_warning("jack_card_get_level: unsupported command."); + } + return value; +} + +void jack_card_set_source(JackCard *obj,int source) +{ +} + +MSFilter *jack_card_create_read_filter(JackCard *card) +{ + MSFilter *f=ms_oss_read_new(); + ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); + return f; +} + +MSFilter *jack_card_create_write_filter(JackCard *card) +{ + MSFilter *f=ms_oss_write_new(); + ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); + return f; +} +SndCard * jack_card_new(jack_client_t *client) +{ + JackCard * obj; + SndCard *base; + + obj= g_new0(JackCard,1); + + if (!client) return NULL; + obj->client = client; + obj->jack_running = TRUE; + obj->jack_active = FALSE; + obj->can_process = FALSE; + obj->clear = TRUE; + obj->write.can_process = FALSE; + obj->write.open = FALSE; + obj->write.init = TRUE; + obj->write.port = NULL; + obj->write.phys_ports = NULL; + obj->write.buffer = NULL; + obj->read.can_process = FALSE; + obj->read.open = FALSE; + obj->read.init = TRUE; + obj->read.port = NULL; + obj->read.phys_ports = NULL; + obj->read.buffer = NULL; + + /* tell the JACK server to call `process()' whenever + there is work to be done. + */ + jack_set_process_callback (client, process, obj); + + /* tell the JACK server to call `jack_shutdown()' if + it ever shuts down, either entirely, or if it + just decides to stop calling us. + */ + jack_on_shutdown (client, jack_shutdown, obj); + + jack_set_sample_rate_callback (client, samplerate, obj); + + obj->rate = jack_get_sample_rate (client); + obj->buffer_size = jack_get_buffer_size(obj->client); + + jack_init(obj); + + base= SND_CARD(obj); + snd_card_init(base); + +#ifdef HAVE_GLIB + base->card_name=g_strdup_printf("JACK client"); +#else + base->card_name=malloc(100); + snprintf(base->card_name, 100, "JACK client"); +#endif + + base->_probe=(SndCardOpenFunc)jack_card_probe; + base->_open_r=(SndCardOpenFunc)jack_card_open_r; + base->_open_w=(SndCardOpenFunc)jack_card_open_w; + base->_can_read=(SndCardPollFunc)jack_card_can_read; + base->_set_blocking_mode=(SndCardSetBlockingModeFunc)jack_card_set_blocking_mode; + base->_read=(SndCardIOFunc)jack_card_read; + base->_write=(SndCardIOFunc)jack_card_write; + base->_close_r=(SndCardCloseFunc)jack_card_close_r; + base->_close_w=(SndCardCloseFunc)jack_card_close_w; + base->_set_rec_source=(SndCardMixerSetRecSourceFunc)jack_card_set_source; + base->_set_level=(SndCardMixerSetLevelFunc)jack_card_set_level; + base->_get_level=(SndCardMixerGetLevelFunc)jack_card_get_level; + base->_destroy=(SndCardDestroyFunc)jack_card_destroy; + base->_create_read_filter=(SndCardCreateFilterFunc)jack_card_create_read_filter; + base->_create_write_filter=(SndCardCreateFilterFunc)jack_card_create_write_filter; + + obj->read.buffer=NULL; + obj->write.buffer=NULL; + obj->buffer_size = 0; + obj->level = 1.0; + obj->write.level = 1.0; + obj->read.level = 1.0; + + return base; +} + + +gint jack_card_manager_init(SndCardManager *m, gint index) +{ + jack_client_t *client = NULL; + char* client_name; + + client_name=g_strdup_printf("linphone-%u", g_random_int()); + if ((client = jack_client_new (client_name))!= NULL) + { + g_message("Found Jack Daemon"); + g_free(client_name); + m->cards[index]=jack_card_new(client); + m->cards[index]->index=index; + return 1; + } else { + g_free(client_name); + return 0; + } +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h new file mode 100644 index 00000000..33ec46dc --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h @@ -0,0 +1,81 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + JACK support + Copyright (C) 2004 Tobias Gehrig tobias@gehrig.tk +*/ + +#ifndef JACK_CARD_H +#define JACK_CARD_H + +#include <config.h> + +#ifdef __JACK_ENABLED__ + +#include "sndcard.h" + +#include <jack/jack.h> +#include <jack/ringbuffer.h> + +#include <samplerate.h> + +typedef jack_default_audio_sample_t sample_t; + +typedef struct { + jack_port_t *port; + const char **phys_ports; + float level; + jack_ringbuffer_t *buffer; + gint channels; + gint rate; + SRC_STATE* src_state; + SRC_DATA data; + size_t frames; + gboolean can_process; + gboolean open; + gboolean init; +} jackcard_mode_t; + +struct _JackCard +{ + SndCard parent; + + jack_client_t *client; + gboolean jack_running; + gboolean jack_active; + float level; + jack_nframes_t buffer_size; + gint sample_size; + gint frame_size; + gint rate; + gboolean can_process; + gboolean clear; + + jackcard_mode_t read, write; +}; + +typedef struct _JackCard JackCard; + +SndCard * jack_card_new(jack_client_t *client); + +gint jack_card_manager_init(SndCardManager *m, gint index); + +#endif + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h new file mode 100644 index 00000000..3ccbab69 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h @@ -0,0 +1,130 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MEDIASTREAM_H +#define MEDIASTREAM_H + +#include "msrtprecv.h" +#include "msrtpsend.h" +#include "ms.h" +#include "msosswrite.h" +#include "msossread.h" +#include "msread.h" +#include "mswrite.h" +#include "mstimer.h" +#include "mscodec.h" +#ifdef HAVE_SPEEX +#include "msspeexdec.h" +#endif +#include "msringplayer.h" + + +struct _AudioStream +{ + MSSync *timer; + RtpSession *send_session; + RtpSession *recv_session; + MSFilter *soundread; + MSFilter *soundwrite; + MSFilter *encoder; + MSFilter *decoder; + MSFilter *rtprecv; + MSFilter *rtpsend; +}; + + +typedef struct _AudioStream AudioStream; + +struct _RingStream +{ + MSSync *timer; + MSFilter *source; + MSFilter *sndwrite; +}; + +typedef struct _RingStream RingStream; + +/* start a thread that does sampling->encoding->rtp_sending|rtp_receiving->decoding->playing */ +AudioStream *audio_stream_start (RtpProfile * prof, int locport, char *remip, + int remport, int profile, int jitt_comp); + +AudioStream *audio_stream_start_with_sndcards(RtpProfile * prof, int locport, char *remip4, + int remport, int profile, int jitt_comp, SndCard *playcard, SndCard *captcard); + +AudioStream *audio_stream_start_with_files (RtpProfile * prof, int locport, + char *remip4, int remport, + int profile, int jitt_comp, + gchar * infile, gchar * outfile); +void audio_stream_set_rtcp_information(AudioStream *st, const char *cname); + + +/* stop the above process*/ +void audio_stream_stop (AudioStream * stream); + +RingStream *ring_start (gchar * file, gint interval, SndCard *sndcard); +RingStream *ring_start_with_cb(gchar * file, gint interval, SndCard *sndcard, MSFilterNotifyFunc func,gpointer user_data); +void ring_stop (RingStream * stream); + +/* returns the latency in samples if the audio device with id dev_id is openable in full duplex mode, else 0 */ +gint test_audio_dev (int dev_id); + +/* send a dtmf */ +gint audio_stream_send_dtmf (AudioStream * stream, gchar dtmf); + +void audio_stream_set_default_card(int cardindex); + + +#ifdef VIDEO_ENABLED + +/***************** + Video Support + *****************/ + + + +struct _VideoStream +{ + MSSync *timer; + RtpSession *send_session; + RtpSession *recv_session; + MSFilter *source; + MSFilter *output; + MSFilter *encoder; + MSFilter *decoder; + MSFilter *rtprecv; + MSFilter *rtpsend; + gboolean show_local; +}; + + +typedef struct _VideoStream VideoStream; + +VideoStream *video_stream_start(RtpProfile *profile, int locport, char *remip4, int remport, + int payload, int jitt_comp, gboolean show_local, const gchar *source, const gchar *device); +void video_stream_set_rtcp_information(VideoStream *st, const char *cname); +void video_stream_stop (VideoStream * stream); + +VideoStream * video_preview_start(const gchar *source, const gchar *device); +void video_preview_stop(VideoStream *stream); + +#endif + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c new file mode 100644 index 00000000..cfcafa33 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c @@ -0,0 +1,342 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ms.h" +#include "sndcard.h" +#include "mscodec.h" + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef VIDEO_ENABLED +extern void ms_video_source_register_all(); +#endif +#ifdef HAVE_ILBC +extern void ms_ilbc_codec_init(); +#endif + +/** + * ms_init: + * + * + * Initialize the mediastreamer. This must be the first function called in a program + * using the mediastreamer library. + * + * + */ +void ms_init() +{ + if (!g_thread_supported()) g_thread_init (NULL); +#ifdef HAVE_GLIB + if (!g_module_supported()){ + g_error("GModule is not supported."); + } +#endif + /* initialize the oss subsystem */ + snd_card_manager_init(snd_card_manager); + /* register the statically linked codecs */ + ms_codec_register_all(); +#ifdef VIDEO_ENABLED + ms_video_source_register_all(); +#endif +#ifdef HAVE_ILBC + ms_ilbc_codec_init(); +#endif +} + + +static gint compare(gconstpointer a, gconstpointer b) +{ + MSFilter *f1=(MSFilter*)a,*f2=(MSFilter*)b; + if (f1->klass<f2->klass) return -1; + if (f1->klass==f2->klass) return 0; + /* if f1->klass>f2->klass ....*/ + return 1; +} + +static GList *g_list_append_if_new(GList *l,gpointer data) +{ + GList *res=l; + if (g_list_find(res,data)==NULL) + res=g_list_append(res,data); + return(res); +} + +static GList *get_nexts(MSFilter *f,GList *l) +{ + int i; + MSFifo *fifo; + MSQueue *q; + GList *res=l; + + /* check fifos*/ + for (i=0;i <f->klass->max_foutputs;i++) + { + fifo=f->outfifos[i]; + if (fifo!=NULL) res=g_list_append_if_new(res,(gpointer)fifo->next_data); + } + /* check queues*/ + for (i=0;i <f->klass->max_qoutputs;i++) + { + q=f->outqueues[i]; + if (q!=NULL) res=g_list_append_if_new(res,(gpointer)q->next_data); + } + return(res); +} + +/* compile graphs attached to a sync source*/ +int ms_compile(MSSync *sync) +{ + int i; + GList *list1=NULL,*list2=NULL,*elem; + GList *proc_chain=NULL; + MSFilter *f; + + /* first free the old list if we are just updating*/ + if (sync->execution_list!=NULL) g_list_free(sync->execution_list); + /* get the list of filters attached to this sync*/ + for (i=0;i<sync->filters;i++) + { + /* //printf("found filter !\n"); */ + list1=g_list_append(list1,sync->attached_filters[i]); + } + /* find the processing chain */ + while (list1!=NULL) + { + list2=NULL; + /* sort the list by types of filter*/ + list1=g_list_sort(list1,compare); + /* save into the processing chain list*/ + /* //printf("list1 :%i elements\n",g_list_length(list1)); */ + proc_chain=g_list_concat(proc_chain,list1); + /* get all following filters. They are appended to list2*/ + elem=list1; + while (elem!=NULL) + { + f=(MSFilter*)(elem->data); + /* check if filter 's status */ + if (f->klass->attributes & FILTER_CAN_SYNC) + { + sync->samples_per_tick=0; + } + list2=get_nexts(f,list2); + elem=g_list_next(elem); + } + list1=list2; + } + sync->execution_list=proc_chain; + sync->flags&=~MS_SYNC_NEED_UPDATE; + ms_trace("%i filters successfully compiled in a processing chain.",g_list_length(sync->execution_list)); + return 0; +} + +/*execute the processing chain attached to a sync source. It is called as a thread by ms_main()*/ +void *ms_thread_run(void *sync_ptr) +{ + MSSync *sync=(MSSync*) sync_ptr; + GList *filter; + MSFilter *f; + + + ms_sync_lock(sync); + while(sync->run) + { + /* //g_message("sync->run=%i",sync->run); */ + if (sync->samples_per_tick==0) ms_sync_suspend(sync); + if (sync->flags & MS_SYNC_NEED_UPDATE){ + ms_compile(sync); + ms_sync_setup(sync); + } + filter=sync->execution_list; + ms_sync_unlock(sync); + /* //ms_trace("Calling synchronisation"); */ + ms_sync_synchronize(sync); + while(filter!=NULL) + { + f=(MSFilter*)filter->data; + if (MS_FILTER_GET_CLASS(f)->attributes & FILTER_IS_SOURCE) + { + /* execute it once */ + ms_trace("Running source filter %s.",f->klass->name); + ms_filter_process(f); + } + else + { + /* make the filter process its input data until it has no more */ + while ( ms_filter_fifos_have_data(f) || ms_filter_queues_have_data(f) ) + { + ms_trace("Running filter %s.",f->klass->name); + ms_filter_process(f); + } + } + filter=g_list_next(filter); + } + ms_sync_lock(sync); + } + g_cond_signal(sync->stop_cond); /* signal that the sync thread has finished */ + ms_sync_unlock(sync); + g_message("Mediastreamer processing thread is exiting."); + return NULL; +} + +/* stop the processing chain attached to a sync source.*/ +void ms_thread_stop(MSSync *sync) +{ + if (sync->thread!=NULL) + { + if (sync->samples_per_tick==0) + { + /* to wakeup the thread */ + /* //g_cond_signal(sync->thread_cond); */ + } + g_mutex_lock(sync->lock); + sync->run=0; + sync->thread=NULL; + g_cond_wait(sync->stop_cond,sync->lock); + g_mutex_unlock(sync->lock); + } + /* //g_message("ms_thread_stop() finished."); */ +} + +/** + * ms_start: + * @sync: A synchronisation source to be started. + * + * Starts a thread that will shedule all processing chains attached to the synchronisation source @sync. + * + * + */ +void ms_start(MSSync *sync) +{ + if (sync->run==1) return; /*already running*/ + ms_compile(sync); + ms_sync_setup(sync); + /* this is to avoid race conditions, for example: + ms_start(sync); + ms_oss_write_start(ossw); + here tge ossw filter need to be compiled to run ms_oss_write_start() + */ + ms_trace("ms_start: creating new thread."); + sync->run=1; + sync->thread=g_thread_create((GThreadFunc)ms_thread_run,(gpointer)sync,TRUE,NULL); + if (sync->thread==NULL){ + g_warning("Could not create thread !"); + } +} + +/** + * ms_stop: + * @sync: A synchronisation source to be stopped. + * + * Stop the thread that was sheduling the processing chains attached to the synchronisation source @sync. + * The processing chains are kept unchanged, no object is freed. The synchronisation source can be restarted using ms_start(). + * + * + */ +void ms_stop(MSSync *sync) +{ + ms_thread_stop(sync); + ms_sync_unsetup(sync); +} + + +gint ms_load_plugin(gchar *path) +{ +#ifdef HAVE_GLIB + g_module_open(path,0); +#endif + return 0; +} + +gchar * ms_proc_get_param(gchar *parameter) +{ + gchar *file; + int fd; + int err,len; + gchar *p,*begin,*end; + gchar *ret; + fd=open("/proc/cpuinfo",O_RDONLY); + if (fd<0){ + g_warning("Could not open /proc/cpuinfo."); + return NULL; + } + file=g_malloc(1024); + err=read(fd,file,1024); + file[err-1]='\0'; + /* find the parameter */ + p=strstr(file,parameter); + if (p==NULL){ + /* parameter not found */ + g_free(file); + return NULL; + } + /* find the following ':' */ + p=strchr(p,':'); + if (p==NULL){ + g_free(file); + return NULL; + } + /* find the value*/ + begin=p+2; + end=strchr(begin,'\n'); + if (end==NULL) end=strchr(begin,'\0'); + len=end-begin+1; + ret=g_malloc(len+1); + snprintf(ret,len,"%s",begin); + /* //printf("%s=%s\n",parameter,ret); */ + g_free(file); + return ret; +} + +gint ms_proc_get_type() +{ + static int proc_type=0; + gchar *value; + if (proc_type==0){ + value=ms_proc_get_param("cpu family"); + if (value!=NULL) { + proc_type=atoi(value); + g_free(value); + }else return -1; + } + return proc_type; +} + +gint ms_proc_get_speed() +{ + char *value; + static int proc_speed=0; + if (proc_speed==0){ + value=ms_proc_get_param("cpu MHz"); + if (value!=NULL){ + proc_speed=atoi(value); + g_free(value); + }else return -1; + } + /* //printf("proc_speed=%i\n",proc_speed); */ + return proc_speed; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h new file mode 100644 index 00000000..51c69b96 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h @@ -0,0 +1,81 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#ifndef MS_H +#define MS_H +#include "msfilter.h" +#include "mssync.h" + + +void ms_init(); + +/* compile graphs attached to a sync source*/ +int ms_compile(MSSync *source); + + +/* stop the processing chain attached to a sync source.*/ +void ms_thread_stop(MSSync *sync); + + +/** + * function_name:ms_thread_run + * @sync: The synchronization source for all the set of graphs to run. + * + * Execute the processing chain attached to a sync source. This function loops indefinitely. + * The media streamer programmer can choose to execute this function directly, or to call ms_start(), + * that will start a thread for the synchronisation source. + * + * Returns: no return value. + */ +void *ms_thread_run(void *sync); + + +/** + * function_name:ms_start + * @sync: A synchronisation source to be started. + * + * Starts a thread that will shedule all processing chains attached to the synchronisation source @sync. + * + * Returns: no return value. + */ +void ms_start(MSSync *sync); + + +/** + * function_name:ms_stop + * @sync: A synchronisation source to be stopped. + * + * Stop the thread that was sheduling the processing chains attached to the synchronisation source @sync. + * The processing chains are kept unchanged, no object is freed. The synchronisation source can be restarted using ms_start(). + * + * Returns: no return value. + */ +void ms_stop(MSSync *sync); + + +gchar * ms_proc_get_param(gchar *parameter); +gint ms_proc_get_type(); +gint ms_proc_get_speed(); + + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c new file mode 100644 index 00000000..70cc906e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c @@ -0,0 +1,132 @@ + /* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <msAlawdec.h> +#include <g711common.h> + +extern MSFilter * ms_ALAWencoder_new(void); + +MSCodecInfo ALAWinfo={ + { + "ALAW codec", + 0, + MS_FILTER_AUDIO_CODEC, + ms_ALAWencoder_new, + "This is the classic A-law codec. Good quality, but only usable with high speed network connections." + }, + ms_ALAWencoder_new, + ms_ALAWdecoder_new, + 320, + 160, + 64000, + 8000, + 8, + "PCMA", + 1, + 1, +}; + +static MSALAWDecoderClass *ms_ALAWdecoder_class=NULL; + +MSFilter * ms_ALAWdecoder_new(void) +{ + MSALAWDecoder *r; + + r=g_new(MSALAWDecoder,1); + ms_ALAWdecoder_init(r); + if (ms_ALAWdecoder_class==NULL) + { + ms_ALAWdecoder_class=g_new(MSALAWDecoderClass,1); + ms_ALAWdecoder_class_init(ms_ALAWdecoder_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ALAWdecoder_class); + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_ALAWdecoder_init(MSALAWDecoder *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->outfifos=r->f_outputs; + MS_FILTER(r)->r_mingran=ALAW_DECODER_RMAXGRAN; + memset(r->f_inputs,0,sizeof(MSFifo*)*MSALAWDECODER_MAX_INPUTS); + memset(r->f_outputs,0,sizeof(MSFifo*)*MSALAWDECODER_MAX_INPUTS); + +} + +void ms_ALAWdecoder_class_init(MSALAWDecoderClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ALAWDecoder"); + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ALAWinfo; + MS_FILTER_CLASS(klass)->max_finputs=MSALAWDECODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSALAWDECODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=ALAW_DECODER_RMAXGRAN; + MS_FILTER_CLASS(klass)->w_maxgran=ALAW_DECODER_WMAXGRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ALAWdecoder_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ALAWdecoder_process; +} + +void ms_ALAWdecoder_process(MSALAWDecoder *r) +{ + MSFifo *fi,*fo; + int inlen,outlen; + gchar *s,*d; + int i; + /* process output fifos, but there is only one for this class of filter*/ + + /* this is the simplest process function design: + the filter declares a r_mingran of ALAW_DECODER_RMAXGRAN, so the mediastreamer's + scheduler will call the process function each time there is ALAW_DECODER_RMAXGRAN + bytes to read in the input fifo. If there is more, then it will call it several + time in order to the fifo to be completetly processed. + This is very simple, but not very efficient because of the multiple call function + of MSFilterProcessFunc that may happen. + The MSAlawEncoder implements another design; see it. + */ + + fi=r->f_inputs[0]; + fo=r->f_outputs[0]; + g_return_if_fail(fi!=NULL); + g_return_if_fail(fo!=NULL); + + inlen=ms_fifo_get_read_ptr(fi,ALAW_DECODER_RMAXGRAN,(void**)&s); + if (s==NULL) return; + outlen=ms_fifo_get_write_ptr(fo,ALAW_DECODER_WMAXGRAN,(void**)&d); + if (d!=NULL) + { + for(i=0;i<ALAW_DECODER_RMAXGRAN;i++) + { + ((gint16*)d)[i]=alaw_to_s16( (unsigned char) s[i]); + } + } + else g_warning("MSALAWDecoder: Discarding samples !!"); + +} + + + +void ms_ALAWdecoder_destroy( MSALAWDecoder *obj) +{ + g_free(obj); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h new file mode 100644 index 00000000..7db4c750 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h @@ -0,0 +1,65 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSALAWDECODER_H +#define MSALAWDECODER_H + +#include <msfilter.h> +#include <mscodec.h> + +/*this is the class that implements a ALAWdecoder filter*/ + +#define MSALAWDECODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSALAWDecoder +{ + /* the MSALAWDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSALAWDecoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSALAWDECODER_MAX_INPUTS]; + MSFifo *f_outputs[MSALAWDECODER_MAX_INPUTS]; +} MSALAWDecoder; + +typedef struct _MSALAWDecoderClass +{ + /* the MSALAWDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSALAWDecoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSALAWDecoderClass; + +/* PUBLIC */ +#define MS_ALAWDECODER(filter) ((MSALAWDecoder*)(filter)) +#define MS_ALAWDECODER_CLASS(klass) ((MSALAWDecoderClass*)(klass)) +MSFilter * ms_ALAWdecoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_ALAWdecoder_init(MSALAWDecoder *r); +void ms_ALAWdecoder_class_init(MSALAWDecoderClass *klass); +void ms_ALAWdecoder_destroy( MSALAWDecoder *obj); +void ms_ALAWdecoder_process(MSALAWDecoder *r); + +/* tuning parameters :*/ +#define ALAW_DECODER_WMAXGRAN 320 +#define ALAW_DECODER_RMAXGRAN 160 + +extern MSCodecInfo ALAWinfo; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c new file mode 100644 index 00000000..fd1f9abe --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c @@ -0,0 +1,124 @@ + /* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "msAlawenc.h" +#include "g711common.h" + +extern MSCodecInfo ALAWinfo; + +static MSALAWEncoderClass *ms_ALAWencoder_class=NULL; + +MSFilter * ms_ALAWencoder_new(void) +{ + MSALAWEncoder *r; + + r=g_new(MSALAWEncoder,1); + ms_ALAWencoder_init(r); + if (ms_ALAWencoder_class==NULL) + { + ms_ALAWencoder_class=g_new(MSALAWEncoderClass,1); + ms_ALAWencoder_class_init(ms_ALAWencoder_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ALAWencoder_class); + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_ALAWencoder_init(MSALAWEncoder *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->outfifos=r->f_outputs; + MS_FILTER(r)->r_mingran=ALAW_ENCODER_RMAXGRAN; /* the filter can be called as soon as there is + something to process */ + memset(r->f_inputs,0,sizeof(MSFifo*)*MSALAWENCODER_MAX_INPUTS); + memset(r->f_outputs,0,sizeof(MSFifo*)*MSALAWENCODER_MAX_INPUTS); + +} + +void ms_ALAWencoder_class_init(MSALAWEncoderClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ALAWEncoder"); + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ALAWinfo; + MS_FILTER_CLASS(klass)->max_finputs=MSALAWENCODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSALAWENCODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=ALAW_ENCODER_RMAXGRAN; + MS_FILTER_CLASS(klass)->w_maxgran=ALAW_ENCODER_WMAXGRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ALAWencoder_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ALAWencoder_process; +} + +void ms_ALAWencoder_process(MSALAWEncoder *r) +{ + MSFifo *fi,*fo; + int inlen,outlen; + gchar *s,*d; + int i; + /* process output fifos, but there is only one for this class of filter*/ + + /* this is the sophisticated design of the process function: + Here the filter declares that it can be called as soon as there is something + to read on the input fifo by setting r_mingran=0. + Then it ask for the fifo to get as many data as possible by calling: + inlen=ms_fifo_get_read_ptr(fi,0,(void**)&s); + This avoid multiple call to the process function to process all data available + on the input fifo... but the writing of the process function is a bit + more difficult, because althoug ms_fifo_get_read_ptr() returns N bytes, + we cannot ask ms_fifo_get_write_ptr to return N bytes if + N>MS_FILTER_CLASS(klass)->w_maxgran. This is forbidden by the MSFifo + mechanism. + This is an open issue. + For the moment what is done here is that ms_fifo_get_write_ptr() is called + several time with its maximum granularity in order to try to write the output. + ... + One solution: + -create a new function ms_fifo_get_rw_ptr(fifo1,p1, fifo2,p2) to + return the number of bytes able to being processed according to the input + and output fifo, and their respective data pointers + */ + + + fi=r->f_inputs[0]; + fo=r->f_outputs[0]; + + inlen=ms_fifo_get_read_ptr(fi,ALAW_ENCODER_RMAXGRAN,(void**)&s); + if (s==NULL) return; + outlen=ms_fifo_get_write_ptr(fo,ALAW_ENCODER_WMAXGRAN,(void**)&d); + if (d!=NULL) + { + for(i=0;i<ALAW_ENCODER_WMAXGRAN;i++) + { + d[i]=s16_to_alaw( *((gint16*)s) ); + s+=2; + } + } + else g_warning("MSALAWDecoder: Discarding samples !!"); + +} + + + +void ms_ALAWencoder_destroy( MSALAWEncoder *obj) +{ + g_free(obj); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h new file mode 100644 index 00000000..608a9884 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h @@ -0,0 +1,64 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSALAWENCODER_H +#define MSALAWENCODER_H + +#include "mscodec.h" + + +/*this is the class that implements a ALAWencoder filter*/ + +#define MSALAWENCODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSALAWEncoder +{ + /* the MSALAWEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSALAWEncoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSALAWENCODER_MAX_INPUTS]; + MSFifo *f_outputs[MSALAWENCODER_MAX_INPUTS]; +} MSALAWEncoder; + +typedef struct _MSALAWEncoderClass +{ + /* the MSALAWEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSALAWEncoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSALAWEncoderClass; + +/* PUBLIC */ +#define MS_ALAWENCODER(filter) ((MSALAWEncoder*)(filter)) +#define MS_ALAWENCODER_CLASS(klass) ((MSALAWEncoderClass*)(klass)) +MSFilter * ms_ALAWencoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_ALAWencoder_init(MSALAWEncoder *r); +void ms_ALAWencoder_class_init(MSALAWEncoderClass *klass); +void ms_ALAWencoder_destroy( MSALAWEncoder *obj); +void ms_ALAWencoder_process(MSALAWEncoder *r); + +/* tuning parameters :*/ +#define ALAW_ENCODER_WMAXGRAN 160 +#define ALAW_ENCODER_RMAXGRAN 320 + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h new file mode 100644 index 00000000..e73fb332 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h @@ -0,0 +1,64 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSGSMDECODER_H +#define MSGSMDECODER_H + +#include <msfilter.h> +#include <mscodec.h> +#include <gsm.h> + +/*this is the class that implements a GSMdecoder filter*/ + +#define MSGSMDECODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSGSMDecoder +{ + /* the MSGSMDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSGSMDecoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSGSMDECODER_MAX_INPUTS]; + MSFifo *f_outputs[MSGSMDECODER_MAX_INPUTS]; + gsm gsm_handle; +} MSGSMDecoder; + +typedef struct _MSGSMDecoderClass +{ + /* the MSGSMDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSGSMDecoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSGSMDecoderClass; + +/* PUBLIC */ +#define MS_GSMDECODER(filter) ((MSGSMDecoder*)(filter)) +#define MS_GSMDECODER_CLASS(klass) ((MSGSMDecoderClass*)(klass)) +MSFilter * ms_GSMdecoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_GSMdecoder_init(MSGSMDecoder *r); +void ms_GSMdecoder_class_init(MSGSMDecoderClass *klass); +void ms_GSMdecoder_destroy( MSGSMDecoder *obj); +void ms_GSMdecoder_process(MSGSMDecoder *r); + +extern MSCodecInfo GSMinfo; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h new file mode 100644 index 00000000..2deae387 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h @@ -0,0 +1,61 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSGSMENCODER_H +#define MSGSMENCODER_H + +#include "msfilter.h" +#include <gsm.h> + +/*this is the class that implements a GSMencoder filter*/ + +#define MSGSMENCODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSGSMEncoder +{ + /* the MSGSMEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSGSMEncoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSGSMENCODER_MAX_INPUTS]; + MSFifo *f_outputs[MSGSMENCODER_MAX_INPUTS]; + gsm gsm_handle; +} MSGSMEncoder; + +typedef struct _MSGSMEncoderClass +{ + /* the MSGSMEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSGSMEncoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSGSMEncoderClass; + +/* PUBLIC */ +#define MS_GSMENCODER(filter) ((MSGSMEncoder*)(filter)) +#define MS_GSMENCODER_CLASS(klass) ((MSGSMEncoderClass*)(klass)) +MSFilter * ms_GSMencoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_GSMencoder_init(MSGSMEncoder *r); +void ms_GSMencoder_class_init(MSGSMEncoderClass *klass); +void ms_GSMencoder_destroy( MSGSMEncoder *obj); +void ms_GSMencoder_process(MSGSMEncoder *r); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h new file mode 100644 index 00000000..59d9deca --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h @@ -0,0 +1,64 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSLPC10DECODER_H +#define MSLPC10DECODER_H + +#include <msfilter.h> +#include <mscodec.h> +#include <lpc10.h> + +/*this is the class that implements a LPC10decoder filter*/ + +#define MSLPC10DECODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSLPC10Decoder +{ + /* the MSLPC10Decoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSLPC10Decoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSLPC10DECODER_MAX_INPUTS]; + MSFifo *f_outputs[MSLPC10DECODER_MAX_INPUTS]; + struct lpc10_decoder_state *lpc10_dec; +} MSLPC10Decoder; + +typedef struct _MSLPC10DecoderClass +{ + /* the MSLPC10Decoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSLPC10Decoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSLPC10DecoderClass; + +/* PUBLIC */ +#define MS_LPC10DECODER(filter) ((MSLPC10Decoder*)(filter)) +#define MS_LPC10DECODER_CLASS(klass) ((MSLPC10DecoderClass*)(klass)) +MSFilter * ms_LPC10decoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_LPC10decoder_init(MSLPC10Decoder *r); +void ms_LPC10decoder_class_init(MSLPC10DecoderClass *klass); +void ms_LPC10decoder_destroy( MSLPC10Decoder *obj); +void ms_LPC10decoder_process(MSLPC10Decoder *r); + +extern MSCodecInfo LPC10info; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h new file mode 100644 index 00000000..4db16436 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h @@ -0,0 +1,74 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSLPC10ENCODER_H +#define MSLPC10ENCODER_H + +#include "mscodec.h" + + +int +read_16bit_samples(gint16 int16samples[], float speech[], int n); + +int +write_16bit_samples(gint16 int16samples[], float speech[], int n); + +void +write_bits(unsigned char *data, gint32 *bits, int len); + +int +read_bits(unsigned char *data, gint32 *bits, int len); + + +/*this is the class that implements a LPC10encoder filter*/ + +#define MSLPC10ENCODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSLPC10Encoder +{ + /* the MSLPC10Encoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSLPC10Encoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSLPC10ENCODER_MAX_INPUTS]; + MSFifo *f_outputs[MSLPC10ENCODER_MAX_INPUTS]; + struct lpc10_encoder_state *lpc10_enc; +} MSLPC10Encoder; + +typedef struct _MSLPC10EncoderClass +{ + /* the MSLPC10Encoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSLPC10Encoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSLPC10EncoderClass; + +/* PUBLIC */ +#define MS_LPC10ENCODER(filter) ((MSLPC10Encoder*)(filter)) +#define MS_LPC10ENCODER_CLASS(klass) ((MSLPC10EncoderClass*)(klass)) +MSFilter * ms_LPC10encoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_LPC10encoder_init(MSLPC10Encoder *r); +void ms_LPC10encoder_class_init(MSLPC10EncoderClass *klass); +void ms_LPC10encoder_destroy( MSLPC10Encoder *obj); +void ms_LPC10encoder_process(MSLPC10Encoder *r); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c new file mode 100644 index 00000000..500f2389 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c @@ -0,0 +1,130 @@ + /* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <msMUlawdec.h> +#include <g711common.h> + +extern MSFilter * ms_MULAWencoder_new(void); + +MSCodecInfo MULAWinfo={ + { + "MULAW codec", + 0, + MS_FILTER_AUDIO_CODEC, + ms_MULAWencoder_new, + "This is the classic Mu-law codec. Good quality, but only usable with high speed network connections." + }, + ms_MULAWencoder_new, + ms_MULAWdecoder_new, + 320, + 160, + 64000, + 8000, + 0, + "PCMU", + 1, + 1 +}; + +static MSMULAWDecoderClass *ms_MULAWdecoder_class=NULL; + +MSFilter * ms_MULAWdecoder_new(void) +{ + MSMULAWDecoder *r; + + r=g_new(MSMULAWDecoder,1); + ms_MULAWdecoder_init(r); + if (ms_MULAWdecoder_class==NULL) + { + ms_MULAWdecoder_class=g_new(MSMULAWDecoderClass,1); + ms_MULAWdecoder_class_init(ms_MULAWdecoder_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_MULAWdecoder_class); + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_MULAWdecoder_init(MSMULAWDecoder *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->outfifos=r->f_outputs; + MS_FILTER(r)->r_mingran=MULAW_DECODER_RMAXGRAN; + memset(r->f_inputs,0,sizeof(MSFifo*)*MSMULAWDECODER_MAX_INPUTS); + memset(r->f_outputs,0,sizeof(MSFifo*)*MSMULAWDECODER_MAX_INPUTS); + +} + +void ms_MULAWdecoder_class_init(MSMULAWDecoderClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"MULAWDecoder"); + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&MULAWinfo; + MS_FILTER_CLASS(klass)->max_finputs=MSMULAWDECODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSMULAWDECODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=MULAW_DECODER_RMAXGRAN; + MS_FILTER_CLASS(klass)->w_maxgran=MULAW_DECODER_WMAXGRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_MULAWdecoder_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_MULAWdecoder_process; +} + +void ms_MULAWdecoder_process(MSMULAWDecoder *r) +{ + MSFifo *fi,*fo; + int inlen,outlen; + gchar *s,*d; + int i; + /* process output fifos, but there is only one for this class of filter*/ + + /* this is the simplest process function design: + the filter declares a r_mingran of MULAW_DECODER_RMAXGRAN, so the mediastreamer's + scheduler will call the process function each time there is MULAW_DECODER_RMAXGRAN + bytes to read in the input fifo. If there is more, then it will call it several + time in order to the fifo to be completetly processed. + This is very simple, but not very efficient because of the multiple call function + of MSFilterProcessFunc that may happen. + The MSAlawEncoder implements another design; see it. + */ + + fi=r->f_inputs[0]; + fo=r->f_outputs[0]; + + inlen=ms_fifo_get_read_ptr(fi,MULAW_DECODER_RMAXGRAN,(void**)&s); + if (s==NULL) g_error("ms_MULAWdecoder_process: internal error."); + outlen=ms_fifo_get_write_ptr(fo,MULAW_DECODER_WMAXGRAN,(void**)&d); + if (d!=NULL) + { + for(i=0;i<MULAW_DECODER_RMAXGRAN;i++) + { + *((gint16*)d)=ulaw_to_s16( (unsigned char) s[i]); + d+=2; + } + } + else g_warning("MSMULAWDecoder: Discarding samples !!"); +} + + + +void ms_MULAWdecoder_destroy( MSMULAWDecoder *obj) +{ + g_free(obj); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h new file mode 100644 index 00000000..c135d21d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h @@ -0,0 +1,66 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSMULAWDECODER_H +#define MSMULAWDECODER_H + +#include <msfilter.h> +#include <mscodec.h> + +/*this is the class that implements a MULAWdecoder filter*/ + +#define MSMULAWDECODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSMULAWDecoder +{ + /* the MSMULAWDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSMULAWDecoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSMULAWDECODER_MAX_INPUTS]; + MSFifo *f_outputs[MSMULAWDECODER_MAX_INPUTS]; +} MSMULAWDecoder; + +typedef struct _MSMULAWDecoderClass +{ + /* the MSMULAWDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSMULAWDecoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSMULAWDecoderClass; + +/* PUBLIC */ +#define MS_MULAWDECODER(filter) ((MSMULAWDecoder*)(filter)) +#define MS_MULAWDECODER_CLASS(klass) ((MSMULAWDecoderClass*)(klass)) +MSFilter * ms_MULAWdecoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_MULAWdecoder_init(MSMULAWDecoder *r); +void ms_MULAWdecoder_class_init(MSMULAWDecoderClass *klass); +void ms_MULAWdecoder_destroy( MSMULAWDecoder *obj); +void ms_MULAWdecoder_process(MSMULAWDecoder *r); + +/* tuning parameters :*/ +#define MULAW_DECODER_WMAXGRAN 320 +#define MULAW_DECODER_RMAXGRAN 160 + +extern MSCodecInfo MULAWinfo; + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c new file mode 100644 index 00000000..2f740d89 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c @@ -0,0 +1,99 @@ + /* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "msMUlawenc.h" +#include "g711common.h" + +extern MSCodecInfo MULAWinfo; + +static MSMULAWEncoderClass *ms_MULAWencoder_class=NULL; + +MSFilter * ms_MULAWencoder_new(void) +{ + MSMULAWEncoder *r; + + r=g_new(MSMULAWEncoder,1); + ms_MULAWencoder_init(r); + if (ms_MULAWencoder_class==NULL) + { + ms_MULAWencoder_class=g_new(MSMULAWEncoderClass,1); + ms_MULAWencoder_class_init(ms_MULAWencoder_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_MULAWencoder_class); + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_MULAWencoder_init(MSMULAWEncoder *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->outfifos=r->f_outputs; + MS_FILTER(r)->r_mingran=MULAW_ENCODER_RMAXGRAN; /* the filter can be called as soon as there is + something to process */ + memset(r->f_inputs,0,sizeof(MSFifo*)*MSMULAWENCODER_MAX_INPUTS); + memset(r->f_outputs,0,sizeof(MSFifo*)*MSMULAWENCODER_MAX_INPUTS); + +} + +void ms_MULAWencoder_class_init(MSMULAWEncoderClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"MULAWEncoder"); + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&MULAWinfo; + MS_FILTER_CLASS(klass)->max_finputs=MSMULAWENCODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSMULAWENCODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=MULAW_ENCODER_RMAXGRAN; + MS_FILTER_CLASS(klass)->w_maxgran=MULAW_ENCODER_WMAXGRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_MULAWencoder_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_MULAWencoder_process; +} + +void ms_MULAWencoder_process(MSMULAWEncoder *r) +{ + MSFifo *fi,*fo; + int inlen,outlen; + gchar *s,*d; + int i; + /* process output fifos, but there is only one for this class of filter*/ + + fi=r->f_inputs[0]; + fo=r->f_outputs[0]; + inlen=ms_fifo_get_read_ptr(fi,MULAW_ENCODER_RMAXGRAN,(void**)&s); + outlen=ms_fifo_get_write_ptr(fo,MULAW_ENCODER_WMAXGRAN,(void**)&d); + if (d!=NULL) + { + for(i=0;i<MULAW_ENCODER_WMAXGRAN;i++) + { + d[i]=s16_to_ulaw( *((gint16*)s) ); + s+=2; + } + } + else g_warning("MSMULAWDecoder: Discarding samples !!"); +} + + + +void ms_MULAWencoder_destroy( MSMULAWEncoder *obj) +{ + g_free(obj); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h new file mode 100644 index 00000000..52f76661 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h @@ -0,0 +1,63 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSMULAWENCODER_H +#define MSMULAWENCODER_H + +#include "mscodec.h" + + +/*this is the class that implements a MULAWencoder filter*/ + +#define MSMULAWENCODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSMULAWEncoder +{ + /* the MSMULAWEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSMULAWEncoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSMULAWENCODER_MAX_INPUTS]; + MSFifo *f_outputs[MSMULAWENCODER_MAX_INPUTS]; +} MSMULAWEncoder; + +typedef struct _MSMULAWEncoderClass +{ + /* the MSMULAWEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSMULAWEncoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSMULAWEncoderClass; + +/* PUBLIC */ +#define MS_MULAWENCODER(filter) ((MSMULAWEncoder*)(filter)) +#define MS_MULAWENCODER_CLASS(klass) ((MSMULAWEncoderClass*)(klass)) +MSFilter * ms_MULAWencoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_MULAWencoder_init(MSMULAWEncoder *r); +void ms_MULAWencoder_class_init(MSMULAWEncoderClass *klass); +void ms_MULAWencoder_destroy( MSMULAWEncoder *obj); +void ms_MULAWencoder_process(MSMULAWEncoder *r); + +/* tuning parameters :*/ +#define MULAW_ENCODER_WMAXGRAN 160 +#define MULAW_ENCODER_RMAXGRAN 320 + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h new file mode 100644 index 00000000..e7c880be --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h @@ -0,0 +1,87 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSAVDECODER_H +#define MSAVDECODER_H + +#include "msfilter.h" + +
+#include <avcodec.h> + +/*this is the class that implements a AVdecoder filter*/ + +#define MSAVDECODER_MAX_INPUTS 1 /* max output per filter*/ + + +struct _MSAVDecoder +{ + /* the MSAVDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSAVDecoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSQueue *q_inputs[MSAVDECODER_MAX_INPUTS]; + MSQueue *q_outputs[MSAVDECODER_MAX_INPUTS]; + AVCodec *av_codec; /*the AVCodec from which this MSFilter is related */ + AVCodecContext av_context; /* the context of the AVCodec */ + gint av_opened; + int output_pix_fmt; + int width; + int height; + int skip_gob; + unsigned char buf_compressed[100000]; + int buf_size; + MSBuffer *obufwrap; /* alternate buffer, when format change is needed*/ +}; + +typedef struct _MSAVDecoder MSAVDecoder; + +struct _MSAVDecoderClass +{ + /* the MSAVDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSAVDecoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +}; + +typedef struct _MSAVDecoderClass MSAVDecoderClass; + +/* PUBLIC */ +#define MS_AVDECODER(filter) ((MSAVDecoder*)(filter)) +#define MS_AVDECODER_CLASS(klass) ((MSAVDecoderClass*)(klass)) + +MSFilter *ms_h263_decoder_new(); +MSFilter *ms_mpeg_decoder_new(); +MSFilter *ms_mpeg4_decoder_new(); +MSFilter * ms_AVdecoder_new_with_codec(enum CodecID codec_id); + +gint ms_AVdecoder_set_format(MSAVDecoder *dec, gchar *fmt); +void ms_AVdecoder_set_width(MSAVDecoder *av,gint w); +void ms_AVdecoder_set_height(MSAVDecoder *av,gint h); + +/* FOR INTERNAL USE*/ +void ms_AVdecoder_init(MSAVDecoder *r, AVCodec *codec); +void ms_AVdecoder_uninit(MSAVDecoder *enc); +void ms_AVdecoder_class_init(MSAVDecoderClass *klass); +void ms_AVdecoder_destroy( MSAVDecoder *obj); +void ms_AVdecoder_process(MSAVDecoder *r); + +void ms_AVCodec_init(); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h new file mode 100644 index 00000000..6fe5cad4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h @@ -0,0 +1,90 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSAVENCODER_H +#define MSAVENCODER_H + +#include "msfilter.h" +#include "mscodec.h" +#include <avcodec.h> + +/*this is the class that implements a AVencoder filter*/ + +#define MSAVENCODER_MAX_INPUTS 1 /* max output per filter*/ +#define MSAVENCODER_MAX_OUTPUTS 2 + +struct _MSAVEncoder +{ + /* the MSAVEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSAVEncoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSQueue *q_inputs[MSAVENCODER_MAX_INPUTS]; + MSQueue *q_outputs[MSAVENCODER_MAX_OUTPUTS]; + AVCodec *av_codec; /*the AVCodec from which this MSFilter is related */ + AVCodecContext av_context; /* the context of the AVCodec */ + gint input_pix_fmt; + gint av_opened; + MSBuffer *comp_buf; + MSBuffer *yuv_buf; +}; + +typedef struct _MSAVEncoder MSAVEncoder; +/* MSAVEncoder always outputs planar YUV and accept any incoming format you should setup using + ms_AVencoder_set_format() +q_outputs[0] is the compressed video stream output +q_outputs[1] is a YUV planar buffer of the image it receives in input. +*/ + + +struct _MSAVEncoderClass +{ + /* the MSAVEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSAVEncoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +}; + +typedef struct _MSAVEncoderClass MSAVEncoderClass; + +/* PUBLIC */ +#define MS_AVENCODER(filter) ((MSAVEncoder*)(filter)) +#define MS_AVENCODER_CLASS(klass) ((MSAVEncoderClass*)(klass)) + +MSFilter *ms_h263_encoder_new(); +MSFilter *ms_mpeg_encoder_new(); +MSFilter *ms_mpeg4_encoder_new(); +MSFilter * ms_AVencoder_new_with_codec(enum CodecID codec_id, MSCodecInfo *info); + +gint ms_AVencoder_set_format(MSAVEncoder *enc, gchar *fmt); + +#define ms_AVencoder_set_width(av,w) (av)->av_context.width=(w) +#define ms_AVencoder_set_height(av,h) (av)->av_context.height=(h) +#define ms_AVencoder_set_bit_rate(av,r) (av)->av_context.bit_rate=(r) + +void ms_AVencoder_set_frame_rate(MSAVEncoder *enc, gint frame_rate, gint frame_rate_base); + +/* FOR INTERNAL USE*/ +void ms_AVencoder_init(MSAVEncoder *r, AVCodec *codec); +void ms_AVencoder_uninit(MSAVEncoder *enc); +void ms_AVencoder_class_init(MSAVEncoderClass *klass); +void ms_AVencoder_destroy( MSAVEncoder *obj); +void ms_AVencoder_process(MSAVEncoder *r); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c new file mode 100644 index 00000000..4ca3c925 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c @@ -0,0 +1,94 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msbuffer.h" +#include "msutils.h" +#include <string.h> + + + +MSBuffer * ms_buffer_new(guint32 size) +{ + MSBuffer *buf; + buf=(MSBuffer*)g_malloc(sizeof(MSBuffer)+size); + buf->ref_count=0; + buf->size=size; + ms_trace("ms_buffer_new: Allocating buffer of %i bytes.",size); + /* allocate the data buffer: there is a lot of optmisation that can be done by using a pool of cached buffers*/ + buf->buffer=((char*)(buf))+sizeof(MSBuffer); /* to avoid to do two allocations, + buffer info and buffer are contigous.*/ + buf->flags=MS_BUFFER_CONTIGUOUS; + return(buf); +} + +MSBuffer *ms_buffer_alloc(gint flags) +{ + MSBuffer *buf; + buf=(MSBuffer*)g_malloc(sizeof(MSBuffer)); + buf->ref_count=0; + buf->size=0; + buf->buffer=NULL; + buf->flags=0; + return(buf); +} + + +void ms_buffer_destroy(MSBuffer *buf) +{ + if (buf->flags & MS_BUFFER_CONTIGUOUS){ + g_free(buf); + } + else { + g_free(buf->buffer); + g_free(buf); + } +} + +MSMessage *ms_message_alloc() +{ + MSMessage *m=g_malloc(sizeof(MSMessage)); + memset(m,0,sizeof(MSMessage)); + return m; +} + +MSMessage *ms_message_new(gint size) +{ + MSMessage *m=ms_message_alloc(); + MSBuffer *buf=ms_buffer_new(size); + ms_message_set_buf(m,buf); + return m; +} + +void ms_message_destroy(MSMessage *m) +{ + /* the buffer is freed if its ref_count goes to zero */ + if (m->buffer!=NULL){ + m->buffer->ref_count--; + if (m->buffer->ref_count==0) ms_buffer_destroy(m->buffer); + } + g_free(m); +} + +MSMessage * ms_message_dup(MSMessage *m) +{ + MSMessage *msg=ms_message_alloc(); + ms_message_set_buf(msg,m->buffer); + return msg; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h new file mode 100644 index 00000000..f96b35a7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h @@ -0,0 +1,75 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSBUFFER_H +#define MSBUFFER_H +#include <config.h> + +#ifdef HAVE_GLIB +#include <glib.h> +#else +#include <uglib.h> +#endif + + +#define MS_BUFFER_LARGE 4092 + + +typedef struct _MSBuffer +{ + gchar *buffer; + guint32 size; + guint16 ref_count; + guint16 flags; +#define MS_BUFFER_CONTIGUOUS (1) +}MSBuffer; + +MSBuffer * ms_buffer_new(guint32 size); +void ms_buffer_destroy(MSBuffer *buf); + +struct _MSMessage +{ + MSBuffer *buffer; /* points to a MSBuffer */ + void *data; /*points to buffer->buffer */ + guint32 size; /* the size of the buffer to read in data. It may not be the + physical size (I mean buffer->buffer->size */ + struct _MSMessage *next; + struct _MSMessage *prev; /* MSMessage are queued into MSQueues */ +}; + +typedef struct _MSMessage MSMessage; + + +MSBuffer *ms_buffer_alloc(gint flags); +MSMessage *ms_message_new(gint size); + +#define ms_message_set_buf(m,b) do { (b)->ref_count++; (m)->buffer=(b); (m)->data=(b)->buffer; (m)->size=(b)->size; }while(0) +#define ms_message_unset_buf(m) do { (m)->buffer->ref_count--; (m)->buffer=NULL; (m)->size=0; (m)->data=NULL; } while(0) + +#define ms_message_size(m) (m)->size +void ms_message_destroy(MSMessage *m); + +MSMessage * ms_message_dup(MSMessage *m); + +/* allocate a single message without buffer */ +MSMessage *ms_message_alloc(); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c new file mode 100644 index 00000000..dafa1e87 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c @@ -0,0 +1,250 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "mscodec.h" +#include "msMUlawdec.h" + +#ifdef TRUESPEECH +extern MSCodecInfo TrueSpeechinfo; +#endif + +#ifdef VIDEO_ENABLED +extern void ms_AVCodec_init(); +#endif + +#define UDP_HDR_SZ 8 +#define RTP_HDR_SZ 12 +#define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/ + + + + +/* register all statically linked codecs */ +void ms_codec_register_all() +{ +/*// ms_filter_register(MS_FILTER_INFO(&GSMinfo)); + // ms_filter_register(MS_FILTER_INFO(&LPC10info));*/ + ms_filter_register(MS_FILTER_INFO(&MULAWinfo)); +#ifdef TRUESPEECH + ms_filter_register(MS_FILTER_INFO(&TrueSpeechinfo)); +#endif +#ifdef VIDEO_ENABLED + ms_AVCodec_init(); +#endif + +} + +/* returns a list of MSCodecInfo */ +GList * ms_codec_get_all_audio() +{ + GList *audio_codecs=NULL; + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if (info->type==MS_FILTER_AUDIO_CODEC){ + audio_codecs=g_list_append(audio_codecs,info); + } + elem=g_list_next(elem); + } + return audio_codecs; +} + + +MSCodecInfo * ms_audio_codec_info_get(gchar *name) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ( (info->type==MS_FILTER_AUDIO_CODEC) ){ + MSCodecInfo *codinfo=(MSCodecInfo *)info; + if (strcmp(codinfo->description,name)==0){ + return MS_CODEC_INFO(info); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +MSCodecInfo * ms_video_codec_info_get(gchar *name) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ( (info->type==MS_FILTER_VIDEO_CODEC) ){ + MSCodecInfo *codinfo=(MSCodecInfo *)info; + if (strcmp(codinfo->description,name)==0){ + return MS_CODEC_INFO(info); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +/* returns a list of MSCodecInfo */ +GList * ms_codec_get_all_video() +{ + GList *video_codecs=NULL; + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if (info->type==MS_FILTER_VIDEO_CODEC){ + video_codecs=g_list_append(video_codecs,info); + } + elem=g_list_next(elem); + } + return video_codecs; +} + +MSFilter * ms_encoder_new(gchar *name) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){ + MSCodecInfo *codinfo=(MSCodecInfo *)elem->data; + if (strcmp(info->name,name)==0){ + return codinfo->encoder(); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +MSFilter * ms_decoder_new(gchar *name) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){ + MSCodecInfo *codinfo=(MSCodecInfo *)elem->data; + if (strcmp(info->name,name)==0){ + return codinfo->decoder(); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +MSFilter * ms_encoder_new_with_pt(gint pt) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){ + MSCodecInfo *codinfo=(MSCodecInfo *)elem->data; + if (codinfo->pt==pt){ + return codinfo->encoder(); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +MSFilter * ms_decoder_new_with_pt(gint pt) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){ + MSCodecInfo *codinfo=(MSCodecInfo *)elem->data; + if (codinfo->pt==pt){ + return codinfo->decoder(); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +MSFilter * ms_decoder_new_with_string_id(gchar *id) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){ + MSCodecInfo *codinfo=(MSCodecInfo *)elem->data; + if (strcasecmp(codinfo->description,id)==0){ + return codinfo->decoder(); + } + } + elem=g_list_next(elem); + } + return NULL; +} + +MSFilter * ms_encoder_new_with_string_id(gchar *id) +{ + GList *elem=filter_list; + MSFilterInfo *info; + while (elem!=NULL) + { + info=(MSFilterInfo *)elem->data; + if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){ + MSCodecInfo *codinfo=(MSCodecInfo *)elem->data; + if (strcasecmp(codinfo->description,id)==0){ + return codinfo->encoder(); + } + } + elem=g_list_next(elem); + } + return NULL; +} +/* return 0 if codec can be used with bandwidth, -1 else*/ +int ms_codec_is_usable(MSCodecInfo *codec,double bandwidth) +{ + double codec_band; + double npacket; + double packet_size; + + if (((MSFilterInfo*)codec)->type==MS_FILTER_AUDIO_CODEC) + { + /* calculate the total bandwdith needed by codec (including headers for rtp, udp, ip)*/ + /* number of packet per second*/ + npacket=2.0*(double)(codec->rate)/(double)(codec->fr_size); + packet_size=(double)(codec->dt_size)+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ; + codec_band=packet_size*8.0*npacket; + } + else return -1; + return(codec_band<bandwidth); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h new file mode 100644 index 00000000..6c6847d8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h @@ -0,0 +1,67 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSCODEC_H +#define MSCODEC_H + +#include "msfilter.h" + +struct _MSCodecInfo +{ + MSFilterInfo info; + MSFilterNewFunc encoder; + MSFilterNewFunc decoder; + gint fr_size; /* size in char of the uncompressed frame */ + gint dt_size; /* size in char of the compressed frame */ + gint bitrate; /* the minimum bit rate in bits/second */ + gint rate; /*frequency */ + gint pt; /* the payload type number associated with this codec*/ + gchar *description; /* a rtpmap field to describe the codec */ + guint is_usable:1; /* linphone set this flag to remember if it can use this codec considering the total bandwidth*/ + guint is_selected:1; /* linphone (user) set this flag if he allows this codec to be used*/ +}; + +typedef struct _MSCodecInfo MSCodecInfo; + +MSFilter * ms_encoder_new(gchar *name); +MSFilter * ms_decoder_new(gchar *name); + +MSFilter * ms_encoder_new_with_pt(gint pt); +MSFilter * ms_decoder_new_with_pt(gint pt); + +MSFilter * ms_encoder_new_with_string_id(gchar *id); +MSFilter * ms_decoder_new_with_string_id(gchar *id); + +/* return 0 if codec can be used with bandwidth, -1 else*/ +int ms_codec_is_usable(MSCodecInfo *codec,double bandwidth); + +GList * ms_codec_get_all_audio(); + +GList * ms_codec_get_all_video(); + +MSCodecInfo * ms_audio_codec_info_get(gchar *name); +MSCodecInfo * ms_video_codec_info_get(gchar *name); + +/* register all statically linked codecs */ +void ms_codec_register_all(); + +#define MS_CODEC_INFO(codinfo) ((MSCodecInfo*)codinfo) + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c new file mode 100644 index 00000000..3040b2e2 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c @@ -0,0 +1,96 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "mscopy.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> + +static MSCopyClass *ms_copy_class=NULL; + +MSFilter * ms_copy_new(void) +{ + MSCopy *r; + + r=g_new(MSCopy,1); + ms_copy_init(r); + if (ms_copy_class==NULL) + { + ms_copy_class=g_new(MSCopyClass,1); + ms_copy_class_init(ms_copy_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_copy_class); + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_copy_init(MSCopy *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->outfifos=r->f_outputs; + MS_FILTER(r)->r_mingran=MSCOPY_DEF_GRAN; + memset(r->f_inputs,0,sizeof(MSFifo*)*MSCOPY_MAX_INPUTS); + memset(r->f_outputs,0,sizeof(MSFifo*)*MSCOPY_MAX_INPUTS); +} + +void ms_copy_class_init(MSCopyClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"fifocopier"); + MS_FILTER_CLASS(klass)->max_finputs=MSCOPY_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSCOPY_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=MSCOPY_DEF_GRAN; + MS_FILTER_CLASS(klass)->w_maxgran=MSCOPY_DEF_GRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_copy_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_copy_process; +} + +void ms_copy_process(MSCopy *r) +{ + MSFifo *fi,*fo; + int err1; + gint gran=MS_FILTER(r)->klass->r_maxgran; + void *s,*d; + + /* process output fifos, but there is only one for this class of filter*/ + + fi=r->f_inputs[0]; + fo=r->f_outputs[0]; + if (fi!=NULL) + { + err1=ms_fifo_get_read_ptr(fi,gran,&s); + if (err1>0) err1=ms_fifo_get_write_ptr(fo,gran,&d); + if (err1>0) + { + memcpy(d,s,gran); + } + } +} + +void ms_copy_destroy( MSCopy *obj) +{ + g_free(obj); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h new file mode 100644 index 00000000..2b5749b9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h @@ -0,0 +1,61 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSCOPY_H +#define MSCOPY_H + +#include "msfilter.h" + + +/*this is the class that implements a copy filter*/ + +#define MSCOPY_MAX_INPUTS 1 /* max output per filter*/ + +#define MSCOPY_DEF_GRAN 64 /* the default granularity*/ + +typedef struct _MSCopy +{ + /* the MSCopy derivates from MSFilter, so the MSFilter object MUST be the first of the MSCopy object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSCOPY_MAX_INPUTS]; + MSFifo *f_outputs[MSCOPY_MAX_INPUTS]; +} MSCopy; + +typedef struct _MSCopyClass +{ + /* the MSCopy derivates from MSFilter, so the MSFilter class MUST be the first of the MSCopy class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSCopyClass; + +/* PUBLIC */ +#define MS_COPY(filter) ((MSCopy*)(filter)) +#define MS_COPY_CLASS(klass) ((MSCopyClass*)(klass)) +MSFilter * ms_copy_new(void); + +/* FOR INTERNAL USE*/ +void ms_copy_init(MSCopy *r); +void ms_copy_class_init(MSCopyClass *klass); +void ms_copy_destroy( MSCopy *obj); +void ms_copy_process(MSCopy *r); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c new file mode 100644 index 00000000..692bbb7b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c @@ -0,0 +1,94 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a dispatcher of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "msfdispatcher.h" + +static MSFdispatcherClass *ms_fdispatcher_class=NULL; + +MSFilter * ms_fdispatcher_new(void) +{ + MSFdispatcher *obj; + obj=g_malloc(sizeof(MSFdispatcher)); + if (ms_fdispatcher_class==NULL){ + ms_fdispatcher_class=g_malloc(sizeof(MSFdispatcherClass)); + ms_fdispatcher_class_init(ms_fdispatcher_class); + } + MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_fdispatcher_class); + ms_fdispatcher_init(obj); + return MS_FILTER(obj); +} + + +void ms_fdispatcher_init(MSFdispatcher *obj) +{ + ms_filter_init(MS_FILTER(obj)); + MS_FILTER(obj)->infifos=obj->f_inputs; + MS_FILTER(obj)->outfifos=obj->f_outputs; + MS_FILTER(obj)->r_mingran=MS_FDISPATCHER_DEF_GRAN; + memset(obj->f_inputs,0,sizeof(MSFifo*)*MS_FDISPATCHER_MAX_INPUTS); + memset(obj->f_outputs,0,sizeof(MSFifo*)*MS_FDISPATCHER_MAX_OUTPUTS); +} + + + +void ms_fdispatcher_class_init(MSFdispatcherClass *klass) +{ + MSFilterClass *parent_class=MS_FILTER_CLASS(klass); + ms_filter_class_init(parent_class); + ms_filter_class_set_name(parent_class,"fdispatcher"); + parent_class->max_finputs=MS_FDISPATCHER_MAX_INPUTS; + parent_class->max_foutputs=MS_FDISPATCHER_MAX_OUTPUTS; + parent_class->r_maxgran=MS_FDISPATCHER_DEF_GRAN; + parent_class->w_maxgran=MS_FDISPATCHER_DEF_GRAN; + parent_class->destroy=(MSFilterDestroyFunc)ms_fdispatcher_destroy; + parent_class->process=(MSFilterProcessFunc)ms_fdispatcher_process; +} + + +void ms_fdispatcher_destroy( MSFdispatcher *obj) +{ + g_free(obj); +} + +void ms_fdispatcher_process(MSFdispatcher *obj) +{ + gint i; + MSFifo *inf=obj->f_inputs[0]; + + + if (inf!=NULL){ + void *s,*d; + /* dispatch fifos */ + while ( ms_fifo_get_read_ptr(inf,MS_FDISPATCHER_DEF_GRAN,&s) >0 ){ + for (i=0;i<MS_FDISPATCHER_MAX_OUTPUTS;i++){ + MSFifo *outf=obj->f_outputs[i]; + + if (outf!=NULL) + { + ms_fifo_get_write_ptr(outf,MS_FDISPATCHER_DEF_GRAN,&d); + if (d!=NULL) memcpy(d,s,MS_FDISPATCHER_DEF_GRAN); + } + } + } + } +} + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h new file mode 100644 index 00000000..b1b457df --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h @@ -0,0 +1,61 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a dispatcher of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSFDISPATCHER_H +#define MSFDISPATCHER_H + +#include "msfilter.h" + + +/*this is the class that implements a fdispatcher filter*/ + +#define MS_FDISPATCHER_MAX_INPUTS 1 +#define MS_FDISPATCHER_MAX_OUTPUTS 5 +#define MS_FDISPATCHER_DEF_GRAN 64 /* the default granularity*/ + +typedef struct _MSFdispatcher +{ + /* the MSFdispatcher derivates from MSFilter, so the MSFilter object MUST be the first of the MSFdispatcher object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MS_FDISPATCHER_MAX_INPUTS]; + MSFifo *f_outputs[MS_FDISPATCHER_MAX_OUTPUTS]; +} MSFdispatcher; + +typedef struct _MSFdispatcherClass +{ + /* the MSFdispatcher derivates from MSFilter, so the MSFilter class MUST be the first of the MSFdispatcher class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSFdispatcherClass; + +/* PUBLIC */ +#define MS_FDISPATCHER(filter) ((MSFdispatcher*)(filter)) +#define MS_FDISPATCHER_CLASS(klass) ((MSFdispatcherClass*)(klass)) +MSFilter * ms_fdispatcher_new(void); + +/* FOR INTERNAL USE*/ +void ms_fdispatcher_init(MSFdispatcher *r); +void ms_fdispatcher_class_init(MSFdispatcherClass *klass); +void ms_fdispatcher_destroy( MSFdispatcher *obj); +void ms_fdispatcher_process(MSFdispatcher *r); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c new file mode 100644 index 00000000..7e783c24 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c @@ -0,0 +1,168 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <errno.h> +#include <string.h> +#include "msutils.h" +#include "msfifo.h" + +MSFifo * ms_fifo_new(MSBuffer *buf, gint r_gran, gint w_gran, gint r_offset, gint w_offset) +{ + MSFifo *fifo; + gint saved_offset=MAX(r_gran+r_offset,w_offset); + + g_return_val_if_fail(saved_offset<=(buf->size),NULL); + fifo=g_malloc(sizeof(MSFifo)); + fifo->buffer=buf; + fifo->r_gran=r_gran; + fifo->w_gran=w_gran; + fifo->begin=fifo->wr_ptr=fifo->rd_ptr=buf->buffer+saved_offset; + fifo->readsize=0; + fifo->size=fifo->writesize=buf->size-saved_offset; + fifo->saved_offset= saved_offset; + fifo->r_end=fifo->w_end=buf->buffer+buf->size; + fifo->pre_end=fifo->w_end-saved_offset; + buf->ref_count++; + fifo->prev_data=NULL; + fifo->next_data=NULL; + ms_trace("fifo base=%x, begin=%x, end=%x, saved_offset=%i, size=%i" + ,fifo->buffer->buffer,fifo->begin,fifo->w_end,fifo->saved_offset,fifo->size); + return(fifo); +} + +MSFifo * ms_fifo_new_with_buffer(gint r_gran, gint w_gran, gint r_offset, gint w_offset, + gint min_fifo_size) +{ + MSFifo *fifo; + MSBuffer *buf; + gint saved_offset=MAX(r_gran+r_offset,w_offset); + gint fifo_size; + gint tmp; + if (min_fifo_size==0) min_fifo_size=w_gran; + + /* we must allocate a fifo with a size multiple of min_fifo_size, + with a saved_offset */ + if (min_fifo_size>MS_BUFFER_LARGE) + fifo_size=(min_fifo_size) + saved_offset; + else fifo_size=(6*min_fifo_size) + saved_offset; + buf=ms_buffer_new(fifo_size); + fifo=ms_fifo_new(buf,r_gran,w_gran,r_offset,w_offset); + ms_trace("fifo_size=%i",fifo_size); + return(fifo); +} + +void ms_fifo_destroy( MSFifo *fifo) +{ + g_free(fifo); +} + +void ms_fifo_destroy_with_buffer(MSFifo *fifo) +{ + ms_buffer_destroy(fifo->buffer); + ms_fifo_destroy(fifo); +} + +gint ms_fifo_get_read_ptr(MSFifo *fifo, gint bsize, void **ret_ptr) +{ + gchar *rnext; + + *ret_ptr=NULL; + /* //ms_trace("ms_fifo_get_read_ptr: entering.");*/ + g_return_val_if_fail(bsize<=fifo->r_gran,-EINVAL); + + if (bsize>fifo->readsize) + { + ms_trace("Not enough data: bsize=%i, readsize=%i",bsize,fifo->readsize); + return (-ENODATA); + } + + rnext=fifo->rd_ptr+bsize; + if (rnext<=fifo->r_end){ + + *ret_ptr=fifo->rd_ptr; + fifo->rd_ptr=rnext; + }else{ + int unread=fifo->r_end-fifo->rd_ptr; + *ret_ptr=fifo->begin-unread; + memcpy(fifo->buffer->buffer,fifo->r_end-fifo->saved_offset,fifo->saved_offset); + fifo->rd_ptr=(char*)(*ret_ptr) + bsize; + fifo->r_end=fifo->w_end; /* this is important ! */ + ms_trace("moving read ptr to %x",fifo->rd_ptr); + + } + /* update write size*/ + fifo->writesize+=bsize; + fifo->readsize-=bsize; + return bsize; +} + + +void ms_fifo_update_write_ptr(MSFifo *fifo, gint written){ + gint reserved=fifo->wr_ptr-fifo->prev_wr_ptr; + gint unwritten; + g_return_if_fail(reserved>=0); + unwritten=reserved-written; + g_return_if_fail(unwritten>=0); + /* fix readsize and writesize */ + fifo->readsize-=unwritten; + fifo->writesize+=unwritten; + fifo->wr_ptr+=written; +} + +gint ms_fifo_get_write_ptr(MSFifo *fifo, gint bsize, void **ret_ptr) +{ + gchar *wnext; + + *ret_ptr=NULL; + /* //ms_trace("ms_fifo_get_write_ptr: Entering.");*/ + g_return_val_if_fail(bsize<=fifo->w_gran,-EINVAL); + if (bsize>fifo->writesize) + { + ms_trace("Not enough space: bsize=%i, writesize=%i",bsize,fifo->writesize); + *ret_ptr=NULL; + return(-ENODATA); + } + wnext=fifo->wr_ptr+bsize; + if (wnext<=fifo->w_end){ + *ret_ptr=fifo->wr_ptr; + fifo->wr_ptr=wnext; + }else{ + *ret_ptr=fifo->begin; + fifo->r_end=fifo->wr_ptr; + fifo->wr_ptr=fifo->begin+bsize; + ms_trace("moving write ptr to %x",fifo->wr_ptr); + } + fifo->prev_wr_ptr=*ret_ptr; + /* update readsize*/ + fifo->readsize+=bsize; + fifo->writesize-=bsize; + /* //ms_trace("ms_fifo_get_write_ptr: readsize=%i, writesize=%i",fifo->readsize,fifo->writesize);*/ + return bsize; +} + +gint ms_fifo_get_rw_ptr(MSFifo *f1,void **p1,gint minsize1, + MSFifo *f2,void **p2,gint minsize2) +{ + gint rbsize,wbsize; + + rbsize=MIN(f1->readsize,(f1->pre_end-f1->rd_ptr)); + wbsize=MIN(f2->writesize,(f2->w_end-f2->wr_ptr)); + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h new file mode 100644 index 00000000..fde1bece --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h @@ -0,0 +1,73 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_GLIB +#include <glib.h> +#else +#include "glist.h" +#endif +#include "msbuffer.h" + +typedef struct _MSFifo +{ + gint r_gran; /*maximum granularity for reading*/ + gint w_gran; /*maximum granularity for writing*/ + gchar * rd_ptr; /* read pointer on the position where there is something to read on the MSBuffer */ + guint32 readsize; + gchar * wr_ptr; + gchar * prev_wr_ptr; + guint32 writesize; /* write pointer on the position where it is possible to write on the MSBuffer */ + gchar * begin; /* rd_ptr et wr_ptr must all be >=begin*/ + guint32 size; /* the length of the fifo, but this may not be equal to buffer->size*/ + guint32 saved_offset; + gchar * pre_end; /* the end of the buffer that is copied at the begginning when we wrap around*/ + gchar * w_end; /* when a wr ptr is expected to exceed end_offset, + it must be wrapped around to go at the beginning of the buffer. This is the end of the buffer*/ + gchar * r_end; /* this is the last position written at the end of the fifo. If a read ptr is expected to + exceed this pointer, it must be put at the begginning of the buffer */ + void *prev_data; /*user data, usually the writing MSFilter*/ + void *next_data; /* user data, usually the reading MSFilter */ + MSBuffer *buffer; +} MSFifo; + +/* constructor*/ +/* r_gran: max granularity for reading (in number of bytes)*/ +/* w_gran: max granularity for writing (in number of bytes)*/ +/* r_offset: number of bytes that are kept available behind read pointer (for recursive filters)*/ +/* w_offset: number of bytes that are kept available behind write pointer (for recursive filters)*/ +/* buf is a MSBuffer that should be compatible with the above parameter*/ +MSFifo * ms_fifo_new(MSBuffer *buf, gint r_gran, gint w_gran, gint r_offset, gint w_offset); + +/*does the same that ms_fifo_new(), but also allocate a compatible buffer automatically*/ +MSFifo * ms_fifo_new_with_buffer(gint r_gran, gint w_gran, gint r_offset, gint w_offset, gint min_buffer_size); + +void ms_fifo_destroy( MSFifo *fifo); + +void ms_fifo_destroy_with_buffer(MSFifo *fifo); + +/* get data to read */ +gint ms_fifo_get_read_ptr(MSFifo *fifo, gint bsize, void **ret_ptr); + +/* get a buffer to write*/ +gint ms_fifo_get_write_ptr(MSFifo *fifo, gint bsize, void **ret_ptr); + +/* in case the buffer got by ms_fifo_get_write_ptr() could not be filled completely, you must +tell it by using this function */ +void ms_fifo_update_write_ptr(MSFifo *fifo, gint written); diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c new file mode 100644 index 00000000..c67e9f0e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c @@ -0,0 +1,537 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <errno.h> +#include "msfilter.h" + + + +void ms_filter_init(MSFilter *filter) +{ + filter->finputs=0; + filter->foutputs=0; + filter->qinputs=0; + filter->qoutputs=0; + filter->infifos=NULL; + filter->outfifos=NULL; + filter->inqueues=NULL; + filter->outqueues=NULL; + filter->lock=g_mutex_new(); + filter->min_fifo_size=0x7fff; + filter->notify_event=NULL; + filter->userdata=NULL; +} + +void ms_filter_uninit(MSFilter *filter) +{ + g_mutex_free(filter->lock); +} + +void ms_filter_class_init(MSFilterClass *filterclass) +{ + filterclass->name=NULL; + filterclass->max_finputs=0; + filterclass->max_foutputs=0; + filterclass->max_qinputs=0; + filterclass->max_qoutputs=0; + filterclass->r_maxgran=0; + filterclass->w_maxgran=0; + filterclass->r_offset=0; + filterclass->w_offset=0; + filterclass->set_property=NULL; + filterclass->get_property=NULL; + filterclass->setup=NULL; + filterclass->unsetup=NULL; + filterclass->process=NULL; + filterclass->destroy=NULL; + filterclass->attributes=0; + filterclass->ref_count=0; +} + +/* find output queue */ +gint find_oq(MSFilter *m1,MSQueue *oq) +{ + gint i; + + for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qoutputs;i++){ + if (m1->outqueues[i]==oq) return i; + } + + return -1; +} + +/* find input queue */ +gint find_iq(MSFilter *m1,MSQueue *iq) +{ + gint i; + for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qinputs;i++){ + if (m1->inqueues[i]==iq) return i; + } + return -1; +} + +/* find output fifo */ +gint find_of(MSFilter *m1,MSFifo *of) +{ + gint i; + for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_foutputs;i++){ + if (m1->outfifos[i]==of) return i; + } + + return -1; +} + +/* find input fifo */ +gint find_if(MSFilter *m1,MSFifo *inf) +{ + gint i; + + for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_finputs;i++){ + if (m1->infifos[i]==inf) return i; + } + + return -1; +} + +#define find_free_iq(_m1) find_iq(_m1,NULL) +#define find_free_oq(_m1) find_oq(_m1,NULL) +#define find_free_if(_m1) find_if(_m1,NULL) +#define find_free_of(_m1) find_of(_m1,NULL) + +int ms_filter_add_link(MSFilter *m1, MSFilter *m2) +{ + gint m1_q=-1; + gint m1_f=-1; + gint m2_q=-1; + gint m2_f=-1; + /* determine the type of link we can add */ + m1_q=find_free_oq(m1); + m1_f=find_free_of(m1); + m2_q=find_free_iq(m2); + m2_f=find_free_if(m2); + if ((m1_q!=-1) && (m2_q!=-1)){ + /* link with queues */ + ms_trace("m1_q=%i , m2_q=%i",m1_q,m2_q); + return ms_filter_link(m1,m1_q,m2,m2_q,LINK_QUEUE); + } + if ((m1_f!=-1) && (m2_f!=-1)){ + /* link with queues */ + ms_trace("m1_f=%i , m2_f=%i",m1_f,m2_f); + return ms_filter_link(m1,m1_f,m2,m2_f,LINK_FIFO); + } + g_warning("ms_filter_add_link: could not link."); + return -1; +} +/** + * ms_filter_link: + * @m1: A #MSFilter object. + * @pin1: The pin number on @m1. + * @m2: A #MSFilter object. + * @pin2: The pin number on @m2. + * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS. + * + * This function links two MSFilter object between them. It must be used to make chains of filters. + * All data outgoing from pin1 of m1 will go to the input pin2 of m2. + * The way to communicate can be fifos or queues, depending of the nature of the filters. Filters can have + * multiple queue pins and multiple fifo pins, but most of them have only one queue input/output or only one + * fifo input/output. Fifos are usally used by filters doing audio processing, while queues are used by filters doing + * video processing. + * + * Returns: 0 if successfull, a negative value reprensenting the errno.h error. + */ +int ms_filter_link(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2, int linktype) +{ + MSQueue *q; + MSFifo *fifo; + + g_message("ms_filter_add_link: %s,%i -> %s,%i",m1->klass->name,pin1,m2->klass->name,pin2); + switch(linktype) + { + case LINK_QUEUE: + /* Are filter m1 and m2 able to accept more queues connections ?*/ + g_return_val_if_fail(m1->qoutputs<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EMLINK); + g_return_val_if_fail(m2->qinputs<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EMLINK); + /* Are filter m1 and m2 valid with their inputs and outputs ?*/ + g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT); + g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT); + /* are the requested pins exists ?*/ + g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL); + g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EINVAL); + /* are the requested pins free ?*/ + g_return_val_if_fail(m1->outqueues[pin1]==NULL,-EBUSY); + g_return_val_if_fail(m2->inqueues[pin2]==NULL,-EBUSY); + + q=ms_queue_new(); + m1->outqueues[pin1]=m2->inqueues[pin2]=q; + m1->qoutputs++; + m2->qinputs++; + q->prev_data=(void*)m1; + q->next_data=(void*)m2; + break; + case LINK_FIFO: + /* Are filter m1 and m2 able to accept more fifo connections ?*/ + g_return_val_if_fail(m1->foutputs<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EMLINK); + g_return_val_if_fail(m2->finputs<MS_FILTER_GET_CLASS(m2)->max_finputs,-EMLINK); + /* Are filter m1 and m2 valid with their inputs and outputs ?*/ + g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT); + g_return_val_if_fail(m2->infifos!=NULL,-EFAULT); + /* are the requested pins exists ?*/ + g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL); + g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_finputs,-EINVAL); + /* are the requested pins free ?*/ + g_return_val_if_fail(m1->outfifos[pin1]==NULL,-EBUSY); + g_return_val_if_fail(m2->infifos[pin2]==NULL,-EBUSY); + + if (MS_FILTER_GET_CLASS(m1)->attributes & FILTER_IS_SOURCE) + { + /* configure min_fifo_size */ + fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran, + MS_FILTER_GET_CLASS(m1)->w_maxgran, + MS_FILTER_GET_CLASS(m2)->r_offset, + MS_FILTER_GET_CLASS(m1)->w_offset, + MS_FILTER_GET_CLASS(m1)->w_maxgran); + m2->min_fifo_size=MS_FILTER_GET_CLASS(m1)->w_maxgran; + } + else + { + gint next_size; + ms_trace("ms_filter_add_link: min_fifo_size=%i",m1->min_fifo_size); + fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran, + MS_FILTER_GET_CLASS(m1)->w_maxgran, + MS_FILTER_GET_CLASS(m2)->r_offset, + MS_FILTER_GET_CLASS(m1)->w_offset, + m1->min_fifo_size); + if (MS_FILTER_GET_CLASS(m2)->r_maxgran>0){ + next_size=(m1->min_fifo_size* + (MS_FILTER_GET_CLASS(m2)->w_maxgran)) / + (MS_FILTER_GET_CLASS(m2)->r_maxgran); + }else next_size=m1->min_fifo_size; + ms_trace("ms_filter_add_link: next_size=%i",next_size); + m2->min_fifo_size=next_size; + } + + + m1->outfifos[pin1]=m2->infifos[pin2]=fifo; + m1->foutputs++; + m2->finputs++; + fifo->prev_data=(void*)m1; + fifo->next_data=(void*)m2; + break; + } + return 0; +} +/** + * ms_filter_unlink: + * @m1: A #MSFilter object. + * @pin1: The pin number on @m1. + * @m2: A #MSFilter object. + * @pin2: The pin number on @m2. + * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS. + * + * Unlink @pin1 of filter @m1 from @pin2 of filter @m2. @linktype specifies what type of connection is removed. + * + * Returns: 0 if successfull, a negative value reprensenting the errno.h error. + */ +int ms_filter_unlink(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2,gint linktype) +{ + switch(linktype) + { + case LINK_QUEUE: + /* Are filter m1 and m2 valid with their inputs and outputs ?*/ + g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT); + g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT); + /* are the requested pins exists ?*/ + g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL); + g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EINVAL); + /* are the requested pins busy ?*/ + g_return_val_if_fail(m1->outqueues[pin1]!=NULL,-ENOENT); + g_return_val_if_fail(m2->inqueues[pin2]!=NULL,-ENOENT); + /* are the two pins connected together ?*/ + g_return_val_if_fail(m1->outqueues[pin1]==m2->inqueues[pin2],-EINVAL); + + ms_queue_destroy(m1->outqueues[pin1]); + m1->outqueues[pin1]=m2->inqueues[pin2]=NULL; + m1->qoutputs--; + m2->qinputs--; + + break; + case LINK_FIFO: + /* Are filter m1 and m2 valid with their inputs and outputs ?*/ + g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT); + g_return_val_if_fail(m2->infifos!=NULL,-EFAULT); + /* are the requested pins exists ?*/ + g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL); + g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_finputs,-EINVAL); + /* are the requested pins busy ?*/ + g_return_val_if_fail(m1->outfifos[pin1]!=NULL,-ENOENT); + g_return_val_if_fail(m2->infifos[pin2]!=NULL,-ENOENT); + /* are the two pins connected together ?*/ + g_return_val_if_fail(m1->outfifos[pin1]==m2->infifos[pin2],-EINVAL); + ms_fifo_destroy_with_buffer(m1->outfifos[pin1]); + m1->outfifos[pin1]=m2->infifos[pin2]=NULL; + m1->foutputs--; + m2->finputs--; + break; + } + return 0; +} + +/** + *ms_filter_remove_links: + *@m1: a filter + *@m2: another filter. + * + * Removes all links between m1 and m2. + * + *Returns: 0 if one more link have been removed, -1 if not. +**/ +gint ms_filter_remove_links(MSFilter *m1, MSFilter *m2) +{ + int i,j; + int removed=-1; + MSQueue *qo; + MSFifo *fo; + /* takes all outputs of m1, and removes the one that goes to m2 */ + if (m1->outqueues!=NULL){ + for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qoutputs;i++) + { + qo=m1->outqueues[i]; + if (qo!=NULL){ + MSFilter *rmf; + /* test if the queue connects to m2 */ + rmf=(MSFilter*)qo->next_data; + if (rmf==m2){ + j=find_iq(rmf,qo); + if (j==-1) g_error("Could not find input queue: impossible case."); + ms_filter_unlink(m1,i,m2,j,LINK_QUEUE); + removed=0; + } + } + } + } + if (m1->outfifos!=NULL){ + for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_foutputs;i++) + { + fo=m1->outfifos[i]; + if (fo!=NULL){ + MSFilter *rmf; + /* test if the queue connects to m2 */ + rmf=(MSFilter*)fo->next_data; + if (rmf==m2){ + j=find_if(rmf,fo); + if (j==-1) g_error("Could not find input fifo: impossible case."); + ms_filter_unlink(m1,i,m2,j,LINK_FIFO); + removed=0; + } + } + } + } + return removed; +} + +/** + * ms_filter_fifos_have_data: + * @f: a #MSFilter object. + * + * Tells if the filter has enough data in its input fifos in order to be executed succesfully. + * + * Returns: 1 if it can be executed, 0 else. + */ +gint ms_filter_fifos_have_data(MSFilter *f) +{ + gint i,j; + gint max_inputs=f->klass->max_finputs; + gint con_inputs=f->finputs; + MSFifo *fifo; + /* test fifos */ + for(i=0,j=0; (i<max_inputs) && (j<con_inputs);i++) + { + fifo=f->infifos[i]; + if (fifo!=NULL) + { + j++; + if (fifo->readsize==0) return 0; + if (fifo->readsize>=f->r_mingran) return 1; + } + } + return 0; +} + +/** + * ms_filter_queues_have_data: + * @f: a #MSFilter object. + * + * Tells if the filter has enough data in its input queues in order to be executed succesfully. + * + * Returns: 1 if it can be executed, 0 else. + */ +gint ms_filter_queues_have_data(MSFilter *f) +{ + gint i,j; + gint max_inputs=f->klass->max_qinputs; + gint con_inputs=f->qinputs; + MSQueue *q; + /* test queues */ + for(i=0,j=0; (i<max_inputs) && (j<con_inputs);i++) + { + q=f->inqueues[i]; + if (q!=NULL) + { + j++; + if (ms_queue_can_get(q)) return 1; + } + } + return 0; +} + + + +void ms_filter_destroy(MSFilter *f) +{ + /* first check if the filter is disconnected from any others */ + g_return_if_fail(f->finputs==0); + g_return_if_fail(f->foutputs==0); + g_return_if_fail(f->qinputs==0); + g_return_if_fail(f->qoutputs==0); + f->klass->destroy(f); +} + +GList *filter_list=NULL; + +void ms_filter_register(MSFilterInfo *info) +{ + gpointer tmp; + tmp=g_list_find(filter_list,info); + if (tmp==NULL) filter_list=g_list_append(filter_list,(gpointer)info); +} + +void ms_filter_unregister(MSFilterInfo *info) +{ + filter_list=g_list_remove(filter_list,(gpointer)info); +} + +static gint compare_names(gpointer info, gpointer name) +{ + MSFilterInfo *i=(MSFilterInfo*) info; + return (strcmp(i->name,name)); +} + +MSFilterInfo * ms_filter_get_by_name(const gchar *name) +{ + GList *elem=g_list_find_custom(filter_list, + (gpointer)name,(GCompareFunc)compare_names); + if (elem!=NULL){ + return (MSFilterInfo*)elem->data; + } + return NULL; +} + + + +MSFilter * ms_filter_new_with_name(const gchar *name) +{ + MSFilterInfo *info=ms_filter_get_by_name(name); + if (info!=NULL) return info->constructor(); + g_warning("ms_filter_new_with_name: no filter named %s found.",name); + return NULL; +} + + +/* find the first codec in the left part of the stream */ +MSFilter * ms_filter_search_upstream_by_type(MSFilter *f,MSFilterType type) +{ + MSFilter *tmp=f; + MSFilterInfo *info; + + if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL)){ + tmp=(MSFilter*) tmp->infifos[0]->prev_data; + while(1){ + info=MS_FILTER_GET_CLASS(tmp)->info; + if (info!=NULL){ + if ( (info->type==type) ){ + return tmp; + } + } + if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL)) + tmp=(MSFilter*) tmp->infifos[0]->prev_data; + else break; + } + } + tmp=f; + if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL)){ + tmp=(MSFilter*) tmp->inqueues[0]->prev_data; + while(1){ + + info=MS_FILTER_GET_CLASS(tmp)->info; + if (info!=NULL){ + if ( (info->type==type)){ + return tmp; + } + }else g_warning("ms_filter_search_upstream_by_type: filter %s has no info." + ,MS_FILTER_GET_CLASS(tmp)->name); + if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL)) + tmp=(MSFilter*) tmp->inqueues[0]->prev_data; + else break; + } + } + return NULL; +} + + +int ms_filter_set_property(MSFilter *f, MSFilterProperty prop,void *value) +{ + if (f->klass->set_property!=NULL){ + return f->klass->set_property(f,prop,value); + } + return 0; +} + +int ms_filter_get_property(MSFilter *f, MSFilterProperty prop,void *value) +{ + if (f->klass->get_property!=NULL){ + return f->klass->get_property(f,prop,value); + } + return -1; +} + +void ms_filter_set_notify_func(MSFilter* filter,MSFilterNotifyFunc func, gpointer userdata) +{ + filter->notify_event=func; + filter->userdata=userdata; +} + +void ms_filter_notify_event(MSFilter *filter,gint event, gpointer arg) +{ + if (filter->notify_event!=NULL){ + filter->notify_event(filter,event,arg,filter->userdata); + } +} + +void swap_buffer(gchar *buffer, gint len) +{ + int i; + gchar tmp; + for (i=0;i<len;i+=2){ + tmp=buffer[i]; + buffer[i]=buffer[i+1]; + buffer[i+1]=tmp; + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h new file mode 100644 index 00000000..71ec81ad --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h @@ -0,0 +1,201 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSFILTER_H +#define MSFILTER_H + +#include <config.h> + +#ifdef HAVE_GLIB +#include <glib.h> +#include <gmodule.h> +#else +#undef VERSION +#undef PACKAGE +#include <uglib.h> +#endif + +#include <string.h> +#include "msutils.h" +#include "msfifo.h" +#include "msqueue.h" + +struct _MSFilter; +/*this is the abstract object and class for all filter types*/ +typedef gint (*MSFilterNotifyFunc)(struct _MSFilter*, gint event, gpointer arg, gpointer userdata); + +struct _MSFilter +{ + struct _MSFilterClass *klass; + GMutex *lock; + guchar finputs; /* number of connected fifo inputs*/ + guchar foutputs; /* number of connected fifo outputs*/ + guchar qinputs; /* number of connected queue inputs*/ + guchar qoutputs; /* number of connected queue outputs*/ + gint min_fifo_size; /* set when linking*/ + gint r_mingran; /* read minimum granularity (for fifos). + It can be zero so that the filter can accept any size of reading data*/ + MSFifo **infifos; /*pointer to a table of pointer to input fifos*/ + MSFifo **outfifos; /*pointer to a table of pointer to output fifos*/ + MSQueue **inqueues; /*pointer to a table of pointer to input queues*/ + MSQueue **outqueues; /*pointer to a table of pointer to output queues*/ + MSFilterNotifyFunc notify_event; + gpointer userdata; +}; + +typedef struct _MSFilter MSFilter; + +typedef enum{ + MS_FILTER_PROPERTY_FREQ, /* value is int */ + MS_FILTER_PROPERTY_BITRATE, /*value is int */ + MS_FILTER_PROPERTY_CHANNELS,/*value is int */ + MS_FILTER_PROPERTY_FMTP /* value is string */ +}MSFilterProperty; + +#define MS_FILTER_PROPERTY_STRING_MAX_SIZE 256 + +typedef MSFilter * (*MSFilterNewFunc)(void); +typedef void (*MSFilterProcessFunc)(MSFilter *); +typedef void (*MSFilterDestroyFunc)(MSFilter *); +typedef int (*MSFilterPropertyFunc)(MSFilter *,int ,void*); +typedef void (*MSFilterSetupFunc)(MSFilter *, void *); /*2nd arg is the sync */ + +typedef struct _MSFilterClass +{ + struct _MSFilterInfo *info; /*pointer to a filter_info */ + gchar *name; + guchar max_finputs; /* maximum number of fifo inputs*/ + guchar max_foutputs; /* maximum number of fifo outputs*/ + guchar max_qinputs; /* maximum number of queue inputs*/ + guchar max_qoutputs; /* maximum number of queue outputs*/ + gint r_maxgran; /* read maximum granularity (for fifos)*/ + gint w_maxgran; /* write maximum granularity (for fifos)*/ + gint r_offset; /* size of kept samples behind read pointer (for fifos)*/ + gint w_offset; /* size of kept samples behind write pointer (for fifos)*/ + MSFilterPropertyFunc set_property; + MSFilterPropertyFunc get_property; + MSFilterSetupFunc setup; /* called when attaching to sync */ + void (*process)(MSFilter *filter); + MSFilterSetupFunc unsetup; /* called when detaching from sync */ + void (*destroy)(MSFilter *filter); + guint attributes; +#define FILTER_HAS_FIFOS (0x0001) +#define FILTER_HAS_QUEUES (0x0001<<1) +#define FILTER_IS_SOURCE (0x0001<<2) +#define FILTER_IS_SINK (0x0001<<3) +#define FILTER_CAN_SYNC (0x0001<<4) + guint ref_count; /*number of object using the class*/ +} MSFilterClass; + + + +#define MS_FILTER(obj) ((MSFilter*)obj) +#define MS_FILTER_CLASS(klass) ((MSFilterClass*)klass) +#define MS_FILTER_GET_CLASS(obj) ((MSFilterClass*)((MS_FILTER(obj)->klass))) + +void ms_filter_class_init(MSFilterClass *filterclass); +void ms_filter_init(MSFilter *filter); + +#define ms_filter_class_set_attr(filter,flag) ((filter)->attributes|=(flag)) +#define ms_filter_class_unset_attr(filter,flag) ((filter)->attributes&=~(flag)) + +#define ms_filter_class_set_name(__klass,__name) (__klass)->name=g_strdup((__name)) +#define ms_filter_class_set_info(_klass,_info) (_klass)->info=(_info) +/* public*/ + +#define ms_filter_process(filter) ((filter)->klass->process((filter))) + +#define ms_filter_lock(filter) g_mutex_lock((filter)->lock) +#define ms_filter_unlock(filter) g_mutex_unlock((filter)->lock) +/* low level connect functions */ +int ms_filter_link(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2, gint linktype); +int ms_filter_unlink(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2,gint linktype); + +/* high level connect functions */ +int ms_filter_add_link(MSFilter *m1, MSFilter *m2); +int ms_filter_remove_links(MSFilter *m1, MSFilter *m2); + +void ms_filter_set_notify_func(MSFilter* filter,MSFilterNotifyFunc func, gpointer userdata); +void ms_filter_notify_event(MSFilter *filter,gint event, gpointer arg); + +int ms_filter_set_property(MSFilter *f,MSFilterProperty property, void *value); +int ms_filter_get_property(MSFilter *f,MSFilterProperty property, void *value); + + +gint ms_filter_fifos_have_data(MSFilter *f); +gint ms_filter_queues_have_data(MSFilter *f); + +void ms_filter_uninit(MSFilter *obj); +void ms_filter_destroy(MSFilter *f); + +#define ms_filter_get_mingran(f) ((f)->r_mingran) +#define ms_filter_set_mingran(f,gran) ((f)->r_mingran=(gran)) + +#define LINK_DEFAULT 0 +#define LINK_FIFO 1 +#define LINK_QUEUE 2 + + +#define MSFILTER_VERSION(a,b,c) (((a)<<2)|((b)<<1)|(c)) + +enum _MSFilterType +{ + MS_FILTER_DISK_IO, + MS_FILTER_AUDIO_CODEC, + MS_FILTER_VIDEO_CODEC, + MS_FILTER_NET_IO, + MS_FILTER_VIDEO_IO, + MS_FILTER_AUDIO_IO, + MS_FILTER_OTHER +}; + +typedef enum _MSFilterType MSFilterType; + + +/* find the first codec in the left part of the stream */ +MSFilter * ms_filter_search_upstream_by_type(MSFilter *f,MSFilterType type); + +struct _MSFilterInfo +{ + gchar *name; + gint version; + MSFilterType type; + MSFilterNewFunc constructor; + char *description; /*some textual information*/ +}; + +typedef struct _MSFilterInfo MSFilterInfo; + +void ms_filter_register(MSFilterInfo *finfo); +void ms_filter_unregister(MSFilterInfo *finfo); +MSFilterInfo * ms_filter_get_by_name(const gchar *name); + +MSFilter * ms_filter_new_with_name(const gchar *name); + + + +extern GList *filter_list; +#define MS_FILTER_INFO(obj) ((MSFilterInfo*)obj) + +void swap_buffer(gchar *buffer, gint len); + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c new file mode 100644 index 00000000..b2dfff98 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c @@ -0,0 +1,194 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <config.h> + +#ifdef HAVE_ILBC + + +#include "msilbcdec.h" +#include "msilbcenc.h" +#include "mscodec.h" +#include <stdlib.h> +#include <stdio.h> + + + +extern MSFilter * ms_ilbc_encoder_new(void); + +MSCodecInfo ilbc_info={ + { + "iLBC codec", + 0, + MS_FILTER_AUDIO_CODEC, + ms_ilbc_encoder_new, + "A speech codec suitable for robust voice communication over IP" + }, + ms_ilbc_encoder_new, + ms_ilbc_decoder_new, + 0, /* not applicable, 2 modes */ + 0, /* not applicable, 2 modes */ + 15200, + 8000, + 97, + "iLBC", + 1, + 1, +}; + + +void ms_ilbc_codec_init() +{ + ms_filter_register(MS_FILTER_INFO(&ilbc_info)); +} + + + +static MSILBCDecoderClass *ms_ilbc_decoder_class=NULL; + +MSFilter * ms_ilbc_decoder_new(void) +{ + MSILBCDecoder *r; + + r=g_new(MSILBCDecoder,1); + ms_ilbc_decoder_init(r); + if (ms_ilbc_decoder_class==NULL) + { + ms_ilbc_decoder_class=g_new(MSILBCDecoderClass,1); + ms_ilbc_decoder_class_init(ms_ilbc_decoder_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ilbc_decoder_class); + return(MS_FILTER(r)); +} + + +int ms_ilbc_decoder_set_property(MSILBCDecoder *obj, MSFilterProperty prop, char *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FMTP: + if (value == NULL) return 0; + if (strstr(value,"ptime=20")!=NULL) obj->ms_per_frame=20; + else if (strstr(value,"ptime=30")!=NULL) obj->ms_per_frame=30; + else g_warning("Unrecognized fmtp parameter for ilbc encoder!"); + break; + } + return 0; +} +int ms_ilbc_decoder_get_property(MSILBCDecoder *obj, MSFilterProperty prop, char *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FMTP: + if (obj->ms_per_frame==20) strncpy(value,"ptime=20",MS_FILTER_PROPERTY_STRING_MAX_SIZE); + if (obj->ms_per_frame==30) strncpy(value,"ptime=30",MS_FILTER_PROPERTY_STRING_MAX_SIZE); + break; + } + return 0; +} + +void ms_ilbc_decoder_setup(MSILBCDecoder *r) +{ + MSFilterClass *klass = NULL; + switch (r->ms_per_frame) { + case 20: + r->samples_per_frame = BLOCKL_20MS; + r->bytes_per_compressed_frame = NO_OF_BYTES_20MS; + break; + case 30: + r->samples_per_frame = BLOCKL_30MS; + r->bytes_per_compressed_frame = NO_OF_BYTES_30MS; + break; + default: + g_error("ms_ilbc_decoder_setup: Bad value for ptime (%i)",r->ms_per_frame); + } + g_message("Using ilbc decoder with %i ms frames mode.",r->ms_per_frame); + initDecode(&r->ilbc_dec, r->ms_per_frame /* ms frames */, /* user enhancer */ 0); +} + + +/* FOR INTERNAL USE*/ +void ms_ilbc_decoder_init(MSILBCDecoder *r) +{ + /* default bitrate */ + r->bitrate = 15200; + r->ms_per_frame = 30; + r->samples_per_frame = BLOCKL_20MS; + r->bytes_per_compressed_frame = NO_OF_BYTES_20MS; + + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->inqueues=r->q_inputs; + MS_FILTER(r)->outfifos=r->f_outputs; + memset(r->q_inputs,0,sizeof(MSFifo*)*MSILBCDECODER_MAX_INPUTS); + memset(r->f_outputs,0,sizeof(MSFifo*)*MSILBCDECODER_MAX_INPUTS); +} + +void ms_ilbc_decoder_class_init(MSILBCDecoderClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ILBCDec"); + MS_FILTER_CLASS(klass)->max_qinputs=MSILBCDECODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSILBCDECODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->w_maxgran= ILBC_MAX_SAMPLES_PER_FRAME*2; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ilbc_decoder_destroy; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ilbc_decoder_set_property; + MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ilbc_decoder_get_property; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ilbc_decoder_setup; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ilbc_decoder_process; + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ilbc_info; +} + +void ms_ilbc_decoder_process(MSILBCDecoder *r) +{ + MSFifo *fo; + MSQueue *qi; + int err1; + void *dst=NULL; + float speech[ILBC_MAX_SAMPLES_PER_FRAME]; + MSMessage *m; + + qi=r->q_inputs[0]; + fo=r->f_outputs[0]; + m=ms_queue_get(qi); + + ms_fifo_get_write_ptr(fo, r->samples_per_frame*2, &dst); + if (dst!=NULL){ + if (m->data!=NULL){ + if (m->size<r->bytes_per_compressed_frame) { + g_warning("Invalid ilbc frame ?"); + } + iLBC_decode(speech, m->data, &r->ilbc_dec, /* mode */1); + }else{ + iLBC_decode(speech,NULL, &r->ilbc_dec,0); + } + ilbc_write_16bit_samples((gint16*)dst, speech, r->samples_per_frame); + } + ms_message_destroy(m); +} + +void ms_ilbc_decoder_uninit(MSILBCDecoder *obj) +{ +} + +void ms_ilbc_decoder_destroy( MSILBCDecoder *obj) +{ + ms_ilbc_decoder_uninit(obj); + g_free(obj); +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h new file mode 100644 index 00000000..c219aabe --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h @@ -0,0 +1,72 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSILBCDECODER_H +#define MSILBCDECODER_H + +#include <msfilter.h> +#include <mscodec.h> +#include <iLBC_decode.h> + +/*this is the class that implements a ILBCdecoder filter*/ + +#define MSILBCDECODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSILBCDecoder +{ + /* the MSILBCDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSILBCDecoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSQueue *q_inputs[MSILBCDECODER_MAX_INPUTS]; + MSFifo *f_outputs[MSILBCDECODER_MAX_INPUTS]; + iLBC_Dec_Inst_t ilbc_dec; + int bitrate; + int ms_per_frame; + int samples_per_frame; + int bytes_per_compressed_frame; +} MSILBCDecoder; + +typedef struct _MSILBCDecoderClass +{ + /* the MSILBCDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSILBCDecoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSILBCDecoderClass; + +/* PUBLIC */ + +/* call this before if don't load the plugin dynamically */ +void ms_ilbc_codec_init(); + +#define MS_ILBCDECODER(filter) ((MSILBCDecoder*)(filter)) +#define MS_ILBCDECODER_CLASS(klass) ((MSILBCDecoderClass*)(klass)) +MSFilter * ms_ilbc_decoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_ilbc_decoder_init(MSILBCDecoder *r); +void ms_ilbc_decoder_class_init(MSILBCDecoderClass *klass); +void ms_ilbc_decoder_destroy( MSILBCDecoder *obj); +void ms_ilbc_decoder_process(MSILBCDecoder *r); + +extern MSCodecInfo ilbc_info; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c new file mode 100644 index 00000000..76d8b648 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c @@ -0,0 +1,244 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <config.h> + +#ifdef HAVE_ILBC + +#include <stdlib.h> +#include <stdio.h> +#include "msilbcenc.h" + + +extern MSCodecInfo ilbc_info; + +/* The return value of each of these calls is the same as that + returned by fread/fwrite, which should be the number of samples + successfully read/written, not the number of bytes. */ + +int +ilbc_read_16bit_samples(gint16 int16samples[], float speech[], int n) +{ + int i; + + /* Convert 16 bit integer samples to floating point values in the + range [-1,+1]. */ + + for (i = 0; i < n; i++) { + speech[i] = int16samples[i]; + } + + return (n); +} + + + +int +ilbc_write_16bit_samples(gint16 int16samples[], float speech[], int n) +{ + int i; + float real_sample; + + /* Convert floating point samples in range [-1,+1] to 16 bit + integers. */ + for (i = 0; i < n; i++) { + float dtmp=speech[i]; + if (dtmp<MIN_SAMPLE) + dtmp=MIN_SAMPLE; + else if (dtmp>MAX_SAMPLE) + dtmp=MAX_SAMPLE; + int16samples[i] = (short) dtmp; + } + return (n); +} + +/* + +Write the bits in bits[0] through bits[len-1] to file f, in "packed" +format. + +bits is expected to be an array of len integer values, where each +integer is 0 to represent a 0 bit, and any other value represents a 1 +bit. This bit string is written to the file f in the form of several +8 bit characters. If len is not a multiple of 8, then the last +character is padded with 0 bits -- the padding is in the least +significant bits of the last byte. The 8 bit characters are "filled" +in order from most significant bit to least significant. + +*/ + +void +ilbc_write_bits(unsigned char *data, unsigned char *bits, int nbytes) +{ + memcpy(data, bits, nbytes); +} + + + +/* + +Read bits from file f into bits[0] through bits[len-1], in "packed" +format. + +*/ + +int +ilbc_read_bits(unsigned char *data, unsigned char *bits, int nbytes) +{ + + memcpy(bits, data, nbytes); + + return (nbytes); +} + + + + +static MSILBCEncoderClass *ms_ilbc_encoder_class=NULL; + +MSFilter * ms_ilbc_encoder_new(void) +{ + MSILBCEncoder *r; + + r=g_new(MSILBCEncoder,1); + ms_ilbc_encoder_init(r); + if (ms_ilbc_encoder_class==NULL) + { + ms_ilbc_encoder_class=g_new(MSILBCEncoderClass,1); + ms_ilbc_encoder_class_init(ms_ilbc_encoder_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ilbc_encoder_class); + return(MS_FILTER(r)); +} + + +int ms_ilbc_encoder_set_property(MSILBCEncoder *obj, MSFilterProperty prop, char *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FMTP: + if (value == NULL) return 0; + if (strstr(value,"ptime=20")!=NULL) obj->ms_per_frame=20; + else if (strstr(value,"ptime=30")!=NULL) obj->ms_per_frame=30; + else g_warning("Unrecognized fmtp parameter for ilbc encoder!"); + break; + } + return 0; +} + + +int ms_ilbc_encoder_get_property(MSILBCEncoder *obj, MSFilterProperty prop, char *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FMTP: + if (obj->ms_per_frame==20) strncpy(value,"ptime=20",MS_FILTER_PROPERTY_STRING_MAX_SIZE); + if (obj->ms_per_frame==30) strncpy(value,"ptime=30",MS_FILTER_PROPERTY_STRING_MAX_SIZE); + break; + } + return 0; +} + +void ms_ilbc_encoder_setup(MSILBCEncoder *r) +{ + MSFilterClass *klass = NULL; + switch (r->ms_per_frame) { + case 20: + r->samples_per_frame = BLOCKL_20MS; + r->bytes_per_compressed_frame = NO_OF_BYTES_20MS; + break; + case 30: + r->samples_per_frame = BLOCKL_30MS; + r->bytes_per_compressed_frame = NO_OF_BYTES_30MS; + break; + default: + g_error("Bad bitrate value (%i) for ilbc encoder!", r->ms_per_frame); + break; + } + MS_FILTER(r)->r_mingran= (r->samples_per_frame * 2); + g_message("Using ilbc encoder with %i ms frames mode.",r->ms_per_frame); + initEncode(&r->ilbc_enc, r->ms_per_frame /* ms frames */); +} + +/* FOR INTERNAL USE*/ +void ms_ilbc_encoder_init(MSILBCEncoder *r) +{ + /* default bitrate */ + r->bitrate = 15200; + r->ms_per_frame = 20; + r->samples_per_frame = BLOCKL_20MS; + r->bytes_per_compressed_frame = NO_OF_BYTES_20MS; + + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->outqueues=r->q_outputs; + MS_FILTER(r)->r_mingran= (r->samples_per_frame * 2); + memset(r->f_inputs,0,sizeof(MSFifo*)*MSILBCENCODER_MAX_INPUTS); + memset(r->q_outputs,0,sizeof(MSFifo*)*MSILBCENCODER_MAX_INPUTS); +} + +void ms_ilbc_encoder_class_init(MSILBCEncoderClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ILBCEnc"); + MS_FILTER_CLASS(klass)->max_finputs=MSILBCENCODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_qoutputs=MSILBCENCODER_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=ILBC_MAX_SAMPLES_PER_FRAME*2; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ilbc_encoder_set_property; + MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ilbc_encoder_get_property; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ilbc_encoder_setup; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ilbc_encoder_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ilbc_encoder_process; + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ilbc_info; +} + +void ms_ilbc_encoder_process(MSILBCEncoder *r) +{ + MSFifo *fi; + MSQueue *qo; + MSMessage *m; + void *src=NULL; + float speech[ILBC_MAX_SAMPLES_PER_FRAME]; + + /* process output fifos, but there is only one for this class of filter*/ + + qo=r->q_outputs[0]; + fi=r->f_inputs[0]; + ms_fifo_get_read_ptr(fi,r->samples_per_frame*2,&src); + if (src==NULL) { + g_warning( "src=%p\n", src); + return; + } + m=ms_message_new(r->bytes_per_compressed_frame); + + ilbc_read_16bit_samples((gint16*)src, speech, r->samples_per_frame); + iLBC_encode((unsigned char *)m->data, speech, &r->ilbc_enc); + ms_queue_put(qo,m); +} + +void ms_ilbc_encoder_uninit(MSILBCEncoder *obj) +{ +} + +void ms_ilbc_encoder_destroy( MSILBCEncoder *obj) +{ + ms_ilbc_encoder_uninit(obj); + g_free(obj); +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h new file mode 100644 index 00000000..bd8f3bf5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h @@ -0,0 +1,84 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSILBCENCODER_H +#define MSILBCENCODER_H + +#include "mscodec.h" +#include <iLBC_encode.h> + +#define ILBC_BITS_IN_COMPRESSED_FRAME 400 + +int +ilbc_read_16bit_samples(gint16 int16samples[], float speech[], int n); + +int +ilbc_write_16bit_samples(gint16 int16samples[], float speech[], int n); + +void +ilbc_write_bits(unsigned char *data, unsigned char *bits, int nbytes); + +int +ilbc_read_bits(unsigned char *data, unsigned char *bits, int nbytes); + + +/*this is the class that implements a ILBCencoder filter*/ + +#define MSILBCENCODER_MAX_INPUTS 1 /* max output per filter*/ + + +typedef struct _MSILBCEncoder +{ + /* the MSILBCEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSILBCEncoder object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSILBCENCODER_MAX_INPUTS]; + MSQueue *q_outputs[MSILBCENCODER_MAX_INPUTS]; + iLBC_Enc_Inst_t ilbc_enc; + int ilbc_encoded_bytes; + int bitrate; + int ms_per_frame; + int samples_per_frame; + int bytes_per_compressed_frame; +} MSILBCEncoder; + +typedef struct _MSILBCEncoderClass +{ + /* the MSILBCEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSILBCEncoder class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSILBCEncoderClass; + +/* PUBLIC */ +#define MS_ILBCENCODER(filter) ((MSILBCEncoder*)(filter)) +#define MS_ILBCENCODER_CLASS(klass) ((MSILBCEncoderClass*)(klass)) +MSFilter * ms_ilbc_encoder_new(void); + +/* FOR INTERNAL USE*/ +void ms_ilbc_encoder_init(MSILBCEncoder *r); +void ms_ilbc_encoder_class_init(MSILBCEncoderClass *klass); +void ms_ilbc_encoder_destroy( MSILBCEncoder *obj); +void ms_ilbc_encoder_process(MSILBCEncoder *r); + +#define ILBC_MAX_BYTES_PER_COMPRESSED_FRAME NO_OF_BYTES_30MS +#define ILBC_MAX_SAMPLES_PER_FRAME BLOCKL_30MS + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c new file mode 100644 index 00000000..af5141c0 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c @@ -0,0 +1,82 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "msnosync.h" + +static MSNoSyncClass *ms_nosync_class=NULL; + +void ms_nosync_init(MSNoSync *sync) +{ + ms_sync_init(MS_SYNC(sync)); + MS_SYNC(sync)->attached_filters=sync->filters; + memset(sync->filters,0,MSNOSYNC_MAX_FILTERS*sizeof(MSFilter*)); + MS_SYNC(sync)->samples_per_tick=160; + sync->started=0; +} + +void ms_nosync_class_init(MSNoSyncClass *klass) +{ + ms_sync_class_init(MS_SYNC_CLASS(klass)); + MS_SYNC_CLASS(klass)->max_filters=MSNOSYNC_MAX_FILTERS; + MS_SYNC_CLASS(klass)->synchronize=(MSSyncSyncFunc)ms_nosync_synchronize; + MS_SYNC_CLASS(klass)->destroy=(MSSyncDestroyFunc)ms_nosync_destroy; + /* no need to overload these function*/ + MS_SYNC_CLASS(klass)->attach=ms_sync_attach_generic; + MS_SYNC_CLASS(klass)->detach=ms_sync_detach_generic; +} + +void ms_nosync_destroy(MSNoSync *nosync) +{ + g_free(nosync); +} + +/* the synchronization function that does nothing*/ +void ms_nosync_synchronize(MSNoSync *nosync) +{ + gint32 time; + if (nosync->started==0){ + gettimeofday(&nosync->start,NULL); + nosync->started=1; + } + gettimeofday(&nosync->current,NULL); + MS_SYNC(nosync)->ticks++; + /* update the time, we are supposed to work at 8000 Hz */ + time=((nosync->current.tv_sec-nosync->start.tv_sec)*1000) + + ((nosync->current.tv_usec-nosync->start.tv_usec)/1000); + MS_SYNC(nosync)->time=time; + return; +} + + +MSSync *ms_nosync_new() +{ + MSNoSync *nosync; + + nosync=g_malloc(sizeof(MSNoSync)); + ms_nosync_init(nosync); + if (ms_nosync_class==NULL) + { + ms_nosync_class=g_new(MSNoSyncClass,1); + ms_nosync_class_init(ms_nosync_class); + } + MS_SYNC(nosync)->klass=MS_SYNC_CLASS(ms_nosync_class); + return(MS_SYNC(nosync)); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h new file mode 100644 index 00000000..eef52d45 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h @@ -0,0 +1,60 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "mssync.h" + +#include <sys/time.h> +#define MSNOSYNC_MAX_FILTERS 10 + +/* MSNoSync derivates from MSSync base class*/ + +typedef struct _MSNoSync +{ + /* the MSSync must be the first field of the object in order to the object mechanism to work*/ + MSSync sync; + MSFilter *filters[MSNOSYNC_MAX_FILTERS]; + int started; + struct timeval start,current; +} MSNoSync; + + +typedef struct _MSNoSyncClass +{ + /* the MSSyncClass must be the first field of the class in order to the class mechanism to work*/ + MSSyncClass parent_class; +} MSNoSyncClass; + + +/*private*/ + +void ms_nosync_init(MSNoSync *sync); +void ms_nosync_class_init(MSNoSyncClass *sync); + +void ms_nosync_destroy(MSNoSync *nosync); +void ms_nosync_synchronize(MSNoSync *nosync); + +/*public*/ + +/* casts a MSSync object into a MSNoSync */ +#define MS_NOSYNC(sync) ((MSNoSync*)(sync)) +/* casts a MSSync class into a MSNoSync class */ +#define MS_NOSYNC_CLASS(klass) ((MSNoSyncClass*)(klass)) + +MSSync *ms_nosync_new(); diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c new file mode 100644 index 00000000..2486c736 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c @@ -0,0 +1,148 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msossread.h" +#include "mssync.h" +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/types.h> + +MSFilterInfo oss_read_info={ + "OSS read", + 0, + MS_FILTER_AUDIO_IO, + ms_oss_read_new, + NULL +}; + +static MSOssReadClass *msossreadclass=NULL; + +MSFilter * ms_oss_read_new() +{ + MSOssRead *w; + + if (msossreadclass==NULL) + { + msossreadclass=g_new(MSOssReadClass,1); + ms_oss_read_class_init( msossreadclass ); + } + + w=g_new(MSOssRead,1); + MS_FILTER(w)->klass=MS_FILTER_CLASS(msossreadclass); + ms_oss_read_init(w); + + return(MS_FILTER(w)); +} + +/* FOR INTERNAL USE*/ +void ms_oss_read_init(MSOssRead *w) +{ + ms_sound_read_init(MS_SOUND_READ(w)); + MS_FILTER(w)->outfifos=w->f_outputs; + MS_FILTER(w)->outfifos[0]=NULL; + w->devid=0; + w->sndcard=NULL; + w->freq=8000; +} + +gint ms_oss_read_set_property(MSOssRead *f,MSFilterProperty prop, void *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FREQ: + f->freq=((gint*)value)[0]; + break; + } + return 0; +} +void ms_oss_read_class_init(MSOssReadClass *klass) +{ + ms_sound_read_class_init(MS_SOUND_READ_CLASS(klass)); + MS_FILTER_CLASS(klass)->max_foutputs=1; /* one fifo output only */ + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_oss_read_setup; + MS_FILTER_CLASS(klass)->unsetup=(MSFilterSetupFunc)ms_oss_read_stop; + MS_FILTER_CLASS(klass)->process= (MSFilterProcessFunc)ms_oss_read_process; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_oss_read_set_property; + MS_FILTER_CLASS(klass)->destroy= (MSFilterDestroyFunc)ms_oss_read_destroy; + MS_FILTER_CLASS(klass)->w_maxgran=MS_OSS_READ_MAX_GRAN; + MS_FILTER_CLASS(klass)->info=&oss_read_info; + MS_SOUND_READ_CLASS(klass)->set_device=(gint (*)(MSSoundRead*,gint))ms_oss_read_set_device; + MS_SOUND_READ_CLASS(klass)->start=(void (*)(MSSoundRead*))ms_oss_read_start; + MS_SOUND_READ_CLASS(klass)->stop=(void (*)(MSSoundRead*))ms_oss_read_stop; + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"OssRead"); + /* //ms_filter_class_set_attr( MS_FILTER_CLASS(klass),FILTER_CAN_SYNC|FILTER_IS_SOURCE); */ +} + +void ms_oss_read_destroy( MSOssRead *obj) +{ + g_free(obj); +} + +void ms_oss_read_process(MSOssRead *f) +{ + MSFifo *fifo; + char *p; + fifo=f->f_outputs[0]; + + g_return_if_fail(f->sndcard!=NULL); + g_return_if_fail(f->gran>0); + + if (snd_card_can_read(f->sndcard)){ + int got; + ms_fifo_get_write_ptr(fifo,f->gran,(void**)&p); + g_return_if_fail(p!=NULL); + got=snd_card_read(f->sndcard,p,f->gran); + if (got>=0 && got!=f->gran) ms_fifo_update_write_ptr(fifo,got); + } +} + + +void ms_oss_read_start(MSOssRead *r) +{ + g_return_if_fail(r->devid!=-1); + r->sndcard=snd_card_manager_get_card(snd_card_manager,r->devid); + g_return_if_fail(r->sndcard!=NULL); + /* open the device for an audio telephony signal with minimum latency */ + snd_card_open_r(r->sndcard,16,0,r->freq); + r->gran=(512*r->freq)/8000; + +} + +void ms_oss_read_stop(MSOssRead *w) +{ + g_return_if_fail(w->devid!=-1); + g_return_if_fail(w->sndcard!=NULL); + snd_card_close_r(w->sndcard); + w->sndcard=NULL; +} + + +void ms_oss_read_setup(MSOssRead *f, MSSync *sync) +{ + f->sync=sync; + ms_oss_read_start(f); +} + + +gint ms_oss_read_set_device(MSOssRead *r,gint devid) +{ + r->devid=devid; + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h new file mode 100644 index 00000000..89d5a40b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h @@ -0,0 +1,77 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MSOSSREAD_H +#define MSOSSREAD_H + +#include "mssoundread.h" +#include "sndcard.h" +#include "mssync.h" + + +/*this is the class that implements oss writing sink filter*/ + +#define MS_OSS_READ_MAX_INPUTS 1 /* max output per filter*/ + +#define MS_OSS_READ_MAX_GRAN (512*2) /* the maximum granularity*/ + +struct _MSOssRead +{ + /* the MSOssRead derivates from MSSoundRead so the MSSoundRead object MUST be the first of the MSOssRead object + in order to the object mechanism to work*/ + MSSoundRead filter; + MSFifo *f_outputs[MS_OSS_READ_MAX_INPUTS]; + MSSync *sync; + SndCard *sndcard; + gint freq; + gint devid; /* the sound device id it depends on*/ + gint gran; + gint flags; +#define START_REQUESTED 1 +#define STOP_REQUESTED 2 +}; + +typedef struct _MSOssRead MSOssRead; + +struct _MSOssReadClass +{ + /* the MSOssRead derivates from MSSoundRead, so the MSSoundRead class MUST be the first of the MSOssRead class + in order to the class mechanism to work*/ + MSSoundReadClass parent_class; +}; + +typedef struct _MSOssReadClass MSOssReadClass; + +/* PUBLIC */ +#define MS_OSS_READ(filter) ((MSOssRead*)(filter)) +#define MS_OSS_READ_CLASS(klass) ((MSOssReadClass*)(klass)) +MSFilter * ms_oss_read_new(void); +gint ms_oss_read_set_device(MSOssRead *w,gint devid); +void ms_oss_read_start(MSOssRead *w); +void ms_oss_read_stop(MSOssRead *w); + +/* FOR INTERNAL USE*/ +void ms_oss_read_init(MSOssRead *r); +void ms_oss_read_class_init(MSOssReadClass *klass); +void ms_oss_read_destroy( MSOssRead *obj); +void ms_oss_read_process(MSOssRead *f); +void ms_oss_read_setup(MSOssRead *f, MSSync *sync); + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c new file mode 100644 index 00000000..cc86cd6b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c @@ -0,0 +1,247 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msosswrite.h" +#include "mssync.h" +#include <unistd.h> +#include <math.h> + +MSFilterInfo oss_write_info={ + "OSS write", + 0, + MS_FILTER_OTHER, + ms_oss_write_new, + NULL +}; + + +static MSOssWriteClass *msosswriteclass=NULL; + +MSFilter * ms_oss_write_new() +{ + MSOssWrite *w; + + if (msosswriteclass==NULL) + { + msosswriteclass=g_new(MSOssWriteClass,1); + ms_oss_write_class_init( msosswriteclass ); + } + w=g_new(MSOssWrite,1); + MS_FILTER(w)->klass=MS_FILTER_CLASS(msosswriteclass); + ms_oss_write_init(w); + return(MS_FILTER(w)); +} + +/* FOR INTERNAL USE*/ +void ms_oss_write_init(MSOssWrite *w) +{ + ms_sound_write_init(MS_SOUND_WRITE(w)); + MS_FILTER(w)->infifos=w->f_inputs; + MS_FILTER(w)->infifos[0]=NULL; + MS_FILTER(w)->r_mingran=512; /* very few cards can do that...*/ + w->devid=0; + w->sndcard=NULL; + w->freq=8000; + w->channels=1; + w->dtmf_time=-1; +} + +gint ms_oss_write_set_property(MSOssWrite *f,MSFilterProperty prop, void *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FREQ: + f->freq=((gint*)value)[0]; + break; + case MS_FILTER_PROPERTY_CHANNELS: + f->channels=((gint*)value)[0]; + break; + } + return 0; +} + +void ms_oss_write_class_init(MSOssWriteClass *klass) +{ + ms_sound_write_class_init(MS_SOUND_WRITE_CLASS(klass)); + MS_FILTER_CLASS(klass)->max_finputs=1; /* one fifo input only */ + MS_FILTER_CLASS(klass)->r_maxgran=MS_OSS_WRITE_DEF_GRAN; + MS_FILTER_CLASS(klass)->process= (MSFilterProcessFunc)ms_oss_write_process; + MS_FILTER_CLASS(klass)->destroy= (MSFilterDestroyFunc)ms_oss_write_destroy; + MS_FILTER_CLASS(klass)->setup= (MSFilterSetupFunc)ms_oss_write_setup; + MS_FILTER_CLASS(klass)->unsetup= (MSFilterSetupFunc)ms_oss_write_stop; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_oss_write_set_property; + MS_FILTER_CLASS(klass)->info=&oss_write_info; + MS_SOUND_WRITE_CLASS(klass)->set_device=(gint (*)(MSSoundWrite*,gint))ms_oss_write_set_device; + MS_SOUND_WRITE_CLASS(klass)->start=(void (*)(MSSoundWrite*))ms_oss_write_start; + MS_SOUND_WRITE_CLASS(klass)->stop=(void (*)(MSSoundWrite*))ms_oss_write_stop; + MS_SOUND_WRITE_CLASS(klass)->set_level=(void (*)(MSSoundWrite*, gint))ms_oss_write_set_level; + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"OssWrite"); +} + +void ms_oss_write_destroy( MSOssWrite *obj) +{ + + g_free(obj); +} + +void ms_oss_write_process(MSOssWrite *f) +{ + MSFifo *fifo; + void *p; + int i; + gint gran=ms_filter_get_mingran(MS_FILTER(f)); + + /* always consume something */ + fifo=f->f_inputs[0]; + ms_fifo_get_read_ptr(fifo,gran,&p); + if (p==NULL) { + g_warning("Not enough data: gran=%i.",gran); + return; + } + g_return_if_fail(f->sndcard!=NULL); + if (f->dtmf_time!=-1){ + gint16 *buf=(gint16*)p; + /* generate a DTMF*/ + for(i=0;i<gran/2;i++){ + buf[i]=(gint16)(10000.0*sin(2*M_PI*(double)f->dtmf_time*f->lowfreq)); + buf[i]+=(gint16)(10000.0*sin(2*M_PI*(double)f->dtmf_time*f->highfreq)); + f->dtmf_time++; + /* //printf("buf[%i]=%i\n",i,buf[i]); */ + } + if (f->dtmf_time>f->dtmf_duration) f->dtmf_time=-1; /*finished*/ + } + snd_card_write(f->sndcard,p,gran); +} + +void ms_oss_write_start(MSOssWrite *w) +{ + gint bsize; + g_return_if_fail(w->devid!=-1); + w->sndcard=snd_card_manager_get_card(snd_card_manager,w->devid); + g_return_if_fail(w->sndcard!=NULL); + /* open the device for an audio telephony signal with minimum latency */ + snd_card_open_w(w->sndcard,16,w->channels==2,w->freq); + w->bsize=snd_card_get_bsize(w->sndcard); + /* //MS_FILTER(w)->r_mingran=w->bsize; */ + /* //ms_sync_set_samples_per_tick(MS_FILTER(w)->sync,bsize); */ +} + +void ms_oss_write_stop(MSOssWrite *w) +{ + g_return_if_fail(w->devid!=-1); + g_return_if_fail(w->sndcard!=NULL); + snd_card_close_w(w->sndcard); + w->sndcard=NULL; +} + +void ms_oss_write_set_level(MSOssWrite *w,gint a) +{ + +} + +gint ms_oss_write_set_device(MSOssWrite *w, gint devid) +{ + w->devid=devid; + return 0; +} + +void ms_oss_write_setup(MSOssWrite *r) +{ + /* //g_message("starting MSOssWrite.."); */ + ms_oss_write_start(r); +} + + + +void ms_oss_write_play_dtmf(MSOssWrite *w, char dtmf){ + + w->dtmf_duration=0.1*w->freq; + switch(dtmf){ + case '0': + w->lowfreq=941; + w->highfreq=1336; + break; + case '1': + w->lowfreq=697; + w->highfreq=1209; + break; + case '2': + w->lowfreq=697; + w->highfreq=1336; + break; + case '3': + w->lowfreq=697; + w->highfreq=1477; + break; + case '4': + w->lowfreq=770; + w->highfreq=1209; + break; + case '5': + w->lowfreq=770; + w->highfreq=1336; + break; + case '6': + w->lowfreq=770; + w->highfreq=1477; + break; + case '7': + w->lowfreq=852; + w->highfreq=1209; + break; + case '8': + w->lowfreq=852; + w->highfreq=1336; + break; + case '9': + w->lowfreq=852; + w->highfreq=1477; + break; + case '*': + w->lowfreq=941; + w->highfreq=1209; + break; + case '#': + w->lowfreq=941; + w->highfreq=1477; + break; + case 'A': + w->lowfreq=697; + w->highfreq=1633; + break; + case 'B': + w->lowfreq=770; + w->highfreq=1633; + break; + case 'C': + w->lowfreq=852; + w->highfreq=1633; + break; + case 'D': + w->lowfreq=941; + w->highfreq=1633; + break; + default: + g_warning("Not a dtmf key."); + return; + } + w->lowfreq=w->lowfreq/w->freq; + w->highfreq=w->highfreq/w->freq; + w->dtmf_time=0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h new file mode 100644 index 00000000..d4775341 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h @@ -0,0 +1,78 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MSOSSWRITE_H +#define MSOSSWRITE_H + +#include "mssoundwrite.h" +#include "sndcard.h" + +/*this is the class that implements oss writing sink filter*/ + +#define MS_OSS_WRITE_MAX_INPUTS 1 /* max output per filter*/ + +#define MS_OSS_WRITE_DEF_GRAN (512*2) /* the default granularity*/ + +struct _MSOssWrite +{ + /* the MSOssWrite derivates from MSSoundWrite, so the MSSoundWrite object MUST be the first of the MSOssWrite object + in order to the object mechanism to work*/ + MSSoundWrite filter; + MSFifo *f_inputs[MS_OSS_WRITE_MAX_INPUTS]; + gint devid; /* the sound device id it depends on*/ + SndCard *sndcard; + gint bsize; + gint freq; + gint channels; + gdouble lowfreq; + gdouble highfreq; + gint dtmf_time; + gint dtmf_duration; +}; + +typedef struct _MSOssWrite MSOssWrite; + +struct _MSOssWriteClass +{ + /* the MSOssWrite derivates from MSSoundWrite, so the MSSoundWrite class MUST be the first of the MSOssWrite class + in order to the class mechanism to work*/ + MSSoundWriteClass parent_class; +}; + +typedef struct _MSOssWriteClass MSOssWriteClass; + +/* PUBLIC */ +#define MS_OSS_WRITE(filter) ((MSOssWrite*)(filter)) +#define MS_OSS_WRITE_CLASS(klass) ((MSOssWriteClass*)(klass)) +MSFilter * ms_oss_write_new(void); +gint ms_oss_write_set_device(MSOssWrite *w,gint devid); +void ms_oss_write_start(MSOssWrite *w); +void ms_oss_write_stop(MSOssWrite *w); +void ms_oss_write_set_level(MSOssWrite *w, gint level); +void ms_oss_write_play_dtmf(MSOssWrite *w, char dtmf); + +/* FOR INTERNAL USE*/ +void ms_oss_write_init(MSOssWrite *r); +void ms_oss_write_setup(MSOssWrite *r); +void ms_oss_write_class_init(MSOssWriteClass *klass); +void ms_oss_write_destroy( MSOssWrite *obj); +void ms_oss_write_process(MSOssWrite *f); + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c new file mode 100644 index 00000000..6bd073b9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c @@ -0,0 +1,91 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a dispatcher of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "msqdispatcher.h" + +static MSQdispatcherClass *ms_qdispatcher_class=NULL; + +MSFilter * ms_qdispatcher_new(void) +{ + MSQdispatcher *obj; + obj=g_malloc(sizeof(MSQdispatcher)); + if (ms_qdispatcher_class==NULL){ + ms_qdispatcher_class=g_malloc0(sizeof(MSQdispatcherClass)); + ms_qdispatcher_class_init(ms_qdispatcher_class); + } + MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_qdispatcher_class); + ms_qdispatcher_init(obj); + return MS_FILTER(obj); +} + + +void ms_qdispatcher_init(MSQdispatcher *obj) +{ + ms_filter_init(MS_FILTER(obj)); + + MS_FILTER(obj)->inqueues=obj->q_inputs; + MS_FILTER(obj)->outqueues=obj->q_outputs; + memset(obj->q_inputs,0,sizeof(MSQueue*)*MS_QDISPATCHER_MAX_INPUTS); + memset(obj->q_outputs,0,sizeof(MSQueue*)*MS_QDISPATCHER_MAX_OUTPUTS); +} + + + +void ms_qdispatcher_class_init(MSQdispatcherClass *klass) +{ + MSFilterClass *parent_class=MS_FILTER_CLASS(klass); + ms_filter_class_init(parent_class); + ms_filter_class_set_name(parent_class,"qdispatcher"); + parent_class->max_qinputs=MS_QDISPATCHER_MAX_INPUTS; + parent_class->max_qoutputs=MS_QDISPATCHER_MAX_OUTPUTS; + + parent_class->destroy=(MSFilterDestroyFunc)ms_qdispatcher_destroy; + parent_class->process=(MSFilterProcessFunc)ms_qdispatcher_process; +} + + +void ms_qdispatcher_destroy( MSQdispatcher *obj) +{ + g_free(obj); +} + +void ms_qdispatcher_process(MSQdispatcher *obj) +{ + gint i; + MSQueue *inq=obj->q_inputs[0]; + + if (inq!=NULL){ + MSQueue *outq; + MSMessage *m1,*m2; + while ( (m1=ms_queue_get(inq))!=NULL){ + /* dispatch incoming messages to output queues */ + for (i=0;i<MS_QDISPATCHER_MAX_OUTPUTS;i++){ + outq=obj->q_outputs[i]; + if (outq!=NULL){ + m2=ms_message_dup(m1); + ms_queue_put(outq,m2); + } + } + ms_message_destroy(m1); + } + } + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h new file mode 100644 index 00000000..3b0c566d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h @@ -0,0 +1,60 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a dispatcher of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSQDISPATCHER_H +#define MSQDISPATCHER_H + +#include "msfilter.h" + + +/*this is the class that implements a qdispatcher filter*/ + +#define MS_QDISPATCHER_MAX_INPUTS 1 +#define MS_QDISPATCHER_MAX_OUTPUTS 5 + +typedef struct _MSQdispatcher +{ + /* the MSQdispatcher derivates from MSFilter, so the MSFilter object MUST be the first of the MSQdispatcher object + in order to the object mechanism to work*/ + MSFilter filter; + MSQueue *q_inputs[MS_QDISPATCHER_MAX_INPUTS]; + MSQueue *q_outputs[MS_QDISPATCHER_MAX_OUTPUTS]; +} MSQdispatcher; + +typedef struct _MSQdispatcherClass +{ + /* the MSQdispatcher derivates from MSFilter, so the MSFilter class MUST be the first of the MSQdispatcher class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSQdispatcherClass; + +/* PUBLIC */ +#define MS_QDISPATCHER(filter) ((MSQdispatcher*)(filter)) +#define MS_QDISPATCHER_CLASS(klass) ((MSQdispatcherClass*)(klass)) +MSFilter * ms_qdispatcher_new(void); + +/* FOR INTERNAL USE*/ +void ms_qdispatcher_init(MSQdispatcher *r); +void ms_qdispatcher_class_init(MSQdispatcherClass *klass); +void ms_qdispatcher_destroy( MSQdispatcher *obj); +void ms_qdispatcher_process(MSQdispatcher *r); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c new file mode 100644 index 00000000..46368956 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c @@ -0,0 +1,56 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msqueue.h" +#include <string.h> + +MSQueue * ms_queue_new() +{ + MSQueue *q=g_malloc(sizeof(MSQueue)); + memset(q,0,sizeof(MSQueue)); + return q; +} + +MSMessage *ms_queue_get(MSQueue *q) +{ + MSMessage *b=q->last; + if (b==NULL) return NULL; + q->last=b->prev; + if (b->prev==NULL) q->first=NULL; /* it was the only element of the queue*/ + q->size--; + b->next=b->prev=NULL; + return(b); +} + +void ms_queue_put(MSQueue *q, MSMessage *m) +{ + MSMessage *mtmp=q->first; + g_return_if_fail(m!=NULL); + q->first=m; + m->next=mtmp; + if (mtmp!=NULL) + { + mtmp->prev=m; + } + else q->last=m; /* it was the first element of the q */ + q->size++; +} + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h new file mode 100644 index 00000000..73ab8d8d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h @@ -0,0 +1,49 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSQUEUE_H +#define MSQUEUE_H + +#include "msbuffer.h" + +/* for the moment these are stupid queues limited to one element*/ + +typedef struct _MSQueue +{ + MSMessage *first; + MSMessage *last; + gint size; + void *prev_data; /*user data, usually the writting filter*/ + void *next_data; /* user data, usually the reading filter*/ +}MSQueue; + + +MSQueue * ms_queue_new(); + +MSMessage *ms_queue_get(MSQueue *q); + +void ms_queue_put(MSQueue *q, MSMessage *m); + +#define ms_queue_can_get(q) ( (q)->size!=0 ) + +#define ms_queue_destroy(q) g_free(q) + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c new file mode 100644 index 00000000..6f0ec99d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c @@ -0,0 +1,182 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msread.h" +#include "mssync.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <string.h> +#include <errno.h> + +static MSReadClass *ms_read_class=NULL; + +MSFilter * ms_read_new(char *name) +{ + MSRead *r; + int fd=-1; + + r=g_new(MSRead,1); + ms_read_init(r); + if (ms_read_class==NULL) + { + ms_read_class=g_new(MSReadClass,1); + ms_read_class_init(ms_read_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_read_class); + r->fd=-1; + if (name!=NULL) ms_read_open(r,name); + return(MS_FILTER(r)); +} + + + +gint ms_read_open(MSRead *r, gchar *name) +{ + gint fd; + fd=open(name,O_RDONLY); + if (fd<0) { + r->fd=-1; + g_warning("ms_read_new: cannot open %s : %s",name,strerror(errno)); + return -1; + } + r->fd=fd; + if (strstr(name,".wav")!=NULL){ + /* skip the header */ + lseek(fd,20,SEEK_SET); +#ifdef WORDS_BIGENDIAN + r->need_swap=1; +#else + r->need_swap=0; +#endif + } + r->state=MS_READ_STATE_STARTED; + return 0; +} + +/* FOR INTERNAL USE*/ +void ms_read_init(MSRead *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->outfifos=r->foutputs; + MS_FILTER(r)->outqueues=r->qoutputs; + memset(r->foutputs,0,sizeof(MSFifo*)*MSREAD_MAX_OUTPUTS); + memset(r->qoutputs,0,sizeof(MSQueue*)*MSREAD_MAX_OUTPUTS); + r->fd=-1; + r->gran=320; + r->state=MS_READ_STATE_STOPPED; + r->need_swap=0; + r->rate=8000; +} + +gint ms_read_set_property(MSRead *f,MSFilterProperty prop, void *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FREQ: + f->rate=((gint*)value)[0]; + break; + } + return 0; +} + +void ms_read_class_init(MSReadClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"dskreader"); + ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE); + MS_FILTER_CLASS(klass)->max_foutputs=MSREAD_MAX_OUTPUTS; + MS_FILTER_CLASS(klass)->max_qoutputs=MSREAD_MAX_OUTPUTS; + MS_FILTER_CLASS(klass)->w_maxgran=MSREAD_DEF_GRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_read_destroy; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_read_setup; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_read_process; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_read_set_property; +} + +void ms_read_process(MSRead *r) +{ + MSFifo *f; + MSQueue *q; + MSMessage *msg=NULL; + int err; + gint gran=r->gran; + void *p; + + f=r->foutputs[0]; + if ((f!=NULL) && (r->state==MS_READ_STATE_STARTED)) + { + ms_fifo_get_write_ptr(f,gran,&p); + if (p!=NULL) + { + err=read(r->fd,p,gran); + if (err<0) + { + /* temp: */ + g_warning("ms_read_process: failed to read: %s.\n",strerror(errno)); + } + else if (err<gran){ + ms_trace("ms_read_process: end of file."); + ms_filter_notify_event(MS_FILTER(r),MS_READ_EVENT_EOF,NULL); + r->state=MS_READ_STATE_STOPPED; + close(r->fd); + r->fd=-1; + } + if (r->need_swap) swap_buffer(p,gran); + } + } + /* process output queues*/ + q=r->qoutputs[0]; + if ((q!=NULL) && (r->fd>0)) + { + msg=ms_message_new(r->gran); + err=read(r->fd,msg->data,r->gran); + if (err>0){ + msg->size=err; + ms_queue_put(q,msg); + if (r->need_swap) swap_buffer(msg->data,r->gran); + }else{ + ms_filter_notify_event(MS_FILTER(r),MS_READ_EVENT_EOF,NULL); + ms_trace("End of file reached."); + r->state=MS_READ_STATE_STOPPED; + } + } +} + +void ms_read_destroy( MSRead *obj) +{ + if (obj->fd!=0) close(obj->fd); + g_free(obj); +} + +gint ms_read_close(MSRead *obj) +{ + if (obj->fd!=0) { + close(obj->fd); + obj->fd=-1; + obj->state=MS_READ_STATE_STOPPED; + } +} + + +void ms_read_setup(MSRead *r, MSSync *sync) +{ + r->sync=sync; + r->gran=(r->rate*sync->interval/1000)*2; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h new file mode 100644 index 00000000..93177f38 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h @@ -0,0 +1,80 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSREAD_H +#define MSREAD_H + +#include "msfilter.h" +#include "mssync.h" + +/*this is the class that implements file reading source filter*/ + +#define MSREAD_MAX_OUTPUTS 1 /* max output per filter*/ + +#define MSREAD_DEF_GRAN 640 /* the default granularity*/ + +typedef enum{ + MS_READ_STATE_STARTED, + MS_READ_STATE_STOPPED, + MS_READ_STATE_EOF +}MSReadState; + +typedef struct _MSRead +{ + /* the MSRead derivates from MSFilter, so the MSFilter object MUST be the first of the MSRead object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *foutputs[MSREAD_MAX_OUTPUTS]; + MSQueue *qoutputs[MSREAD_MAX_OUTPUTS]; + MSSync *sync; + gint rate; + gint fd; /* the file descriptor of the file being read*/ + gint gran; /*granularity*/ /* for use with queues */ + gint need_swap; + gint state; +} MSRead; + +typedef struct _MSReadClass +{ + /* the MSRead derivates from MSFilter, so the MSFilter class MUST be the first of the MSRead class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSReadClass; + +/* PUBLIC */ +#define MS_READ(filter) ((MSRead*)(filter)) +#define MS_READ_CLASS(klass) ((MSReadClass*)(klass)) +MSFilter * ms_read_new(char *name); +/* set the granularity for reading file on disk */ +#define ms_read_set_bufsize(filter,sz) (filter)->gran=(sz) + +/* FOR INTERNAL USE*/ +void ms_read_init(MSRead *r); +void ms_read_class_init(MSReadClass *klass); +void ms_read_destroy( MSRead *obj); +void ms_read_process(MSRead *r); +void ms_read_setup(MSRead *r, MSSync *sync); + +typedef enum{ + MS_READ_EVENT_EOF /* end of file */ +} MSReadEvent; + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c new file mode 100644 index 00000000..fb2006e8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c @@ -0,0 +1,246 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msringplayer.h" +#include "mssync.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <string.h> +#include <errno.h> + +#include "waveheader.h" + +#define WAVE_HEADER_OFFSET sizeof(wave_header_t) + +enum { PLAY_RING, PLAY_SILENCE}; + +static int supported_freq[6]={8000,11025,16000,22050,32000,44100}; + +gint freq_is_supported(gint freq){ + int i; + for (i=0;i<6;i++){ + if (abs(supported_freq[i]-freq)<50) return supported_freq[i]; + } + return 0; +} + +static MSRingPlayerClass *ms_ring_player_class=NULL; + +/** + * ms_ring_player_new: + * @name: The path to the 16-bit 8khz raw file to be played as a ring. + * @seconds: The number of seconds that separates two rings. + * + * Allocates a new MSRingPlayer object. + * + * + * Returns: a pointer the the object, NULL if name could not be open. + */ +MSFilter * ms_ring_player_new(char *name, gint seconds) +{ + MSRingPlayer *r; + int fd=-1; + + if ((name!=NULL) && (strlen(name)!=0)) + { + fd=open(name,O_RDONLY); + if (fd<0) + { + g_warning("ms_ring_player_new: failed to open %s.\n",name); + return NULL; + } + + }else { + g_warning("ms_ring_player_new: Bad file name"); + return NULL; + } + + r=g_new(MSRingPlayer,1); + ms_ring_player_init(r); + if (ms_ring_player_class==NULL) + { + ms_ring_player_class=g_new(MSRingPlayerClass,1); + ms_ring_player_class_init(ms_ring_player_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ring_player_class); + + r->fd=fd; + r->silence=seconds; + r->freq=8000; + if (strstr(name,".wav")!=NULL){ + wave_header_t header; + int freq,freq2; + /* read the header */ + read(fd,&header,sizeof(wave_header_t)); + freq=wave_header_get_rate(&header); + if ((freq2=freq_is_supported(freq))>0){ + r->freq=freq2; + }else { + g_warning("Unsupported sampling rate %i",freq); + r->freq=8000; + } + r->channel=wave_header_get_channel(&header); + lseek(fd,WAVE_HEADER_OFFSET,SEEK_SET); +#ifdef WORDS_BIGENDIAN + r->need_swap=1; +#else + r->need_swap=0; +#endif + } + ms_ring_player_set_property(r, MS_FILTER_PROPERTY_FREQ,&r->freq); + r->state=PLAY_RING; + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_ring_player_init(MSRingPlayer *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->outfifos=r->foutputs; + MS_FILTER(r)->outqueues=r->qoutputs; + memset(r->foutputs,0,sizeof(MSFifo*)*MS_RING_PLAYER_MAX_OUTPUTS); + memset(r->qoutputs,0,sizeof(MSQueue*)*MS_RING_PLAYER_MAX_OUTPUTS); + r->fd=-1; + r->current_pos=0; + r->need_swap=0; + r->sync=NULL; +} + +gint ms_ring_player_set_property(MSRingPlayer *f,MSFilterProperty prop, void *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FREQ: + f->rate=((gint*)value)[0]*2; + f->silence_bytes=f->silence*f->rate; + if (f->sync!=NULL) + f->gran=(f->rate*f->sync->interval/1000)*2; + break; + } + return 0; +} + +gint ms_ring_player_get_property(MSRingPlayer *f,MSFilterProperty prop, void *value) +{ + switch(prop){ + case MS_FILTER_PROPERTY_FREQ: + ((gint*)value)[0]=f->freq; + + break; + case MS_FILTER_PROPERTY_CHANNELS: + ((gint*)value)[0]=f->channel; + break; + } + return 0; +} + +gint ms_ring_player_get_sample_freq(MSRingPlayer *obj){ + return obj->freq; +} + + +void ms_ring_player_class_init(MSRingPlayerClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ringplay"); + ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE); + MS_FILTER_CLASS(klass)->max_foutputs=MS_RING_PLAYER_MAX_OUTPUTS; + MS_FILTER_CLASS(klass)->max_qoutputs=MS_RING_PLAYER_MAX_OUTPUTS; + MS_FILTER_CLASS(klass)->w_maxgran=MS_RING_PLAYER_DEF_GRAN; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ring_player_setup; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ring_player_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ring_player_process; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ring_player_set_property; + MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ring_player_get_property; +} + +void ms_ring_player_process(MSRingPlayer *r) +{ + MSFifo *f; + gint err; + gint processed=0; + gint gran=r->gran; + char *p; + + g_return_if_fail(gran>0); + /* process output fifos*/ + + f=r->foutputs[0]; + ms_fifo_get_write_ptr(f,gran,(void**)&p); + g_return_if_fail(p!=NULL); + for (processed=0;processed<gran;){ + switch(r->state){ + case PLAY_RING: + err=read(r->fd,&p[processed],gran-processed); + if (err<0) + { + memset(&p[processed],0,gran-processed); + processed=gran; + g_warning("ms_ring_player_process: failed to read: %s.\n",strerror(errno)); + return; + } + else if (err<gran) + {/* end of file */ + + r->current_pos=r->silence_bytes; + lseek(r->fd,WAVE_HEADER_OFFSET,SEEK_SET); + r->state=PLAY_SILENCE; + ms_filter_notify_event(MS_FILTER(r),MS_RING_PLAYER_END_OF_RING_EVENT,NULL); + } + if (r->need_swap) swap_buffer(&p[processed],err); + processed+=err; + break; + case PLAY_SILENCE: + err=gran-processed; + if (r->current_pos>err){ + memset(&p[processed],0,err); + r->current_pos-=gran; + processed=gran; + }else{ + memset(&p[processed],0,r->current_pos); + processed+=r->current_pos; + r->state=PLAY_RING; + } + break; + } + } +} + +/** + * ms_ring_player_destroy: + * @obj: A valid MSRingPlayer object. + * + * Destroy a MSRingPlayer object. + * + * + */ + +void ms_ring_player_destroy( MSRingPlayer *obj) +{ + if (obj->fd!=0) close(obj->fd); + g_free(obj); +} + +void ms_ring_player_setup(MSRingPlayer *r,MSSync *sync) +{ + r->sync=sync; + r->gran=(r->rate*r->sync->interval/1000)*r->channel; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h new file mode 100644 index 00000000..1f5e67da --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h @@ -0,0 +1,81 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSRINGPLAYER_H +#define MSRINGPLAYER_H + +#include "msfilter.h" +#include "mssync.h" + + +/*this is the class that implements file reading source filter*/ + +#define MS_RING_PLAYER_MAX_OUTPUTS 1 /* max output per filter*/ + +#define MS_RING_PLAYER_DEF_GRAN 8192 /* the default granularity*/ + +#define MS_RING_PLAYER_END_OF_RING_EVENT 1 + +struct _MSRingPlayer +{ + /* the MSRingPlayer derivates from MSFilter, so the MSFilter object MUST be the first of the MSRingPlayer object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *foutputs[MS_RING_PLAYER_MAX_OUTPUTS]; + MSQueue *qoutputs[MS_RING_PLAYER_MAX_OUTPUTS];\ + MSSync *sync; + gint gran; + gint freq; + gint rate; + gint channel; /* number of interleaved channels */ + gint silence; /* silence time between each ring, in seconds */ + gint state; + gint fd; /* the file descriptor of the file being read*/ + gint silence_bytes; /*silence in number of bytes between each ring */ + gint current_pos; + gint need_swap; +}; + +typedef struct _MSRingPlayer MSRingPlayer; + +struct _MSRingPlayerClass +{ + /* the MSRingPlayer derivates from MSFilter, so the MSFilter class MUST be the first of the MSRingPlayer class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +}; + +typedef struct _MSRingPlayerClass MSRingPlayerClass; + +/* PUBLIC */ +#define MS_RING_PLAYER(filter) ((MSRingPlayer*)(filter)) +#define MS_RING_PLAYER_CLASS(klass) ((MSRingPlayerClass*)(klass)) +MSFilter * ms_ring_player_new(char *name, gint seconds); +gint ms_ring_player_get_sample_freq(MSRingPlayer *obj); + + +/* FOR INTERNAL USE*/ +void ms_ring_player_init(MSRingPlayer *r); +void ms_ring_player_class_init(MSRingPlayerClass *klass); +void ms_ring_player_destroy( MSRingPlayer *obj); +void ms_ring_player_process(MSRingPlayer *r); +#define ms_ring_player_set_bufsize(filter,sz) (filter)->gran=(sz) +void ms_ring_player_setup(MSRingPlayer *r,MSSync *sync); +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c new file mode 100644 index 00000000..9b82e939 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c @@ -0,0 +1,163 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "msrtprecv.h" + + +/* some utilities to convert mblk_t to MSMessage and vice-versa */ +MSMessage *msgb_2_ms_message(mblk_t* mp){ + MSMessage *msg; + MSBuffer *msbuf; + if (mp->b_datap->ref_count!=1) return NULL; /* cannot handle properly non-unique buffers*/ + /* create a MSBuffer using the mblk_t buffer */ + msg=ms_message_alloc(); + msbuf=ms_buffer_alloc(0); + msbuf->buffer=mp->b_datap->db_base; + msbuf->size=(char*)mp->b_datap->db_lim-(char*)mp->b_datap->db_base; + ms_message_set_buf(msg,msbuf); + msg->size=mp->b_wptr-mp->b_rptr; + msg->data=mp->b_rptr; + /* free the mblk_t */ + g_free(mp->b_datap); + g_free(mp); + return msg; +} + + +static MSRtpRecvClass *ms_rtp_recv_class=NULL; + +MSFilter * ms_rtp_recv_new(void) +{ + MSRtpRecv *r; + + r=g_new(MSRtpRecv,1); + ms_rtp_recv_init(r); + if (ms_rtp_recv_class==NULL) + { + ms_rtp_recv_class=g_new0(MSRtpRecvClass,1); + ms_rtp_recv_class_init(ms_rtp_recv_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_rtp_recv_class); + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_rtp_recv_init(MSRtpRecv *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->outfifos=r->f_outputs; + MS_FILTER(r)->outqueues=r->q_outputs; + memset(r->f_outputs,0,sizeof(MSFifo*)*MSRTPRECV_MAX_OUTPUTS); + memset(r->q_outputs,0,sizeof(MSFifo*)*MSRTPRECV_MAX_OUTPUTS); + r->rtpsession=NULL; + r->stream_started=0; +} + +void ms_rtp_recv_class_init(MSRtpRecvClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"RTPRecv"); + MS_FILTER_CLASS(klass)->max_qoutputs=MSRTPRECV_MAX_OUTPUTS; + MS_FILTER_CLASS(klass)->max_foutputs=MSRTPRECV_MAX_OUTPUTS; + MS_FILTER_CLASS(klass)->w_maxgran=MSRTPRECV_DEF_GRAN; + ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE); + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_rtp_recv_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_rtp_recv_process; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_rtp_recv_setup; +} + +void ms_rtp_recv_process(MSRtpRecv *r) +{ + MSFifo *fo; + MSQueue *qo; + MSSync *sync= r->sync; + void *d; + mblk_t *mp; + gint len; + gint gran=ms_sync_get_samples_per_tick(MS_SYNC(sync)); + + if (r->rtpsession==NULL) return; + /* process output fifo and output queue*/ + fo=r->f_outputs[0]; + if (fo!=NULL) + { + while( (mp=rtp_session_recvm_with_ts(r->rtpsession,r->prev_ts))!=NULL) { + /* try to get rtp packets and paste them to the output fifo */ + r->stream_started=1; + len=mp->b_cont->b_wptr-mp->b_cont->b_rptr; + ms_fifo_get_write_ptr(fo,len,&d); + if (d!=NULL){ + memcpy(d,mp->b_cont->b_rptr,len); + }else ms_warning("ms_rtp_recv_process: no space on output fifo !"); + freemsg(mp); + } + r->prev_ts+=gran; + + } + qo=r->q_outputs[0]; + if (qo!=NULL) + { + guint32 clock; + gint got=0; + /* we are connected with queues (surely for video)*/ + /* use the sync system time to compute a timestamp */ + PayloadType *pt=rtp_profile_get_payload(r->rtpsession->profile,r->rtpsession->payload_type); + if (pt==NULL) { + ms_warning("ms_rtp_recv_process(): NULL RtpPayload- skipping."); + return; + } + clock=(guint32)(((double)sync->time*(double)pt->clock_rate)/1000.0); + /*g_message("Querying packet with timestamp %u",clock);*/ + /* get rtp packet, and send them through the output queue */ + while ( (mp=rtp_session_recvm_with_ts(r->rtpsession,clock))!=NULL ){ + MSMessage *msg; + mblk_t *mdata; + /*g_message("Got packet with timestamp %u",clock);*/ + got++; + r->stream_started=1; + mdata=mp->b_cont; + freeb(mp); + msg=msgb_2_ms_message(mdata); + ms_queue_put(qo,msg); + } + } +} + +void ms_rtp_recv_destroy( MSRtpRecv *obj) +{ + g_free(obj); +} + +RtpSession * ms_rtp_recv_set_session(MSRtpRecv *obj,RtpSession *session) +{ + RtpSession *old=obj->rtpsession; + obj->rtpsession=session; + obj->prev_ts=0; + return old; +} + + +void ms_rtp_recv_setup(MSRtpRecv *r,MSSync *sync) +{ + r->sync=sync; + r->stream_started=0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h new file mode 100644 index 00000000..8c2c2ed7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h @@ -0,0 +1,80 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSRTPRECV_H +#define MSRTPRECV_H + +#include "msfilter.h" +#include "mssync.h" + +/* because of a conflict between config.h from oRTP and config.h from linphone:*/ +#undef PACKAGE +#undef VERSION +#include <ortp/ortp.h> + +/*this is the class that implements a copy filter*/ + +#define MSRTPRECV_MAX_OUTPUTS 1 /* max output per filter*/ + +#define MSRTPRECV_DEF_GRAN 4096 /* the default granularity*/ + +struct _MSRtpRecv +{ + /* the MSCopy derivates from MSFilter, so the MSFilter object MUST be the first of the MSCopy object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_outputs[MSRTPRECV_MAX_OUTPUTS]; + MSQueue *q_outputs[MSRTPRECV_MAX_OUTPUTS]; + MSSync *sync; + RtpSession *rtpsession; + guint32 prev_ts; + gint stream_started; +}; + +typedef struct _MSRtpRecv MSRtpRecv; + +struct _MSRtpRecvClass +{ + /* the MSCopy derivates from MSFilter, so the MSFilter class MUST be the first of the MSCopy class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +}; + +typedef struct _MSRtpRecvClass MSRtpRecvClass; + +/* PUBLIC */ +#define MS_RTP_RECV(filter) ((MSRtpRecv*)(filter)) +#define MS_RTP_RECV_CLASS(klass) ((MSRtpRecvClass*)(klass)) +MSFilter * ms_rtp_recv_new(void); +RtpSession * ms_rtp_recv_set_session(MSRtpRecv *obj,RtpSession *session); +#define ms_rtp_recv_unset_session(obj) (ms_rtp_recv_set_session((obj),NULL)) +#define ms_rtp_recv_get_session(obj) ((obj)->rtpsession) + + + +/* FOR INTERNAL USE*/ +void ms_rtp_recv_init(MSRtpRecv *r); +void ms_rtp_recv_class_init(MSRtpRecvClass *klass); +void ms_rtp_recv_destroy( MSRtpRecv *obj); +void ms_rtp_recv_process(MSRtpRecv *r); +void ms_rtp_recv_setup(MSRtpRecv *r,MSSync *sync); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c new file mode 100644 index 00000000..cfcb6b34 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c @@ -0,0 +1,211 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "msrtpsend.h" +#include <ortp/telephonyevents.h> +#include "mssync.h" +#include "mscodec.h" + + + +static MSRtpSendClass *ms_rtp_send_class=NULL; + +MSFilter * ms_rtp_send_new(void) +{ + MSRtpSend *r; + + r=g_new(MSRtpSend,1); + + if (ms_rtp_send_class==NULL) + { + ms_rtp_send_class=g_new(MSRtpSendClass,1); + ms_rtp_send_class_init(ms_rtp_send_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_rtp_send_class); + ms_rtp_send_init(r); + return(MS_FILTER(r)); +} + + +void ms_rtp_send_init(MSRtpSend *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->inqueues=r->q_inputs; + MS_FILTER(r)->r_mingran=MSRTPSEND_DEF_GRAN; + memset(r->f_inputs,0,sizeof(MSFifo*)*MSRTPSEND_MAX_INPUTS); + memset(r->q_inputs,0,sizeof(MSFifo*)*MSRTPSEND_MAX_INPUTS); + r->rtpsession=NULL; + r->ts=0; + r->ts_inc=0; + r->flags=0; + r->delay=0; +} + +void ms_rtp_send_class_init(MSRtpSendClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"RTPSend"); + MS_FILTER_CLASS(klass)->max_qinputs=MSRTPSEND_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_finputs=MSRTPSEND_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=MSRTPSEND_DEF_GRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_rtp_send_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_rtp_send_process; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_rtp_send_setup; +} + +void ms_rtp_send_set_timing(MSRtpSend *r, guint32 ts_inc, gint payload_size) +{ + r->ts_inc=ts_inc; + r->packet_size=payload_size; + if (r->ts_inc!=0) r->flags|=RTPSEND_CONFIGURED; + else r->flags&=~RTPSEND_CONFIGURED; + MS_FILTER(r)->r_mingran=payload_size; + /*g_message("ms_rtp_send_set_timing: ts_inc=%i",ts_inc);*/ +} + +guint32 get_new_timestamp(MSRtpSend *r,guint32 synctime) +{ + guint32 clockts; + /* use the sync system time to compute a timestamp */ + PayloadType *pt=rtp_profile_get_payload(r->rtpsession->profile,r->rtpsession->payload_type); + g_return_val_if_fail(pt!=NULL,0); + clockts=(guint32)(((double)synctime * (double)pt->clock_rate)/1000.0); + ms_trace("ms_rtp_send_process: sync->time=%i clock=%i",synctime,clockts); + if (r->flags & RTPSEND_CONFIGURED){ + if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(clockts,r->ts+(2*r->ts_inc) )){ + r->ts=clockts; + } + else r->ts+=r->ts_inc; + }else{ + r->ts=clockts; + } + return r->ts; +} + + +void ms_rtp_send_process(MSRtpSend *r) +{ + MSFifo *fi; + MSQueue *qi; + MSSync *sync= r->sync; + int gran=ms_sync_get_samples_per_tick(sync); + guint32 ts; + void *s; + guint skip; + guint32 synctime=sync->time; + + g_return_if_fail(gran>0); + if (r->rtpsession==NULL) return; + + ms_filter_lock(MS_FILTER(r)); + skip=r->delay!=0; + if (skip) r->delay--; + /* process output fifo and output queue*/ + fi=r->f_inputs[0]; + if (fi!=NULL) + { + ts=get_new_timestamp(r,synctime); + /* try to read r->packet_size bytes and send them in a rtp packet*/ + ms_fifo_get_read_ptr(fi,r->packet_size,&s); + if (!skip){ + rtp_session_send_with_ts(r->rtpsession,s,r->packet_size,ts); + ms_trace("len=%i, ts=%i ",r->packet_size,ts); + } + } + qi=r->q_inputs[0]; + if (qi!=NULL) + { + MSMessage *msg; + /* read a MSMessage and send it through the network*/ + while ( (msg=ms_queue_get(qi))!=NULL){ + ts=get_new_timestamp(r,synctime); + if (!skip) { + /*g_message("Sending packet with ts=%u",ts);*/ + rtp_session_send_with_ts(r->rtpsession,msg->data,msg->size,ts); + + } + ms_message_destroy(msg); + } + } + ms_filter_unlock(MS_FILTER(r)); +} + +void ms_rtp_send_destroy( MSRtpSend *obj) +{ + g_free(obj); +} + +RtpSession * ms_rtp_send_set_session(MSRtpSend *obj,RtpSession *session) +{ + RtpSession *old=obj->rtpsession; + obj->rtpsession=session; + obj->ts=0; + obj->ts_inc=0; + return old; +} + +void ms_rtp_send_setup(MSRtpSend *r, MSSync *sync) +{ + MSFilter *codec; + MSCodecInfo *info; + r->sync=sync; + codec=ms_filter_search_upstream_by_type(MS_FILTER(r),MS_FILTER_AUDIO_CODEC); + if (codec==NULL) codec=ms_filter_search_upstream_by_type(MS_FILTER(r),MS_FILTER_VIDEO_CODEC); + if (codec==NULL){ + g_warning("ms_rtp_send_setup: could not find upstream codec."); + return; + } + info=MS_CODEC_INFO(codec->klass->info); + if (info->info.type==MS_FILTER_AUDIO_CODEC){ + int ts_inc=info->fr_size/2; + int psize=info->dt_size; + if (ts_inc==0){ + /* dont'use the normal frame size: this is a variable frame size codec */ + /* use the MS_FILTER(codec)->r_mingran */ + ts_inc=MS_FILTER(codec)->r_mingran/2; + psize=0; + } + ms_rtp_send_set_timing(r,ts_inc,psize); + } +} + +gint ms_rtp_send_dtmf(MSRtpSend *r, gchar dtmf) +{ + gint res; + + if (r->rtpsession==NULL) return -1; + if (rtp_session_telephone_events_supported(r->rtpsession)==-1){ + g_warning("ERROR : telephone events not supported.\n"); + return -1; + } + + ms_filter_lock(MS_FILTER(r)); + g_message("Sending DTMF."); + res=rtp_session_send_dtmf(r->rtpsession, dtmf, r->ts); + if (res==0){ + /* //r->ts+=r->ts_inc; */ + r->delay+=2; + }else g_warning("Could not send dtmf."); + + ms_filter_unlock(MS_FILTER(r)); + + return res; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h new file mode 100644 index 00000000..b70f4e55 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h @@ -0,0 +1,85 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSRTPSEND_H +#define MSRTPSEND_H + +#include "msfilter.h" +#include "mssync.h" + +#undef PACKAGE +#undef VERSION +#include <ortp/ortp.h> + + +/*this is the class that implements a sending through rtp filter*/ + +#define MSRTPSEND_MAX_INPUTS 1 /* max input per filter*/ + +#define MSRTPSEND_DEF_GRAN 4096/* the default granularity*/ + +struct _MSRtpSend +{ + /* the MSCopy derivates from MSFilter, so the MSFilter object MUST be the first of the MSCopy object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSRTPSEND_MAX_INPUTS]; + MSQueue *q_inputs[MSRTPSEND_MAX_INPUTS]; + MSSync *sync; + RtpSession *rtpsession; + guint32 ts; + guint32 ts_inc; /* the timestamp increment */ + gint packet_size; + guint flags; + guint delay; /* number of _proccess call which must be skipped */ +#define RTPSEND_CONFIGURED (1) +}; + +typedef struct _MSRtpSend MSRtpSend; + +struct _MSRtpSendClass +{ + /* the MSRtpSend derivates from MSFilter, so the MSFilter class MUST be the first of the MSCopy class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +}; + +typedef struct _MSRtpSendClass MSRtpSendClass; + +/* PUBLIC */ +#define MS_RTP_SEND(filter) ((MSRtpSend*)(filter)) +#define MS_RTP_SEND_CLASS(klass) ((MSRtpSendClass*)(klass)) +MSFilter * ms_rtp_send_new(void); +RtpSession * ms_rtp_send_set_session(MSRtpSend *obj,RtpSession *session); +#define ms_rtp_send_unset_session(obj) (ms_rtp_send_set_session((obj),NULL)) +#define ms_rtp_send_get_session(obj) ((obj)->rtpsession) +void ms_rtp_send_set_timing(MSRtpSend *r, guint32 ts_inc, gint payload_size); +gint ms_rtp_send_dtmf(MSRtpSend *r, gchar dtmf); + + +/* FOR INTERNAL USE*/ +void ms_rtp_send_init(MSRtpSend *r); +void ms_rtp_send_class_init(MSRtpSendClass *klass); +void ms_rtp_send_destroy( MSRtpSend *obj); +void ms_rtp_send_process(MSRtpSend *r); +void ms_rtp_send_setup(MSRtpSend *r, MSSync *sync); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h new file mode 100644 index 00000000..fd6ec547 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * mssdlout.h + * + * Mon Jul 11 16:18:55 2005 + * Copyright 2005 Simon Morlat + * Email simon dot morlat at linphone dot org + ****************************************************************************/ + +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef mssdlout_h +#define mssdlout_h + +#include "msfilter.h" + +#include <SDL/SDL.h> +#include <SDL/SDL_video.h> + +struct _MSSdlOut +{ + MSFilter parent; + MSQueue *input[2]; + gint width,height; + const gchar *format; + SDL_Surface *screen; + SDL_Overlay *overlay; + MSMessage *oldinm1; + gboolean use_yuv; +}; + + +typedef struct _MSSdlOut MSSdlOut; + +struct _MSSdlOutClass +{ + MSFilterClass parent_class; +}; + +typedef struct _MSSdlOutClass MSSdlOutClass; + +MSFilter * ms_sdl_out_new(void); +void ms_sdl_out_set_format(MSSdlOut *obj, const char *fmt); + +#define MS_SDL_OUT(obj) ((MSSdlOut*)obj) + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c new file mode 100644 index 00000000..3803b018 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c @@ -0,0 +1,39 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation + + */ + +#include "mssoundread.h" + + +void ms_sound_read_init(MSSoundRead *w) +{ + ms_filter_init(MS_FILTER(w)); + +} + +void ms_sound_read_class_init(MSSoundReadClass *klass) +{ + int i; + ms_filter_class_init(MS_FILTER_CLASS(klass)); + MS_FILTER_CLASS(klass)->max_foutputs=1; /* one fifo output only */ + + ms_filter_class_set_attr( MS_FILTER_CLASS(klass),FILTER_IS_SOURCE); +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h new file mode 100644 index 00000000..7f2cab93 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h @@ -0,0 +1,80 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MSSOUNDREAD_H +#define MSSOUNDREAD_H + +#include "msfilter.h" +#include "mssync.h" + + + +struct _MSSoundRead +{ + /* the MSOssRead derivates from MSFilter, so the MSFilter object MUST be the first of the MSOssRead object + in order to the object mechanism to work*/ + MSFilter filter; +}; + +typedef struct _MSSoundRead MSSoundRead; + +struct _MSSoundReadClass +{ + /* the MSOssRead derivates from MSFilter, so the MSFilter class MUST be the first of the MSOssRead class + in order to the class mechanism to work*/ + MSFilterClass parent_class; + gint (*set_device)(MSSoundRead *, gint devid); + void (*start)(MSSoundRead *); + void (*stop)(MSSoundRead*); + void (*set_level)(MSSoundRead *, gint a); +}; + +typedef struct _MSSoundReadClass MSSoundReadClass; + +/* PUBLIC */ +#define MS_SOUND_READ(filter) ((MSSoundRead*)(filter)) +#define MS_SOUND_READ_CLASS(klass) ((MSSoundReadClass*)(klass)) + +static inline int ms_sound_read_set_device(MSSoundRead *r,gint devid) +{ + return MS_SOUND_READ_CLASS( MS_FILTER(r)->klass )->set_device(r,devid); +} + +static inline void ms_sound_read_start(MSSoundRead *r) +{ + MS_SOUND_READ_CLASS( MS_FILTER(r)->klass )->start(r); +} + +static inline void ms_sound_read_stop(MSSoundRead *w) +{ + MS_SOUND_READ_CLASS( MS_FILTER(w)->klass )->stop(w); +} + +static inline void ms_sound_read_set_level(MSSoundRead *w,gint a) +{ + MS_SOUND_READ_CLASS( MS_FILTER(w)->klass )->set_level(w,a); +} + +/* FOR INTERNAL USE*/ +void ms_sound_read_init(MSSoundRead *r); +void ms_sound_read_class_init(MSSoundReadClass *klass); + + +#endif + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c new file mode 100644 index 00000000..9c5879f4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c @@ -0,0 +1,39 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation + + */ + +#include "mssoundwrite.h" + + +void ms_sound_write_init(MSSoundWrite *w) +{ + ms_filter_init(MS_FILTER(w)); + +} + +void ms_sound_write_class_init(MSSoundWriteClass *klass) +{ + int i; + ms_filter_class_init(MS_FILTER_CLASS(klass)); + MS_FILTER_CLASS(klass)->max_finputs=1; /* one fifo output only */ + + ms_filter_class_set_attr( MS_FILTER_CLASS(klass),FILTER_IS_SINK); +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h new file mode 100644 index 00000000..e6d79874 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h @@ -0,0 +1,80 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MSSOUNDWRITE_H +#define MSSOUNDWRITE_H + +#include "msfilter.h" +#include "mssync.h" + + + +struct _MSSoundWrite +{ + /* the MSOssWrite derivates from MSFilter, so the MSFilter object MUST be the first of the MSOssWrite object + in order to the object mechanism to work*/ + MSFilter filter; +}; + +typedef struct _MSSoundWrite MSSoundWrite; + +struct _MSSoundWriteClass +{ + /* the MSOssWrite derivates from MSFilter, so the MSFilter class MUST be the first of the MSOssWrite class + in order to the class mechanism to work*/ + MSFilterClass parent_class; + gint (*set_device)(MSSoundWrite *, gint devid); + void (*start)(MSSoundWrite *); + void (*stop)(MSSoundWrite*); + void (*set_level)(MSSoundWrite *, gint a); +}; + +typedef struct _MSSoundWriteClass MSSoundWriteClass; + +/* PUBLIC */ +#define MS_SOUND_WRITE(filter) ((MSSoundWrite*)(filter)) +#define MS_SOUND_WRITE_CLASS(klass) ((MSSoundWriteClass*)(klass)) + +static inline int ms_sound_write_set_device(MSSoundWrite *r,gint devid) +{ + return MS_SOUND_WRITE_CLASS( MS_FILTER(r)->klass )->set_device(r,devid); +} + +static inline void ms_sound_write_start(MSSoundWrite *r) +{ + MS_SOUND_WRITE_CLASS( MS_FILTER(r)->klass )->start(r); +} + +static inline void ms_sound_write_stop(MSSoundWrite *w) +{ + MS_SOUND_WRITE_CLASS( MS_FILTER(w)->klass )->stop(w); +} + +static inline void ms_sound_write_set_level(MSSoundWrite *w,gint a) +{ + MS_SOUND_WRITE_CLASS( MS_FILTER(w)->klass )->set_level(w,a); +} + +/* FOR INTERNAL USE*/ +void ms_sound_write_init(MSSoundWrite *r); +void ms_sound_write_class_init(MSSoundWriteClass *klass); + + +#endif + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c new file mode 100644 index 00000000..b91ca360 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c @@ -0,0 +1,218 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <config.h> + +#ifdef HAVE_SPEEX + +#include "msspeexdec.h" + +#ifdef HAVE_GLIB +#include <gmodule.h> +#endif + +extern MSFilter * ms_speex_enc_new(); + +MSCodecInfo speex_info= +{ + { + "Speex codec", + 0, + MS_FILTER_AUDIO_CODEC, + ms_speex_dec_new, + "A high quality variable bit-rate codec from Jean Marc Valin and David Rowe." + }, + ms_speex_enc_new, + ms_speex_dec_new, + 0, /*frame size */ + 0, + 8000, /*minimal bitrate */ + -1, /* sampling frequency */ + 110, /* payload type */ + "speex", + 1, + 1 +}; + + + +void ms_speex_codec_init() +{ + + ms_filter_register(MS_FILTER_INFO(&speex_info)); + /* //ms_filter_register(MS_FILTER_INFO(&speex_lbr_info)); */ +} + +#ifdef HAVE_GLIB +gchar * g_module_check_init(GModule *module) +{ + ms_speex_codec_init(); + + return NULL; +} +#else +gchar * g_module_check_init() +{ + ms_speex_codec_init(); + + return NULL; +} +#endif + +static MSSpeexDecClass * ms_speex_dec_class=NULL; +/* //static MSSpeexDecClass * ms_speexnb_dec_class=NULL; */ + +MSFilter * ms_speex_dec_new() +{ + MSSpeexDec *obj=g_new(MSSpeexDec,1); + + if (ms_speex_dec_class==NULL){ + ms_speex_dec_class=g_new(MSSpeexDecClass,1); + ms_speex_dec_class_init(ms_speex_dec_class); + } + MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_speex_dec_class); + + ms_speex_dec_init(obj); + return MS_FILTER(obj); +} + +void ms_speex_dec_init(MSSpeexDec *obj) +{ + ms_filter_init(MS_FILTER(obj)); + obj->initialized=0; + MS_FILTER(obj)->outfifos=obj->outf; + MS_FILTER(obj)->inqueues=obj->inq; + obj->outf[0]=NULL; + obj->inq[0]=NULL; + obj->frequency=8000; /*default value */ + +} + +void ms_speex_dec_init_core(MSSpeexDec *obj,const SpeexMode *mode) +{ + int pf=1; + + obj->speex_state=speex_decoder_init(mode); + speex_bits_init(&obj->bits); + /* enable the perceptual post filter */ + speex_decoder_ctl(obj->speex_state,SPEEX_SET_PF, &pf); + + speex_mode_query(mode, SPEEX_MODE_FRAME_SIZE, &obj->frame_size); + + obj->initialized=1; +} + +int ms_speex_dec_set_property(MSSpeexDec *obj, MSFilterProperty prop, int *value) +{ + if (obj->initialized){ + /* we are called when speex is running !! forbid that! */ + ms_warning("ms_speex_dec_set_property: cannot call this function when running!"); + return -1; + } + switch(prop){ + case MS_FILTER_PROPERTY_FREQ: + obj->frequency=value[0]; + break; + } + return 0; +} + +void ms_speex_dec_setup(MSSpeexDec *obj) +{ + const SpeexMode *mode; + g_message("Speex decoder setup: freq=%i",obj->frequency); + if ( obj->frequency< 16000) mode=&speex_nb_mode; + else mode=&speex_wb_mode; + ms_speex_dec_init_core(obj,mode); +} + +void ms_speex_dec_unsetup(MSSpeexDec *obj) +{ + ms_speex_dec_uninit_core(obj); +} + +void ms_speex_dec_class_init(MSSpeexDecClass *klass) +{ + gint frame_size=0; + + ms_filter_class_init(MS_FILTER_CLASS(klass)); + /* use the largest frame size to configure fifos */ + speex_mode_query(&speex_wb_mode, SPEEX_MODE_FRAME_SIZE, &frame_size); + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_speex_dec_process; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_speex_dec_setup; + MS_FILTER_CLASS(klass)->unsetup=(MSFilterSetupFunc)ms_speex_dec_unsetup; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_speex_dec_destroy; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_speex_dec_set_property; + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"SpeexDecoder"); + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&speex_info; + MS_FILTER_CLASS(klass)->max_foutputs=1; + MS_FILTER_CLASS(klass)->max_qinputs=1; + MS_FILTER_CLASS(klass)->w_maxgran=frame_size*2; + ms_trace("ms_speex_dec_class_init: w_maxgran is %i.",MS_FILTER_CLASS(klass)->w_maxgran); +} + +void ms_speex_dec_uninit_core(MSSpeexDec *obj) +{ + speex_decoder_destroy(obj->speex_state); + obj->initialized=0; +} + +void ms_speex_dec_uninit(MSSpeexDec *obj) +{ + +} + +void ms_speex_dec_destroy(MSSpeexDec *obj) +{ + ms_speex_dec_uninit(obj); + g_free(obj); +} + +void ms_speex_dec_process(MSSpeexDec *obj) +{ + MSFifo *outf=obj->outf[0]; + MSQueue *inq=obj->inq[0]; + gint16 *output; + gint gran=obj->frame_size*2; + gint i; + MSMessage *m; + + g_return_if_fail(inq!=NULL); + g_return_if_fail(outf!=NULL); + + m=ms_queue_get(inq); + g_return_if_fail(m!=NULL); + speex_bits_reset(&obj->bits); + ms_fifo_get_write_ptr(outf,gran,(void**)&output); + g_return_if_fail(output!=NULL); + if (m->data!=NULL){ + + speex_bits_read_from(&obj->bits,m->data,m->size); + /* decode */ + speex_decode_int(obj->speex_state,&obj->bits,(short*)output); + }else{ + /* we have a missing packet */ + speex_decode_int(obj->speex_state,NULL,(short*)output); + } + ms_message_destroy(m); + +} + +#endif /* HAVE_SPEEX */ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h new file mode 100644 index 00000000..d4e745fe --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h @@ -0,0 +1,69 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSSPEEXDEC_H +#define MSSPEEXDEC_H + +#include <mscodec.h> +#include <speex.h> + +struct _MSSpeexDec +{ + MSFilter parent; + MSQueue *inq[1]; /* speex has an input q because it can be variable bit rate */ + MSFifo *outf[1]; + void *speex_state; + SpeexBits bits; + int frequency; + int frame_size; + int initialized; +}; + +typedef struct _MSSpeexDec MSSpeexDec; + + +struct _MSSpeexDecClass +{ + MSFilterClass parent; +}; + +typedef struct _MSSpeexDecClass MSSpeexDecClass; + + +#define MS_SPEEX_DEC(o) ((MSSpeexDec*)(o)) +#define MS_SPEEX_DEC_CLASS(o) ((MSSpeexDecClass*)(o)) + +/* call this before if don't load the plugin dynamically */ +void ms_speex_codec_init(); + +/* mediastreamer compliant constructor */ +MSFilter * ms_speex_dec_new(); + +void ms_speex_dec_init(MSSpeexDec *obj); +void ms_speex_dec_init_core(MSSpeexDec *obj,const SpeexMode *mode); +void ms_speex_dec_class_init(MSSpeexDecClass *klass); +void ms_speex_dec_uninit(MSSpeexDec *obj); +void ms_speex_dec_uninit_core(MSSpeexDec *obj); + +void ms_speex_dec_process(MSSpeexDec *obj); +void ms_speex_dec_destroy(MSSpeexDec *obj); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c new file mode 100644 index 00000000..abf976e6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c @@ -0,0 +1,192 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <config.h> + +#ifdef HAVE_SPEEX + +#include "msspeexenc.h" +#include "ms.h" +extern MSCodecInfo speex_info; + +static MSSpeexEncClass * ms_speex_enc_class=NULL; + +MSFilter * ms_speex_enc_new() +{ + MSSpeexEnc *obj=g_new(MSSpeexEnc,1); + + if (ms_speex_enc_class==NULL){ + ms_speex_enc_class=g_new(MSSpeexEncClass,1); + ms_speex_enc_class_init(ms_speex_enc_class); + } + MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_speex_enc_class); + ms_speex_enc_init(MS_SPEEX_ENC(obj)); + return MS_FILTER(obj); +} + +void ms_speex_enc_init(MSSpeexEnc *obj) +{ + ms_filter_init(MS_FILTER(obj)); + MS_FILTER(obj)->infifos=obj->inf; + MS_FILTER(obj)->outqueues=obj->outq; + obj->inf[0]=NULL; + obj->outq[0]=NULL; + obj->frequency=8000; + obj->bitrate=30000; + obj->initialized=0; +} + +void ms_speex_enc_init_core(MSSpeexEnc *obj,const SpeexMode *mode, gint bitrate) +{ + int proc_type, proc_speed; + gchar *proc_vendor; + int tmp; + int frame_size; + + obj->speex_state=speex_encoder_init(mode); + speex_bits_init(&obj->bits); + + if (bitrate>0) { + bitrate++; + speex_encoder_ctl(obj->speex_state, SPEEX_SET_BITRATE, &bitrate); + g_message("Setting speex output bitrate less or equal than %i",bitrate-1); + } + + proc_speed=ms_proc_get_speed(); + proc_vendor=ms_proc_get_param("vendor_id"); + if (proc_speed<0 || proc_vendor==NULL){ + g_warning("Can't guess processor features: setting speex encoder to its lowest complexity."); + tmp=1; + speex_encoder_ctl(obj->speex_state,SPEEX_SET_COMPLEXITY,&tmp); + }else if ((proc_speed!=-1) && (proc_speed<200)){ + g_warning("A cpu speed less than 200 Mhz is not enough: let's reduce the complexity of the speex codec."); + tmp=1; + speex_encoder_ctl(obj->speex_state,SPEEX_SET_COMPLEXITY,&tmp); + }else if (proc_vendor!=NULL) { + if (strncmp(proc_vendor,"GenuineIntel",strlen("GenuineIntel"))==0){ + proc_type=ms_proc_get_type(); + if (proc_type==5){ + g_warning("A pentium I is not enough fast for speex codec in normal mode: let's reduce its complexity."); + tmp=1; + speex_encoder_ctl(obj->speex_state,SPEEX_SET_COMPLEXITY,&tmp); + } + } + g_free(proc_vendor); + } + /* guess the used input frame size */ + speex_mode_query(mode, SPEEX_MODE_FRAME_SIZE, &frame_size); + MS_FILTER(obj)->r_mingran=frame_size*2; + ms_trace("ms_speex_init: using frame size of %i.",MS_FILTER(obj)->r_mingran); + + obj->initialized=1; +} + +/* must be called before the encoder is running*/ +int ms_speex_enc_set_property(MSSpeexEnc *obj,int property,int *value) +{ + if (obj->initialized){ + /* we are called when speex is running !! forbid that! */ + ms_warning("ms_speex_enc_set_property: cannot call this function when running!"); + return -1; + } + switch(property){ + case MS_FILTER_PROPERTY_FREQ: + obj->frequency=value[0]; + break; + case MS_FILTER_PROPERTY_BITRATE: /* to specify max bitrate */ + obj->bitrate=value[0]; + break; + } + return 0; +} + +void ms_speex_enc_setup(MSSpeexEnc *obj) +{ + const SpeexMode *mode; + int quality; + g_message("Speex encoder setup: freq=%i",obj->frequency); + if ( obj->frequency< 16000) mode=&speex_nb_mode; + else mode=&speex_wb_mode; + ms_speex_enc_init_core(obj,mode,obj->bitrate); + +} + +void ms_speex_enc_unsetup(MSSpeexEnc *obj) +{ + ms_speex_enc_uninit_core(obj); +} + +void ms_speex_enc_class_init(MSSpeexEncClass *klass) +{ + gint frame_size=0; + + ms_filter_class_init(MS_FILTER_CLASS(klass)); + /* we take the larger (wb) frame size */ + speex_mode_query(&speex_wb_mode, SPEEX_MODE_FRAME_SIZE, &frame_size); + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_speex_enc_process; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_speex_enc_destroy; + MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_speex_enc_setup; + MS_FILTER_CLASS(klass)->unsetup=(MSFilterSetupFunc)ms_speex_enc_unsetup; + MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_speex_enc_set_property; + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"SpeexEncoder"); + MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&speex_info; + MS_FILTER_CLASS(klass)->max_finputs=1; + MS_FILTER_CLASS(klass)->max_qoutputs=1; + MS_FILTER_CLASS(klass)->r_maxgran=frame_size*2; + ms_trace("ms_speex_enc_class_init: r_maxgran is %i.",MS_FILTER_CLASS(klass)->r_maxgran); +} + +void ms_speex_enc_uninit_core(MSSpeexEnc *obj) +{ + if (obj->initialized){ + speex_encoder_destroy(obj->speex_state); + obj->initialized=0; + } +} + +void ms_speex_enc_destroy(MSSpeexEnc *obj) +{ + ms_speex_enc_uninit_core(obj); + g_free(obj); +} + +void ms_speex_enc_process(MSSpeexEnc *obj) +{ + MSFifo *inf=obj->inf[0]; + MSQueue *outq=obj->outq[0]; + gint16 *input; + gint gran=MS_FILTER(obj)->r_mingran; + gint i; + MSMessage *m; + + g_return_if_fail(inf!=NULL); + g_return_if_fail(outq!=NULL); + + ms_fifo_get_read_ptr(inf,gran,(void**)&input); + g_return_if_fail(input!=NULL); + /* encode */ + speex_bits_reset(&obj->bits); + speex_encode_int(obj->speex_state,(short*)input,&obj->bits); + m=ms_message_new(speex_bits_nbytes(&obj->bits)); + m->size=speex_bits_write(&obj->bits,m->data,m->size); + ms_queue_put(outq,m); +} + +#endif /* HAVE_SPEEX */ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h new file mode 100644 index 00000000..41655b9f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h @@ -0,0 +1,66 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSSPEEXENC_H +#define MSSPEEXENC_H + +#include <mscodec.h> +#include <speex.h> + +struct _MSSpeexEnc +{ + MSFilter parent; + MSFifo *inf[1]; + MSQueue *outq[1]; /* speex has an output q because it can be variable bit rate */ + void *speex_state; + SpeexBits bits; + int frequency; + int bitrate; + int initialized; +}; + +typedef struct _MSSpeexEnc MSSpeexEnc; + + +struct _MSSpeexEncClass +{ + MSFilterClass parent; +}; + +typedef struct _MSSpeexEncClass MSSpeexEncClass; + + +#define MS_SPEEX_ENC(o) ((MSSpeexEnc*)(o)) +#define MS_SPEEX_ENC_CLASS(o) ((MSSpeexEncClass*)(o)) + +/* generic constructor */ +MSFilter * ms_speex_enc_new(); + +void ms_speex_enc_init_core(MSSpeexEnc *obj,const SpeexMode *mode, gint quality); +void ms_speex_enc_uninit_core(MSSpeexEnc *obj); +void ms_speex_enc_init(MSSpeexEnc *obj); +void ms_speex_enc_class_init(MSSpeexEncClass *klass); + + +void ms_speex_enc_process(MSSpeexEnc *obj); +void ms_speex_enc_destroy(MSSpeexEnc *obj); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c new file mode 100644 index 00000000..7656211b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c @@ -0,0 +1,193 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "mssync.h" +#include <errno.h> + +/* TODO: + -define an uninit function that free the mutex +*/ + +/** + * function_name:ms_sync_get_bytes_per_tick + * @sync: A #MSSync object. + * + * Returns the number of bytes per tick. This is a usefull information for sources, so + * that they can know how much data they must deliver each time they are called. + * + */ + +/* private */ +void ms_sync_init(MSSync *sync) +{ + sync->klass=NULL; + sync->lock=g_mutex_new(); + sync->thread_cond=g_cond_new(); + sync->stop_cond=g_cond_new(); + sync->attached_filters=NULL; + sync->execution_list=NULL; + sync->filters=0; + sync->run=0; + sync->flags=0; + sync->samples_per_tick=0; + sync->ticks=0; + sync->time=0; + sync->thread=NULL; +} + +void ms_sync_class_init(MSSyncClass *klass) +{ + klass->max_filters=0; + klass->synchronize=NULL; + klass->attach=ms_sync_attach_generic; + klass->detach=ms_sync_detach_generic; + klass->destroy=NULL; +} + +/* public*/ + + +/** + * ms_sync_attach: + * @sync: A #MSSync object. + * @f: A #MSFilter object. + * + * Attach a chain of filters to a synchronisation source @sync. Filter @f must be the first filter of the processing chain. + * In order to be run, each chain of filter must be attached to a synchronisation source, that will be responsible for scheduling + * the processing. Multiple chains can be attached to a single synchronisation. + * + * Returns: 0 if successfull, a negative value reprensenting the errno.h error. + */ +int ms_sync_attach(MSSync *sync,MSFilter *f) +{ + gint err; + ms_sync_lock(sync); + err=sync->klass->attach(sync,f); + ms_sync_update(sync); + ms_sync_unlock(sync); + return(err); +} + +int ms_sync_attach_generic(MSSync *sync,MSFilter *f) +{ + int i; + /* //printf("attr: %i\n",f->klass->attributes); */ + g_return_val_if_fail(f->klass->attributes & FILTER_IS_SOURCE,-EINVAL); + g_return_val_if_fail(sync->attached_filters!=NULL,-EFAULT); + + + /* find a free place to attach*/ + for (i=0;i<sync->klass->max_filters;i++) + { + if (sync->attached_filters[i]==NULL) + { + sync->attached_filters[i]=f; + sync->filters++; + ms_trace("Filter succesfully attached to sync."); + return 0; + } + } + g_warning("No more link on sync !"); + return(-EMLINK); +} + +/** + * ms_sync_detach: + * @sync: A #MSSync object. + * @f: A #MSFilter object. + * + * Dettach a chain of filters to a synchronisation source. Filter @f must be the first filter of the processing chain. + * The processing chain will no more be executed. + * + * Returns: 0 if successfull, a negative value reprensenting the errno.h error. + */ +int ms_sync_detach(MSSync *sync,MSFilter *f) +{ + gint err; + ms_sync_lock(sync); + err=sync->klass->detach(sync,f); + ms_sync_update(sync); + ms_sync_unlock(sync); + return(err); +} + +int ms_sync_detach_generic(MSSync *sync,MSFilter *f) +{ + int i; + g_return_val_if_fail(f->klass->attributes & FILTER_IS_SOURCE,-EINVAL); + g_return_val_if_fail(sync->attached_filters!=NULL,-EFAULT); + for (i=0;i<sync->filters;i++) + { + if (sync->attached_filters[i]==f) + { + sync->attached_filters[i]=NULL; + sync->filters--; + return 0; + } + } + return(-EMLINK); +} + +void ms_sync_set_samples_per_tick(MSSync *sync,gint size) +{ + if (sync->samples_per_tick==0) + { + sync->samples_per_tick=size; + g_cond_signal(sync->thread_cond); + } + else sync->samples_per_tick=size; +} + +/* call the setup func of each filter attached to the graph */ +void ms_sync_setup(MSSync *sync) +{ + GList *elem=sync->execution_list; + MSFilter *f; + while(elem!=NULL){ + f=(MSFilter*)elem->data; + if (f->klass->setup!=NULL){ + f->klass->setup(f,sync); + } + elem=g_list_next(elem); + } +} + +/* call the unsetup func of each filter attached to the graph */ +void ms_sync_unsetup(MSSync *sync) +{ + GList *elem=sync->execution_list; + MSFilter *f; + while(elem!=NULL){ + f=(MSFilter*)elem->data; + if (f->klass->unsetup!=NULL){ + f->klass->unsetup(f,sync); + } + elem=g_list_next(elem); + } +} + + +int ms_sync_uninit(MSSync *sync) +{ + g_mutex_free(sync->lock); + g_cond_free(sync->thread_cond); + g_cond_free(sync->stop_cond); +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h new file mode 100644 index 00000000..012c068f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h @@ -0,0 +1,136 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MS_SYNC_H +#define MS_SYNC_H + + +#include "msfilter.h" + +struct _MSSync +{ + struct _MSSyncClass *klass; + GMutex *lock; + MSFilter **attached_filters; /* pointer to a table of pointer of filters*/ + GList *execution_list; /* the list of filters to be executed. This is filled with compilation */ + gint filters; /*number of filters attached to the sync */ + gint run; /* flag to indicate whether the sync must be run or not */ + GThread * thread; /* the thread ressource if this sync is run by a thread*/ + GCond *thread_cond; + GCond *stop_cond; + guint32 flags; + gint interval; /* in miliseconds*/ +#define MS_SYNC_NEED_UPDATE (0x0001) /* a modification has occured in the processing chains + attached to this sync; so the execution list has to be updated */ + guint samples_per_tick; /* number of bytes produced by sources of the processing chains*/ + guint32 ticks; + guint32 time; /* a time since the start of the sync expressed in milisec*/ +}; + +typedef struct _MSSync MSSync; + +typedef void (*MSSyncDestroyFunc)(MSSync*); +typedef void (*MSSyncSyncFunc)(MSSync*); +typedef int (*MSSyncAttachFunc)(MSSync*,MSFilter*); +typedef int (*MSSyncDetachFunc)(MSSync*,MSFilter*); + +typedef struct _MSSyncClass +{ + gint max_filters; /* the maximum number of filters that can be attached to this sync*/ + MSSyncSyncFunc synchronize; + MSSyncDestroyFunc destroy; + MSSyncAttachFunc attach; + MSSyncDetachFunc detach; +} MSSyncClass; + +/* private */ +void ms_sync_init(MSSync *sync); +void ms_sync_class_init(MSSyncClass *klass); + +int ms_sync_attach_generic(MSSync *sync,MSFilter *f); +int ms_sync_detach_generic(MSSync *sync,MSFilter *f); + +/* public*/ + +#define MS_SYNC(sync) ((MSSync*)(sync)) +#define MS_SYNC_CLASS(klass) ((MSSyncClass*)(klass)) + +#define ms_sync_synchronize(_sync) \ +do \ +{ \ + MSSync *__sync=_sync; \ + __sync->ticks++; \ + ((__sync)->klass->synchronize((__sync))); \ +}while(0) + +void ms_sync_setup(MSSync *sync); + +void ms_sync_unsetup(MSSync *sync); + +#define ms_sync_update(sync) (sync)->flags|=MS_SYNC_NEED_UPDATE + +#define ms_sync_get_samples_per_tick(sync) ((sync)->samples_per_tick) + +void ms_sync_set_samples_per_tick(MSSync *sync,gint size); + +#define ms_sync_get_tick_count(sync) ((sync)->ticks) + +#define ms_sync_suspend(sync) g_cond_wait((sync)->thread_cond,(sync)->lock) + +#define ms_sync_lock(sync) g_mutex_lock((sync)->lock) + +#define ms_sync_unlock(sync) g_mutex_unlock((sync)->lock) + +#define ms_sync_trylock(sync) g_mutex_trylock((sync)->lock) + +/** + * function_name:ms_sync_attach + * @sync: A #MSSync object. + * @f: A #MSFilter object. + * + * Attach a chain of filters to a synchronisation source. Filter @f must be the first filter of the processing chain. + * + * Returns: 0 if successfull, a negative value reprensenting the errno.h error. + */ +int ms_sync_attach(MSSync *sync,MSFilter *f); + +/** + * ms_sync_detach: + * @sync: A #MSSync object. + * @f: A #MSFilter object. + * + * Dettach a chain of filters to a synchronisation source. Filter @f must be the first filter of the processing chain. + * The processing chain will no more be executed. + * + * Returns: 0 if successfull, a negative value reprensenting the errno.h error. + */ +int ms_sync_detach(MSSync *sync,MSFilter *f); + +int ms_sync_uninit(MSSync *sync); + +#define ms_sync_start(sync) ms_start((sync)) +#define ms_sync_stop(sync) ms_stop((sync)) + + +/*destroy*/ +#define ms_sync_destroy(sync) (sync)->klass->destroy((sync)) + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c new file mode 100644 index 00000000..29b81d3c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c @@ -0,0 +1,114 @@ + /* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "mstimer.h" +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> + +static MSTimerClass *ms_timer_class=NULL; + + +void ms_timer_init(MSTimer *sync) +{ + ms_sync_init(MS_SYNC(sync)); + MS_SYNC(sync)->attached_filters=sync->filters; + memset(sync->filters,0,MSTIMER_MAX_FILTERS*sizeof(MSFilter*)); + MS_SYNC(sync)->samples_per_tick=160; + ms_timer_set_interval(sync,20); + sync->state=MS_TIMER_STOPPED; +} + +void ms_timer_class_init(MSTimerClass *klass) +{ + ms_sync_class_init(MS_SYNC_CLASS(klass)); + MS_SYNC_CLASS(klass)->max_filters=MSTIMER_MAX_FILTERS; + MS_SYNC_CLASS(klass)->synchronize=(MSSyncSyncFunc)ms_timer_synchronize; + MS_SYNC_CLASS(klass)->destroy=(MSSyncDestroyFunc)ms_timer_destroy; + /* no need to overload these function*/ + MS_SYNC_CLASS(klass)->attach=ms_sync_attach_generic; + MS_SYNC_CLASS(klass)->detach=ms_sync_detach_generic; +} + +void ms_timer_destroy(MSTimer *timer) +{ + g_free(timer); +} + + +void ms_timer_synchronize(MSTimer *timer) +{ + /* //printf("ticks=%i \n",MS_SYNC(timer)->ticks); */ + if (timer->state==MS_TIMER_STOPPED){ + timer->state=MS_TIMER_RUNNING; + gettimeofday(&timer->orig,NULL); + timer->sync.time=0; + } + else { + gint32 diff,time; + struct timeval tv,cur; + + gettimeofday(&cur,NULL); + time=((cur.tv_usec-timer->orig.tv_usec)/1000 ) + ((cur.tv_sec-timer->orig.tv_sec)*1000 ); + if ( (diff=time-timer->sync.time)>50){ + g_warning("Must catchup %i miliseconds.",diff); + } + while((diff = timer->sync.time-time) > 0) + { + tv.tv_sec = diff/1000; + tv.tv_usec = (diff%1000)*1000; + select(0,NULL,NULL,NULL,&tv); + gettimeofday(&cur,NULL); + time=((cur.tv_usec-timer->orig.tv_usec)/1000 ) + ((cur.tv_sec-timer->orig.tv_sec)*1000 ); + } + } + timer->sync.time+=timer->milisec; + return; +} + + +MSSync *ms_timer_new() +{ + MSTimer *timer; + + timer=g_malloc(sizeof(MSTimer)); + ms_timer_init(timer); + if (ms_timer_class==NULL) + { + ms_timer_class=g_new(MSTimerClass,1); + ms_timer_class_init(ms_timer_class); + } + MS_SYNC(timer)->klass=MS_SYNC_CLASS(ms_timer_class); + return(MS_SYNC(timer)); +} + +void ms_timer_set_interval(MSTimer *timer, int milisec) +{ + + MS_SYNC(timer)->ticks=0; + MS_SYNC(timer)->interval=milisec; + timer->interval.tv_sec=milisec/1000; + timer->interval.tv_usec=(milisec % 1000)*1000; + timer->milisec=milisec; + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h new file mode 100644 index 00000000..5c7e8ede --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h @@ -0,0 +1,68 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MSTIMER_H +#define MSTIMER_H + +#include "mssync.h" +#include <sys/time.h> + +#define MSTIMER_MAX_FILTERS 10 + +/* MSTimer derivates from MSSync base class*/ + +typedef struct _MSTimer +{ + /* the MSSync must be the first field of the object in order to the object mechanism to work*/ + MSSync sync; + MSFilter *filters[MSTIMER_MAX_FILTERS]; + gint milisec; /* the interval */ + struct timeval interval; + struct timeval orig; + gint state; +} MSTimer; + + +typedef struct _MSTimerClass +{ + /* the MSSyncClass must be the first field of the class in order to the class mechanism to work*/ + MSSyncClass parent_class; +} MSTimerClass; + + +/*private*/ +#define MS_TIMER_RUNNING 1 +#define MS_TIMER_STOPPED 0 +void ms_timer_init(MSTimer *sync); +void ms_timer_class_init(MSTimerClass *sync); + +void ms_timer_destroy(MSTimer *timer); +void ms_timer_synchronize(MSTimer *timer); + +/*public*/ +void ms_timer_set_interval(MSTimer *timer, gint milisec); + +/* casts a MSSync object into a MSTimer */ +#define MS_TIMER(sync) ((MSTimer*)(sync)) +/* casts a MSSync class into a MSTimer class */ +#define MS_TIMER_CLASS(klass) ((MSTimerClass*)(klass)) + +MSSync *ms_timer_new(); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h new file mode 100644 index 00000000..62477436 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2003 Robert W. Brewer <rbrewer at op.net> + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSTRUESPEECHDECODER_H +#define MSTRUESPEECHDECODER_H + +#include "msfilter.h" +#include "mstruespeechencoder.h" + + + +typedef struct _MSTrueSpeechDecoder +{ + /* the MSTrueSpeechDecoder derives from MSFilter, so the MSFilter + object MUST be the first of the MSTrueSpeechDecoder object + in order for the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT]; + MSFifo *f_outputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT]; + Win32Codec* codec; +} MSTrueSpeechDecoder; + +typedef struct _MSTrueSpeechDecoderClass +{ + /* the MSTrueSpeechDecoder derives from MSFilter, + so the MSFilter class MUST be the first of the MSTrueSpechDecoder + class + in order for the class mechanism to work*/ + MSFilterClass parent_class; + Win32CodecDriver* driver; +} MSTrueSpeechDecoderClass; + +/* PUBLIC */ +#define MS_TRUESPEECHDECODER(filter) ((MSTrueSpechMDecoder*)(filter)) +#define MS_TRUESPEECHDECODER_CLASS(klass) ((MSTrueSpeechDecoderClass*)(klass)) +MSFilter * ms_truespeechdecoder_new(void); + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h new file mode 100644 index 00000000..04e40bb8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2003 Robert W. Brewer <rbrewer at op.net> + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef MSTRUESPEECHENCODER_H +#define MSTRUESPEECHENCODER_H + +#include "msfilter.h" +#include <win32codec.h> + + +#define MS_TRUESPEECH_CODEC_MAX_IN_OUT 1 /* max inputs/outputs per filter*/ + +#define TRUESPEECH_FORMAT_TAG 0x22 +#define TRUESPEECH_DLL "tssoft32.acm" + +typedef struct _MSTrueSpeechEncoder +{ + /* the MSTrueSpeechEncoder derives from MSFilter, so the MSFilter + object MUST be the first of the MSTrueSpeechEncoder object + in order for the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT]; + MSFifo *f_outputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT]; + Win32Codec* codec; +} MSTrueSpeechEncoder; + +typedef struct _MSTrueSpeechEncoderClass +{ + /* the MSTrueSpeechEncoder derives from MSFilter, + so the MSFilter class MUST be the first of the MSTrueSpechEncoder + class + in order for the class mechanism to work*/ + MSFilterClass parent_class; + Win32CodecDriver* driver; +} MSTrueSpeechEncoderClass; + +/* PUBLIC */ +#define MS_TRUESPEECHENCODER(filter) ((MSTrueSpechMEncoder*)(filter)) +#define MS_TRUESPEECHENCODER_CLASS(klass) ((MSTrueSpeechEncoderClass*)(klass)) +MSFilter * ms_truespeechencoder_new(void); + +/* for internal use only */ +WAVEFORMATEX* ms_truespeechencoder_wf_create(); + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h new file mode 100644 index 00000000..012b87d8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h @@ -0,0 +1,61 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MSUTILS_H +#define MSUTILS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_GLIB +#include <glib.h> +#else +#include <uglib.h> +#endif +#include <errno.h> + +#ifndef ENODATA +/* this is for freeBSD .*/ +#define ENODATA EWOULDBLOCK +#endif + +#ifdef MS_DEBUG + +#define ms_trace g_message + +#else + +#define ms_trace(...) +#endif + +#define ms_warning g_warning +#define ms_error g_error + +#define VIDEO_SIZE_CIF_W 352 +#define VIDEO_SIZE_CIF_H 288 +#define VIDEO_SIZE_QCIF_W 176 +#define VIDEO_SIZE_QCIF_H 144 +#define VIDEO_SIZE_4CIF_W 704 +#define VIDEO_SIZE_4CIF_H 576 +#define VIDEO_SIZE_MAX_W VIDEO_SIZE_4CIF_W +#define VIDEO_SIZE_MAX_H VIDEO_SIZE_4CIF_H + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h new file mode 100644 index 00000000..e19ac9ea --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h @@ -0,0 +1,96 @@ + /* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSV4L_H +#define MSV4L_H + +#include <msvideosource.h> +#include <sys/types.h> +#include <linux/videodev.h> + +struct _MSV4l +{ + MSVideoSource parent; + int fd; + char *device; + struct video_capability cap; + struct video_channel channel; + struct video_window win; + struct video_picture pict; + struct video_mmap vmap; + struct video_mbuf vmbuf; + struct video_capture vcap; + gint bsize; + gint use_mmap; + gint frame; + guint query_frame; + gchar *mmapdbuf; /* the mmap'd buffer */ + MSBuffer img[VIDEO_MAX_FRAME]; /* the buffer wrappers used for mmaps */ + gint width; /* the capture image size - can be cropped to output size */ + gint height; + MSBuffer *allocdbuf; /* the buffer allocated for read() and mire */ + gint count; + MSBuffer *image_grabbed; + GCond *cond; + GCond *stopcond; + GThread *v4lthread; + gboolean grab_image; + gboolean thread_run; + gboolean thread_exited; +}; + +typedef struct _MSV4l MSV4l; + + +struct _MSV4lClass +{ + MSVideoSourceClass parent_class; + +}; + +typedef struct _MSV4lClass MSV4lClass; + + +/* PUBLIC API */ +#define MS_V4L(v) ((MSV4l*)(v)) +#define MS_V4L_CLASS(k) ((MSV4lClass*)(k)) +MSFilter * ms_v4l_new(); + +void ms_v4l_start(MSV4l *obj); +void ms_v4l_stop(MSV4l *obj); +int ms_v4l_set_device(MSV4l *f, const gchar *device); +gint ms_v4l_get_width(MSV4l *v4l); +gint ms_v4l_get_height(MSV4l *v4l); +void ms_v4l_set_size(MSV4l *v4l, gint w, gint h); + +/* PRIVATE API */ +void ms_v4l_init(MSV4l *obj); +void ms_v4l_class_init(MSV4lClass *klass); +int v4l_configure(MSV4l *f); + +void v4l_process(MSV4l *obj); + +void ms_v4l_uninit(MSV4l *obj); + +void ms_v4l_destroy(MSV4l *obj); + +extern MSFilterInfo v4l_info; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h new file mode 100644 index 00000000..9a27f836 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h @@ -0,0 +1,74 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSVIDEOSOURCE_H +#define MSVIDEOSOURCE_H + + +#include "msfilter.h" + +/* this is the video input abstract class */ + +#define MSVIDEOSOURCE_MAX_OUTPUTS 1 /* max output per filter*/ + +typedef struct _MSVideoSource +{ + /* the MSVideoSource derivates from MSFilter, so the MSFilter object MUST be the first of the MSVideoSource object + in order to the object mechanism to work*/ + MSFilter filter; + MSQueue *outputs[MSVIDEOSOURCE_MAX_OUTPUTS]; + gchar *dev_name; + gint width, height; + gchar *format; + gint frame_rate; + gint frame_rate_base; +} MSVideoSource; + +typedef struct _MSVideoSourceClass +{ + /* the MSVideoSource derivates from MSFilter, so the MSFilter class MUST be the first of the MSVideoSource class + in order to the class mechanism to work*/ + MSFilterClass parent_class; + gint (*set_device)(MSVideoSource *s, const gchar *name); + void (*start)(MSVideoSource *s); + void (*stop)(MSVideoSource *s); + void (*set_size)(MSVideoSource *s, gint width, gint height); + void (*set_frame_rate)(MSVideoSource *s, gint frame_rate, gint frame_rate_base); +} MSVideoSourceClass; + +/* PUBLIC */ +void ms_video_source_register_all(); +int ms_video_source_set_device(MSVideoSource *f, const gchar *device); +gchar* ms_video_source_get_device_name(MSVideoSource *f); +void ms_video_source_start(MSVideoSource *f); +void ms_video_source_stop(MSVideoSource *f); +void ms_video_source_set_size(MSVideoSource *f, gint width, gint height); +void ms_video_source_set_frame_rate(MSVideoSource *f, gint frame_rate, gint frame_rate_base); +gchar* ms_video_source_get_format(MSVideoSource *f); + +#define MS_VIDEO_SOURCE(obj) ((MSVideoSource*)(obj)) +#define MS_VIDEO_SOURCE_CLASS(klass) ((MSVideoSourceClass*)(klass)) + + +/* FOR INTERNAL USE*/ +void ms_video_source_init(MSVideoSource *f); +void ms_video_source_class_init(MSVideoSourceClass *klass); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c new file mode 100644 index 00000000..178e294c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c @@ -0,0 +1,121 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "mswrite.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> + +static MSWriteClass *ms_write_class=NULL; + +MSFilter * ms_write_new(char *name) +{ + MSWrite *r; + int fd=-1; + + r=g_new(MSWrite,1); + ms_write_init(r); + if (ms_write_class==NULL) + { + ms_write_class=g_new(MSWriteClass,1); + ms_write_class_init(ms_write_class); + } + MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_write_class); + if ((name!=NULL) && (strlen(name)!=0)) + { + fd=open(name,O_WRONLY | O_CREAT | O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (fd<0) g_error("ms_write_new: failed to open %s.\n",name); + } + r->fd=fd; + return(MS_FILTER(r)); +} + + +/* FOR INTERNAL USE*/ +void ms_write_init(MSWrite *r) +{ + ms_filter_init(MS_FILTER(r)); + MS_FILTER(r)->infifos=r->f_inputs; + MS_FILTER(r)->inqueues=r->q_inputs; + MS_FILTER(r)->r_mingran=MSWRITE_MIN_GRAN; + memset(r->f_inputs,0,sizeof(MSFifo*)*MSWRITE_MAX_INPUTS); + memset(r->q_inputs,0,sizeof(MSQueue*)*MSWRITE_MAX_INPUTS); + r->fd=-1; +} + +void ms_write_class_init(MSWriteClass *klass) +{ + ms_filter_class_init(MS_FILTER_CLASS(klass)); + ms_filter_class_set_name(MS_FILTER_CLASS(klass),"dskwriter"); + MS_FILTER_CLASS(klass)->max_finputs=MSWRITE_MAX_INPUTS; + MS_FILTER_CLASS(klass)->max_qinputs=MSWRITE_MAX_INPUTS; + MS_FILTER_CLASS(klass)->r_maxgran=MSWRITE_DEF_GRAN; + MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_write_destroy; + MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_write_process; +} + +void ms_write_process(MSWrite *r) +{ + MSFifo *f; + MSQueue *q; + MSMessage *buf=NULL; + int i,j,err1,err2; + gint gran=ms_filter_get_mingran(MS_FILTER(r)); + void *p; + + /* process output fifos*/ + for (i=0,j=0;(i<MS_FILTER(r)->klass->max_finputs)&&(j<MS_FILTER(r)->finputs);i++) + { + f=r->f_inputs[i]; + if (f!=NULL) + { + if ( (err1=ms_fifo_get_read_ptr(f,gran,&p))>0 ) + { + + err2=write(r->fd,p,gran); + if (err2<0) g_warning("ms_write_process: failed to write: %s.\n",strerror(errno)); + } + j++; + } + } + /* process output queues*/ + for (i=0,j=0;(i<MS_FILTER(r)->klass->max_qinputs)&&(j<MS_FILTER(r)->qinputs);i++) + { + q=r->q_inputs[i]; + if (q!=NULL) + { + while ( (buf=ms_queue_get(q))!=NULL ){ + write(r->fd,buf->data,buf->size); + j++; + ms_message_destroy(buf); + } + } + } +} + +void ms_write_destroy( MSWrite *obj) +{ + if (obj->fd!=0) close(obj->fd); + g_free(obj); +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h new file mode 100644 index 00000000..cd766d10 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h @@ -0,0 +1,63 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MSWRITE_H +#define MSWRITE_H + +#include "msfilter.h" + + +/*this is the class that implements writing reading sink filter*/ + +#define MSWRITE_MAX_INPUTS 1 /* max output per filter*/ + +#define MSWRITE_DEF_GRAN 512 /* the default granularity*/ +#define MSWRITE_MIN_GRAN 64 + +typedef struct _MSWrite +{ + /* the MSWrite derivates from MSFilter, so the MSFilter object MUST be the first of the MSWrite object + in order to the object mechanism to work*/ + MSFilter filter; + MSFifo *f_inputs[MSWRITE_MAX_INPUTS]; + MSQueue *q_inputs[MSWRITE_MAX_INPUTS]; + gint fd; /* the file descriptor of the file being written*/ +} MSWrite; + +typedef struct _MSWriteClass +{ + /* the MSWrite derivates from MSFilter, so the MSFilter class MUST be the first of the MSWrite class + in order to the class mechanism to work*/ + MSFilterClass parent_class; +} MSWriteClass; + +/* PUBLIC */ +#define MS_WRITE(filter) ((MSWrite*)(filter)) +#define MS_WRITE_CLASS(klass) ((MSWriteClass*)(klass)) +MSFilter * ms_write_new(char *name); + +/* FOR INTERNAL USE*/ +void ms_write_init(MSWrite *r); +void ms_write_class_init(MSWriteClass *klass); +void ms_write_destroy( MSWrite *obj); +void ms_write_process(MSWrite *r); + +#endif + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c new file mode 100644 index 00000000..636c5792 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c @@ -0,0 +1,495 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "osscard.h" + +#include "msossread.h" +#include "msosswrite.h" + +#ifdef HAVE_SYS_SOUNDCARD_H +#include <sys/soundcard.h> + +#include <errno.h> +#include <fcntl.h> +#include <sys/time.h> + +#if 0 +void * oss_thread(OssCard *obj) +{ + gint i; + gint err; + g_message("oss_thread: starting **********"); + while(1){ + for(i=0;i<OSS_CARD_BUFFERS;i++){ + g_mutex_lock(obj->lock); + if (obj->ref==0){ + g_cond_signal(obj->cond); + g_mutex_unlock(obj->lock); + g_thread_exit(NULL); + } + g_mutex_unlock(obj->lock); + obj->readindex=i; + + err=read(obj->fd,obj->readbuf[i],SND_CARD(obj)->bsize); + if (err<0) g_warning("oss_thread: read() error:%s.",strerror(errno)); + obj->writeindex=i; + write(obj->fd,obj->writebuf[i],SND_CARD(obj)->bsize); + memset(obj->writebuf[i],0,SND_CARD(obj)->bsize); + } + } +} +#endif +int oss_open(OssCard *obj, int bits,int stereo, int rate) +{ + int fd; + int p=0,cond=0; + int i=0; + int min_size=0,blocksize=512; + int err; + + //g_message("opening sound device"); + fd=open(obj->dev_name,O_RDWR|O_NONBLOCK); + if (fd<0) return -EWOULDBLOCK; + /* unset nonblocking mode */ + /* We wanted non blocking open but now put it back to normal ; thanks Xine !*/ + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)&~O_NONBLOCK); + + /* reset is maybe not needed but takes time*/ + /*ioctl(fd, SNDCTL_DSP_RESET, 0); */ + + +#ifdef WORDS_BIGENDIAN + p=AFMT_U16_BE; +#else + p=AFMT_U16_LE; +#endif + + err=ioctl(fd,SNDCTL_DSP_SETFMT,&p); + if (err<0){ + g_warning("oss_open: can't set sample format:%s.",strerror(errno)); + } + + + p = bits; /* 16 bits */ + err=ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p); + if (err<0){ + g_warning("oss_open: can't set sample size to %i:%s.",bits,strerror(errno)); + } + + p = rate; /* rate in khz*/ + err=ioctl(fd, SNDCTL_DSP_SPEED, &p); + if (err<0){ + g_warning("oss_open: can't set sample rate to %i:%s.",rate,strerror(errno)); + } + + p = stereo; /* stereo or not */ + err=ioctl(fd, SNDCTL_DSP_STEREO, &p); + if (err<0){ + g_warning("oss_open: can't set mono/stereo mode:%s.",strerror(errno)); + } + + if (rate==16000) blocksize=4096; /* oss emulation is not very good at 16khz */ + else blocksize=blocksize*(rate/8000); + ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); + + /* try to subdivide BLKSIZE to reach blocksize if necessary */ + if (min_size>blocksize) + { + cond=1; + p=min_size/blocksize; + while(cond) + { + i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p); + //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno); + if ((i==0) || (p==1)) cond=0; + else p=p/2; + } + } + ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); + if (min_size>blocksize) + { + g_warning("dsp block size set to %i.",min_size); + }else{ + /* no need to access the card with less latency than needed*/ + min_size=blocksize; + } + + g_message("dsp blocksize is %i.",min_size); + + /* start recording !!! Alex */ + { + int fl,res; + + fl=PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT; + res=ioctl(fd, SNDCTL_DSP_SETTRIGGER, &fl); + if (res<0) g_warning("OSS_TRIGGER: %s",strerror(errno)); + } + + obj->fd=fd; + obj->readpos=0; + obj->writepos=0; + SND_CARD(obj)->bits=bits; + SND_CARD(obj)->stereo=stereo; + SND_CARD(obj)->rate=rate; + SND_CARD(obj)->bsize=min_size; + return fd; +} + +int oss_card_probe(OssCard *obj,int bits,int stereo,int rate) +{ + + int fd; + int p=0,cond=0; + int i=0; + int min_size=0,blocksize=512; + + if (obj->fd>0) return SND_CARD(obj)->bsize; + fd=open(obj->dev_name,O_RDWR|O_NONBLOCK); + if (fd<0) { + g_warning("oss_card_probe: can't open %s: %s.",obj->dev_name,strerror(errno)); + return -1; + } + ioctl(fd, SNDCTL_DSP_RESET, 0); + + p = bits; /* 16 bits */ + ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p); + + p = stereo; /* number of channels */ + ioctl(fd, SNDCTL_DSP_CHANNELS, &p); + + p = rate; /* rate in khz*/ + ioctl(fd, SNDCTL_DSP_SPEED, &p); + + ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); + + /* try to subdivide BLKSIZE to reach blocksize if necessary */ + if (min_size>blocksize) + { + cond=1; + p=min_size/blocksize; + while(cond) + { + i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p); + //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno); + if ((i==0) || (p==1)) cond=0; + else p=p/2; + } + } + ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); + if (min_size>blocksize) + { + g_warning("dsp block size set to %i.",min_size); + }else{ + /* no need to access the card with less latency than needed*/ + min_size=blocksize; + } + close(fd); + return min_size; +} + + +int oss_card_open(OssCard *obj,int bits,int stereo,int rate) +{ + int fd; + obj->ref++; + if (obj->fd==0){ + fd=oss_open(obj,bits,stereo,rate); + if (fd<0) { + obj->fd=0; + obj->ref--; + return -1; + } + } + + obj->readbuf=g_malloc0(SND_CARD(obj)->bsize); + obj->writebuf=g_malloc0(SND_CARD(obj)->bsize); + + SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; + return 0; +} + +void oss_card_close(OssCard *obj) +{ + int i; + obj->ref--; + if (obj->ref==0) { + close(obj->fd); + obj->fd=0; + SND_CARD(obj)->flags&=~SND_CARD_FLAGS_OPENED; + g_free(obj->readbuf); + obj->readbuf=NULL; + g_free(obj->writebuf); + obj->writebuf=NULL; + + } +} + +void oss_card_destroy(OssCard *obj) +{ + snd_card_uninit(SND_CARD(obj)); + g_free(obj->dev_name); + g_free(obj->mixdev_name); + if (obj->readbuf!=NULL) g_free(obj->readbuf); + if (obj->writebuf!=NULL) g_free(obj->writebuf); +} + +gboolean oss_card_can_read(OssCard *obj) +{ + struct timeval tout={0,0}; + int err; + fd_set fdset; + if (obj->readpos!=0) return TRUE; + FD_ZERO(&fdset); + FD_SET(obj->fd,&fdset); + err=select(obj->fd+1,&fdset,NULL,NULL,&tout); + if (err>0) return TRUE; + else return FALSE; +} + +int oss_card_read(OssCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + if (size<bsize){ + gint canread=MIN(bsize-obj->readpos,size); + if (obj->readpos==0){ + err=read(obj->fd,obj->readbuf,bsize); + if (err<0) { + g_warning("oss_card_read: read() failed:%s.",strerror(errno)); + return -1; + } + } + + memcpy(buf,&obj->readbuf[obj->readpos],canread); + obj->readpos+=canread; + if (obj->readpos>=bsize) obj->readpos=0; + return canread; + }else{ + err=read(obj->fd,buf,size); + if (err<0) { + g_warning("oss_card_read: read-2() failed:%s.",strerror(errno)); + } + return err; + } + +} + +int oss_card_write(OssCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + + if (size<bsize){ + gint canwrite; + canwrite=MIN(bsize-obj->writepos,size); + memcpy(&obj->writebuf[obj->writepos],buf,canwrite); + obj->writepos+=canwrite; + if (obj->writepos>=bsize){ + err=write(obj->fd,obj->writebuf,bsize); + obj->writepos=0; + } + return canwrite; + }else{ + return write(obj->fd,buf,bsize); + } +} + +void oss_card_set_level(OssCard *obj,gint way,gint a) +{ + int p,mix_fd; + int osscmd; + g_return_if_fail(obj->mixdev_name!=NULL); +#ifdef HAVE_SYS_SOUNDCARD_H + switch(way){ + case SND_CARD_LEVEL_GENERAL: + osscmd=SOUND_MIXER_VOLUME; + break; + case SND_CARD_LEVEL_INPUT: + osscmd=SOUND_MIXER_IGAIN; + break; + case SND_CARD_LEVEL_OUTPUT: + osscmd=SOUND_MIXER_PCM; + break; + default: + g_warning("oss_card_set_level: unsupported command."); + return; + } + p=(((int)a)<<8 | (int)a); + mix_fd = open(obj->mixdev_name, O_WRONLY); + ioctl(mix_fd,MIXER_WRITE(osscmd), &p); + close(mix_fd); +#endif +} + +gint oss_card_get_level(OssCard *obj,gint way) +{ + int p=0,mix_fd; + int osscmd; + g_return_if_fail(obj->mixdev_name!=NULL); +#ifdef HAVE_SYS_SOUNDCARD_H + switch(way){ + case SND_CARD_LEVEL_GENERAL: + osscmd=SOUND_MIXER_VOLUME; + break; + case SND_CARD_LEVEL_INPUT: + osscmd=SOUND_MIXER_IGAIN; + break; + case SND_CARD_LEVEL_OUTPUT: + osscmd=SOUND_MIXER_PCM; + break; + default: + g_warning("oss_card_get_level: unsupported command."); + return -1; + } + mix_fd = open(obj->mixdev_name, O_RDONLY); + ioctl(mix_fd,MIXER_READ(SOUND_MIXER_VOLUME), &p); + close(mix_fd); +#endif + return p>>8; +} + +void oss_card_set_source(OssCard *obj,int source) +{ + gint p=0; + gint mix_fd; + g_return_if_fail(obj->mixdev_name!=NULL); +#ifdef HAVE_SYS_SOUNDCARD_H + if (source == 'c') + p = 1 << SOUND_MIXER_CD; + if (source == 'l') + p = 1 << SOUND_MIXER_LINE; + if (source == 'm') + p = 1 << SOUND_MIXER_MIC; + + + mix_fd = open(obj->mixdev_name, O_WRONLY); + ioctl(mix_fd, SOUND_MIXER_WRITE_RECSRC, &p); + close(mix_fd); +#endif +} + +MSFilter *oss_card_create_read_filter(OssCard *card) +{ + MSFilter *f=ms_oss_read_new(); + ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); + return f; +} + +MSFilter *oss_card_create_write_filter(OssCard *card) +{ + MSFilter *f=ms_oss_write_new(); + ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); + return f; +} + + +SndCard * oss_card_new(char *devname, char *mixdev_name) +{ + OssCard * obj= g_new0(OssCard,1); + SndCard *base= SND_CARD(obj); + snd_card_init(base); + obj->dev_name=g_strdup(devname); + obj->mixdev_name=g_strdup( mixdev_name); +#ifdef HAVE_GLIB + base->card_name=g_strdup_printf("%s (Open Sound System)",devname); +#else + base->card_name=malloc(100); + snprintf(base->card_name, 100, "%s (Open Sound System)",devname); +#endif + base->_probe=(SndCardOpenFunc)oss_card_probe; + base->_open_r=(SndCardOpenFunc)oss_card_open; + base->_open_w=(SndCardOpenFunc)oss_card_open; + base->_can_read=(SndCardPollFunc)oss_card_can_read; + base->_read=(SndCardIOFunc)oss_card_read; + base->_write=(SndCardIOFunc)oss_card_write; + base->_close_r=(SndCardCloseFunc)oss_card_close; + base->_close_w=(SndCardCloseFunc)oss_card_close; + base->_set_rec_source=(SndCardMixerSetRecSourceFunc)oss_card_set_source; + base->_set_level=(SndCardMixerSetLevelFunc)oss_card_set_level; + base->_get_level=(SndCardMixerGetLevelFunc)oss_card_get_level; + base->_destroy=(SndCardDestroyFunc)oss_card_destroy; + base->_create_read_filter=(SndCardCreateFilterFunc)oss_card_create_read_filter; + base->_create_write_filter=(SndCardCreateFilterFunc)oss_card_create_write_filter; + return base; +} + +#define DSP_NAME "/dev/dsp" +#define MIXER_NAME "/dev/mixer" + +gint oss_card_manager_init(SndCardManager *manager, gint tabindex) +{ + gchar *devname; + gchar *mixername; + gint devindex=0; + gint found=0; + + /* search for /dev/dsp and /dev/mixer */ +#ifdef HAVE_GLIB + if (g_file_test(DSP_NAME,G_FILE_TEST_EXISTS)){ + tabindex++; + devindex++; + manager->cards[0]=oss_card_new(DSP_NAME,MIXER_NAME); + manager->cards[0]->index=0; + found++; + g_message("Found /dev/dsp."); + } + for (;tabindex<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){ + devname=g_strdup_printf("%s%i",DSP_NAME,devindex); + mixername=g_strdup_printf("%s%i",MIXER_NAME,devindex); + if (g_file_test(devname,G_FILE_TEST_EXISTS)){ + manager->cards[tabindex]=oss_card_new(devname,mixername); + manager->cards[tabindex]->index=tabindex; + tabindex++; + found++; + } + g_free(devname); + g_free(mixername); + } +#else + if (access(DSP_NAME,F_OK)==0){ + tabindex++; + devindex++; + manager->cards[0]=oss_card_new(DSP_NAME,MIXER_NAME); + manager->cards[0]->index=0; + found++; + g_message("Found /dev/dsp."); + } + for (;tabindex<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){ + devname=malloc(100); + snprintf(devname, 100, "%s%i",DSP_NAME,devindex); + mixername=malloc(100); + snprintf(mixername, 100, "%s%i",MIXER_NAME,devindex); + + if (access(devname,F_OK)==0){ + manager->cards[tabindex]=oss_card_new(devname,mixername); + manager->cards[tabindex]->index=tabindex; + tabindex++; + found++; + } + g_free(devname); + g_free(mixername); + } +#endif + if (tabindex==0) g_warning("No sound cards found !"); + return found; +} + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h new file mode 100644 index 00000000..30b96c23 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h @@ -0,0 +1,47 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* An implementation of SndCard : the OssCard */ + +#ifndef OSS_CARD_H +#define OSS_CARD_H + +#include "sndcard.h" + +#define OSS_CARD_BUFFERS 3 +struct _OssCard +{ + SndCard parent; + gchar *dev_name; /* /dev/dsp0 for example */ + gchar *mixdev_name; /* /dev/mixer0 for example */ + gint fd; /* the file descriptor of the open soundcard, 0 if not open*/ + gint ref; + gchar *readbuf; + gint readpos; + gchar *writebuf; + gint writepos; +}; + +typedef struct _OssCard OssCard; + +SndCard * oss_card_new(char *devname, char *mixdev_name); + +typedef OssCard HpuxSndCard; + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c new file mode 100644 index 00000000..9570b905 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c @@ -0,0 +1,315 @@ +/* + Copyright (C) 2005 Remko Troncon + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <stdio.h> +#include <portaudio.h> +#include <stdlib.h> + +#include "portaudiocard.h" +#include "msossread.h" +#include "msosswrite.h" + +// Settings +#define BUFFER_SIZE 2048 + +// PortAudio settings +#define FRAMES_PER_BUFFER 256 + + +// ----------------------------------------------------------------------------- + +int readBuffer(char* buffer, char** buffer_read_p, char*buffer_write, char* buffer_end, char* target_buffer, int target_len) +{ + char *end, *tmp, *buffer_read = *buffer_read_p; + size_t remaining, len; + int read = 0; + + // First phase + tmp = buffer_read + target_len; + if (buffer_write < buffer_read) { + if (tmp > buffer_end) { + end = buffer_end; + remaining = tmp - buffer_end; + } + else { + end = tmp; + remaining = 0; + } + } + else { + end = (tmp >= buffer_write ? buffer_write : tmp); + remaining = 0; + } + //printf("end: %p\n",end); + + // Copy the data + len = end - buffer_read; + memcpy(target_buffer, buffer_read, len); + buffer_read += len; + target_buffer += len; + read += len; + + // Second phase + if (remaining > 0) { + buffer_read = buffer; + tmp = buffer_read + remaining; + len = (tmp > buffer_write ? buffer_write : tmp) - buffer_read; + memcpy(target_buffer, buffer_read, len); + buffer_read += len; + read += len; + } + + // Finish up + *buffer_read_p = buffer_read; + + return read; +} + +int writeBuffer(char* buffer, char* buffer_read, char** buffer_write_p, char* buffer_end, char* source_buffer, int source_len) +{ + char *end, *tmp, *buffer_write = *buffer_write_p; + size_t remaining, len; + int written = 0; + + // First phase + tmp = buffer_write + source_len; + if (buffer_write >= buffer_read) { + if (tmp > buffer_end) { + end = buffer_end; + remaining = tmp - buffer_end; + } + else { + end = tmp; + remaining = 0; + } + } + else { + if (tmp > buffer_read) { + printf("Warning: Dropping frame(s) %p %p\n", tmp, buffer_read); + end = buffer_read; + remaining = 0; + } + else { + end = tmp; + remaining = 0; + } + } + + len = end - buffer_write; + memcpy(buffer_write, source_buffer, len); + buffer_write += len; + source_buffer += len; + written += len; + + // Second phase + if (remaining > 0) { + buffer_write = buffer; + tmp = buffer_write + remaining; + if (tmp > buffer_read) { + printf("Warning: Dropping frame(s) %p %p\n", tmp, buffer_read); + end = buffer_read; + } + else { + end = tmp; + } + + len = end - buffer_write; + memcpy(buffer_write, source_buffer, len); + buffer_write += len; + written += len; + } + + // Finish up + *buffer_write_p = buffer_write; + return written; +} + +// ----------------------------------------------------------------------------- + +static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *card_p ) +{ + PortAudioCard* card = (PortAudioCard*) card_p; + + size_t len = framesPerBuffer * Pa_GetSampleSize(paInt16); + //printf("PA::readBuffer begin %p %p %p %p %d\n",card->out_buffer,card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, len); + readBuffer(card->out_buffer,&card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, outputBuffer, len); + //printf("PA::readBuffer end %p %p %p %p %d\n",card->out_buffer,card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, len); + writeBuffer(card->in_buffer,card->in_buffer_read,&card->in_buffer_write,card->in_buffer_end, inputBuffer, len); + return 0; +} + +// ----------------------------------------------------------------------------- + +int portaudio_card_probe(PortAudioCard *obj, int bits, int stereo, int rate) +{ + return FRAMES_PER_BUFFER * (SND_CARD(obj)->stereo ? 2 : 1) * Pa_GetSampleSize(paInt16); +} + + +int portaudio_card_open_r(PortAudioCard *obj,int bits,int stereo,int rate) +{ + fprintf(stderr,"Opening PortAudio card\n"); + + int err; + err = Pa_OpenDefaultStream(&obj->stream, 1, 1, paInt16, rate, FRAMES_PER_BUFFER, 0, portAudioCallback, obj); + if (err != paNoError) { + fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err)); + return -1; + } + + err = Pa_StartStream(obj->stream); + if (err != paNoError) { + fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err)); + Pa_CloseStream(obj->stream); + obj->stream = NULL; + return -1; + } + + SND_CARD(obj)->bits = 16; + SND_CARD(obj)->stereo = 0; + SND_CARD(obj)->rate = rate; + // Should this be multiplied by Pa_GetMinNumBuffers(FRAMES_PER_BUFFER,sampleRate) ? + SND_CARD(obj)->bsize = FRAMES_PER_BUFFER * (SND_CARD(obj)->stereo ? 2 : 1) * Pa_GetSampleSize(paInt16); + + return 0; + +} + +void portaudio_card_close_r(PortAudioCard *obj) +{ + fprintf(stderr, "Closing PortAudio card\n"); + if (obj->stream) { + Pa_StopStream(obj->stream); + Pa_CloseStream(obj->stream); + obj->stream = NULL; + } +} + +int portaudio_card_open_w(PortAudioCard *obj,int bits,int stereo,int rate) +{ +} + +void portaudio_card_close_w(PortAudioCard *obj) +{ +} + +void portaudio_card_destroy(PortAudioCard *obj) +{ + snd_card_uninit(SND_CARD(obj)); + free(obj->in_buffer); + free(obj->out_buffer); +} + +gboolean portaudio_card_can_read(PortAudioCard *obj) +{ + return obj->in_buffer_read != obj->in_buffer_write; +} + +int portaudio_card_read(PortAudioCard *obj,char *buf,int size) +{ + //printf("read begin %p %p %p %p %d\n",obj->in_buffer,obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, size); + return readBuffer(obj->in_buffer,&obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, buf, size); + //printf("read end %p %p %p %p %d\n",obj->in_buffer,obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, size); +} + +int portaudio_card_write(PortAudioCard *obj,char *buf,int size) +{ + //printf("writeBuffer begin %p %p %p %p %d\n",obj->out_buffer,obj->out_buffer_read,obj->out_buffer_write,obj->out_buffer_end, size); + return writeBuffer(obj->out_buffer,obj->out_buffer_read,&obj->out_buffer_write,obj->out_buffer_end, buf, size); + //printf("writeBuffer end %p %p %p %p %d\n",obj->out_buffer,obj->out_buffer_read,obj->out_buffer_write,obj->out_buffer_end, size); +} + +void portaudio_card_set_level(PortAudioCard *obj,gint way,gint a) +{ +} + +gint portaudio_card_get_level(PortAudioCard *obj,gint way) +{ + return 0; +} + +void portaudio_card_set_source(PortAudioCard *obj,int source) +{ +} + +MSFilter *portaudio_card_create_read_filter(PortAudioCard *card) +{ + MSFilter *f=ms_oss_read_new(); + ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); + return f; +} + +MSFilter *portaudio_card_create_write_filter(PortAudioCard *card) +{ + MSFilter *f=ms_oss_write_new(); + ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); + return f; +} + + +SndCard* portaudio_card_new() +{ + // Basic stuff + PortAudioCard* obj= g_new0(PortAudioCard,1); + SndCard* base= SND_CARD(obj); + snd_card_init(base); + base->card_name=g_strdup_printf("PortAudio Card"); + base->_probe=(SndCardOpenFunc)portaudio_card_probe; + base->_open_r=(SndCardOpenFunc)portaudio_card_open_r; + base->_open_w=(SndCardOpenFunc)portaudio_card_open_w; + base->_can_read=(SndCardPollFunc)portaudio_card_can_read; + base->_read=(SndCardIOFunc)portaudio_card_read; + base->_write=(SndCardIOFunc)portaudio_card_write; + base->_close_r=(SndCardCloseFunc)portaudio_card_close_r; + base->_close_w=(SndCardCloseFunc)portaudio_card_close_w; + base->_set_rec_source=(SndCardMixerSetRecSourceFunc)portaudio_card_set_source; + base->_set_level=(SndCardMixerSetLevelFunc)portaudio_card_set_level; + base->_get_level=(SndCardMixerGetLevelFunc)portaudio_card_get_level; + base->_destroy=(SndCardDestroyFunc)portaudio_card_destroy; + base->_create_read_filter=(SndCardCreateFilterFunc)portaudio_card_create_read_filter; + base->_create_write_filter=(SndCardCreateFilterFunc)portaudio_card_create_write_filter; + + // Initialize stream + obj->stream = NULL; + + // Initialize buffers + obj->out_buffer = (char*) malloc(sizeof(char)*BUFFER_SIZE); + obj->out_buffer_read = obj->out_buffer_write = obj->out_buffer; + obj->out_buffer_end = obj->out_buffer + BUFFER_SIZE; + obj->in_buffer = (char*) malloc(sizeof(char)*BUFFER_SIZE); + obj->in_buffer_read = obj->in_buffer_write = obj->in_buffer; + obj->in_buffer_end = obj->in_buffer + BUFFER_SIZE; + + return base; +} + +gint portaudio_card_manager_init(SndCardManager *manager, gint tabindex) +{ + // Initialize portaudio lib + int err = Pa_Initialize(); + if (err != paNoError) { + fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err)); + return 0; + } + + // Create new card + manager->cards[0]=portaudio_card_new(); + manager->cards[0]->index=0; + + return 1; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h new file mode 100644 index 00000000..cbaa7982 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2005 Remko Troncon + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* An implementation of SndCard : the OssCard */ + +#ifndef PORTAUDIO_CARD_H +#define PORTAUDIO_CARD_H + +#include "sndcard.h" + +typedef struct _PortAudioCard +{ + SndCard parent; + PortAudioStream* stream; + char *out_buffer, *out_buffer_read, *out_buffer_write, *out_buffer_end; + char *in_buffer, *in_buffer_read, *in_buffer_write, *in_buffer_end; +} PortAudioCard; + +gint portaudio_card_manager_init(SndCardManager *manager, gint tabindex); + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c new file mode 100644 index 00000000..3a0f5d9a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c @@ -0,0 +1,209 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "sndcard.h" +#include "msfilter.h" + +void snd_card_init(SndCard *obj) +{ + memset(obj,0,sizeof(SndCard)); +} + +void snd_card_uninit(SndCard *obj) +{ + if (obj->card_name!=NULL) g_free(obj->card_name); +} + +const gchar *snd_card_get_identifier(SndCard *obj) +{ + return obj->card_name; +} + +int snd_card_open_r(SndCard *obj, int bits, int stereo, int rate) +{ + g_return_val_if_fail(obj->_open_r!=NULL,-1); + g_message("Opening sound card [%s] in capture mode with stereo=%i,rate=%i,bits=%i",obj->card_name,stereo,rate,bits); + return obj->_open_r(obj,bits,stereo,rate); +} +int snd_card_open_w(SndCard *obj, int bits, int stereo, int rate) +{ + g_return_val_if_fail(obj->_open_w!=NULL,-1); + g_message("Opening sound card [%s] in playback mode with stereo=%i,rate=%i,bits=%i",obj->card_name,stereo,rate,bits); + return obj->_open_w(obj,bits,stereo,rate); +} + +gboolean snd_card_can_read(SndCard *obj){ + g_return_val_if_fail(obj->_can_read!=NULL,-1); + return obj->_can_read(obj); +} + +void snd_card_set_blocking_mode(SndCard *obj,gboolean yesno){ + g_return_if_fail(obj->_set_blocking_mode!=NULL); + obj->_set_blocking_mode(obj,yesno); +} + +int snd_card_read(SndCard *obj,char *buffer,int size) +{ + g_return_val_if_fail(obj->_read!=NULL,-1); + return obj->_read(obj,buffer,size); +} +int snd_card_write(SndCard *obj,char *buffer,int size) +{ + g_return_val_if_fail(obj->_write!=NULL,-1); + return obj->_write(obj,buffer,size); +} + +int snd_card_get_bsize(SndCard *obj) +{ + if (obj->flags & SND_CARD_FLAGS_OPENED){ + return obj->bsize; + } + return -1; +} + +void snd_card_close_r(SndCard *obj) +{ + g_return_if_fail(obj->_close_r!=NULL); + g_message("Closing reading channel of soundcard."); + obj->_close_r(obj); +} + +void snd_card_close_w(SndCard *obj) +{ + g_return_if_fail(obj->_close_w!=NULL); + g_message("Closing writing channel of soundcard."); + obj->_close_w(obj); +} + +gint snd_card_probe(SndCard *obj,int bits, int stereo, int rate) +{ + g_return_val_if_fail(obj->_probe!=NULL,-1); + return obj->_probe(obj,bits,stereo,rate); +} + +void snd_card_set_rec_source(SndCard *obj, int source) +{ + g_return_if_fail(obj->_set_rec_source!=NULL); + obj->_set_rec_source(obj,source); +} + +void snd_card_set_level(SndCard *obj, int way, int level) +{ + g_return_if_fail(obj->_set_level!=NULL); + obj->_set_level(obj,way,level); +} + +gint snd_card_get_level(SndCard *obj,int way) +{ + g_return_val_if_fail(obj->_get_level!=NULL,-1); + return obj->_get_level(obj,way); +} + + +MSFilter * snd_card_create_read_filter(SndCard *obj) +{ + g_return_val_if_fail(obj->_create_read_filter!=NULL,NULL); + return obj->_create_read_filter(obj); +} +MSFilter * snd_card_create_write_filter(SndCard *obj) +{ + g_return_val_if_fail(obj->_create_write_filter!=NULL,NULL); + return obj->_create_write_filter(obj); +} + + +#ifdef HAVE_SYS_AUDIO_H +gint sys_audio_manager_init(SndCardManager *manager, gint index) +{ + /* this is a quick shortcut, as multiple soundcards on HPUX does not happen + very often... */ + manager->cards[index]=hpux_snd_card_new("/dev/audio","/dev/audio"); + return 1; +} + +#endif + +#include "osscard.h" +#include "alsacard.h" +#include "jackcard.h" + +void snd_card_manager_init(SndCardManager *manager) +{ + gint index=0; + gint tmp=0; + memset(manager,0,sizeof(SndCardManager)); + #ifdef HAVE_SYS_SOUNDCARD_H + tmp=oss_card_manager_init(manager,index); + index+=tmp; + if (index>=MAX_SND_CARDS) return; + #endif + #ifdef __ALSA_ENABLED__ + tmp=alsa_card_manager_init(manager,index); + index+=tmp; + if (index>=MAX_SND_CARDS) return; + #endif + #ifdef __JACK_ENABLED__ + tmp=jack_card_manager_init(manager,index); + index+=tmp; + if (index>=MAX_SND_CARDS) return; + #endif + #ifdef HAVE_PORTAUDIO + tmp=portaudio_card_manager_init(manager,index); + index+=tmp; + if (index>=MAX_SND_CARDS) return; + #endif + #ifdef HAVE_SYS_AUDIO_H + tmp=sys_audio_manager_init(manager,index); + index+=tmp; + #endif +} + + + + + +SndCard * snd_card_manager_get_card(SndCardManager *manager,int index) +{ + g_return_val_if_fail(index>=0,NULL); + g_return_val_if_fail(index<MAX_SND_CARDS,NULL); + if (index>MAX_SND_CARDS) return NULL; + return manager->cards[index]; +} + +SndCard * snd_card_manager_get_card_with_string(SndCardManager *manager,const char *cardname,int *index) +{ + int i; + for (i=0;i<MAX_SND_CARDS;i++){ + gchar *card_name; + if (manager->cards[i]==NULL) continue; + card_name=manager->cards[i]->card_name; + if (card_name==NULL) continue; + if (strcmp(card_name,cardname)==0){ + *index=i; + return manager->cards[i]; + } + } + g_warning("No card %s found.",cardname); + return NULL; +} + +SndCardManager _snd_card_manager; +SndCardManager *snd_card_manager=&_snd_card_manager; diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h new file mode 100644 index 00000000..d84757fd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h @@ -0,0 +1,143 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + This library 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 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#ifndef SNDCARD_H +#define SNDCARD_H + +#undef PACKAGE +#undef VERSION +#include <config.h> +#undef PACKAGE +#undef VERSION + +#ifdef HAVE_GLIB +#include <glib.h> +#else +#include <uglib.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* the base class for all soundcards: SndCard */ +struct _SndCard; + +typedef int (*SndCardOpenFunc)(struct _SndCard*,int, int, int); +typedef void (*SndCardSetBlockingModeFunc)(struct _SndCard*, gboolean ); +typedef void (*SndCardCloseFunc)(struct _SndCard*); +typedef gint (*SndCardIOFunc)(struct _SndCard*,char *,int); +typedef void (*SndCardDestroyFunc)(struct _SndCard*); +typedef gboolean (*SndCardPollFunc)(struct _SndCard*); +typedef gint (*SndCardMixerGetLevelFunc)(struct _SndCard*,gint); +typedef void (*SndCardMixerSetRecSourceFunc)(struct _SndCard*,gint); +typedef void (*SndCardMixerSetLevelFunc)(struct _SndCard*,gint ,gint); +typedef struct _MSFilter * (*SndCardCreateFilterFunc)(struct _SndCard *); + +struct _SndCard +{ + gchar *card_name; /* SB16 PCI for example */ + gint index; + gint bsize; + gint rate; + gint stereo; + gint bits; + gint flags; +#define SND_CARD_FLAGS_OPENED 1 + SndCardOpenFunc _probe; + SndCardOpenFunc _open_r; + SndCardOpenFunc _open_w; + SndCardSetBlockingModeFunc _set_blocking_mode; + SndCardPollFunc _can_read; + SndCardIOFunc _read; + SndCardIOFunc _write; + SndCardCloseFunc _close_r; + SndCardCloseFunc _close_w; + SndCardMixerGetLevelFunc _get_level; + SndCardMixerSetLevelFunc _set_level; + SndCardMixerSetRecSourceFunc _set_rec_source; + SndCardCreateFilterFunc _create_read_filter; + SndCardCreateFilterFunc _create_write_filter; + SndCardDestroyFunc _destroy; +}; + + +typedef struct _SndCard SndCard; + +void snd_card_init(SndCard *obj); +void snd_card_uninit(SndCard *obj); +gint snd_card_probe(SndCard *obj, int bits, int stereo, int rate); +int snd_card_open_r(SndCard *obj, int bits, int stereo, int rate); +int snd_card_open_w(SndCard *obj, int bits, int stereo, int rate); +int snd_card_get_bsize(SndCard *obj); +gboolean snd_card_can_read(SndCard *obj); +int snd_card_read(SndCard *obj,char *buffer,int size); +int snd_card_write(SndCard *obj,char *buffer,int size); +void snd_card_set_blocking_mode(SndCard *obj,gboolean yesno); +void snd_card_close_r(SndCard *obj); +void snd_card_close_w(SndCard *obj); + +void snd_card_set_rec_source(SndCard *obj, int source); /* source='l' or 'm'*/ +void snd_card_set_level(SndCard *obj, int way, int level); +gint snd_card_get_level(SndCard *obj,int way); + +const gchar *snd_card_get_identifier(SndCard *obj); + +struct _MSFilter * snd_card_create_read_filter(SndCard *sndcard); +struct _MSFilter * snd_card_create_write_filter(SndCard *sndcard); + + +#define SND_CARD_LEVEL_GENERAL 1 +#define SND_CARD_LEVEL_INPUT 2 +#define SND_CARD_LEVEL_OUTPUT 3 + + +int snd_card_destroy(SndCard *obj); + +#define SND_CARD(obj) ((SndCard*)(obj)) + + + + +/* SndCardManager */ + +#define MAX_SND_CARDS 20 + + +struct _SndCardManager +{ + SndCard *cards[MAX_SND_CARDS]; +}; + +typedef struct _SndCardManager SndCardManager; + +void snd_card_manager_init(SndCardManager *manager); +SndCard * snd_card_manager_get_card(SndCardManager *manager,int index); +SndCard * snd_card_manager_get_card_with_string(SndCardManager *manager,const char *cardname,int *index); + +extern SndCardManager *snd_card_manager; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h new file mode 100644 index 00000000..6768d8f8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h @@ -0,0 +1,111 @@ +/* +linphone +Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* the following code was taken from a free software utility that I don't remember the name. */ +/* sorry */ + + + +#include <ms.h> +#ifndef waveheader_h +#define waveheader_h + +typedef struct uint16scheme +{ + unsigned char lo_byte; + unsigned char hi_byte; +} uint16scheme_t; + +typedef struct uint32scheme +{ + guint16 lo_int; + guint16 hi_int; +} uint32scheme_t; + + +/* all integer in wav header must be read in least endian order */ +inline guint16 _readuint16(guint16 a) +{ + guint16 res; + uint16scheme_t *tmp1=(uint16scheme_t*)&a; + + ((uint16scheme_t *)(&res))->lo_byte=tmp1->hi_byte; + ((uint16scheme_t *)(&res))->hi_byte=tmp1->lo_byte; + return res; +} + +inline guint32 _readuint32(guint32 a) +{ + guint32 res; + uint32scheme_t *tmp1=(uint32scheme_t*)&a; + + ((uint32scheme_t *)(&res))->lo_int=_readuint16(tmp1->hi_int); + ((uint32scheme_t *)(&res))->hi_int=_readuint16(tmp1->lo_int); + return res; +} + +#ifdef WORDS_BIGENDIAN +#define le_uint32(a) (_readuint32((a))) +#define le_uint16(a) (_readuint16((a))) +#define le_int16(a) ( (gint16) _readuint16((guint16)((a))) ) +#else +#define le_uint32(a) (a) +#define le_uint16(a) (a) +#define le_int16(a) (a) +#endif + +typedef struct _riff_t { + char riff[4] ; /* "RIFF" (ASCII characters) */ + guint32 len ; /* Length of package (binary, little endian) */ + char wave[4] ; /* "WAVE" (ASCII characters) */ +} riff_t; + +/* The FORMAT chunk */ + +typedef struct _format_t { + char fmt[4] ; /* "fmt_" (ASCII characters) */ + guint32 len ; /* length of FORMAT chunk (always 0x10) */ + guint16 que ; /* Always 0x01 */ + guint16 channel ; /* Channel numbers (0x01 = mono, 0x02 = stereo) */ + guint32 rate ; /* Sample rate (binary, in Hz) */ + guint32 bps ; /* Bytes Per Second */ + guint16 bpsmpl ; /* bytes per sample: 1 = 8 bit Mono, + 2 = 8 bit Stereo/16 bit Mono, + 4 = 16 bit Stereo */ + guint16 bitpspl ; /* bits per sample */ +} format_t; + +/* The DATA chunk */ + +typedef struct _data_t { + char data[4] ; /* "data" (ASCII characters) */ + int len ; /* length of data */ +} data_t; + +typedef struct _wave_header_t +{ + riff_t riff_chunk; + format_t format_chunk; + data_t data_chunk; +} wave_header_t; + +#define wave_header_get_rate(header) le_uint32((header)->format_chunk.rate) +#define wave_header_get_channel(header) le_uint16((header)->format_chunk.channel) + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am new file mode 100644 index 00000000..1e7abcfd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am @@ -0,0 +1,18 @@ +libcricketxmllite_la_SOURCES = qname.cc \ + xmlbuilder.cc \ + xmlconstants.cc \ + xmlelement.cc \ + xmlnsstack.cc \ + xmlparser.cc \ + xmlprinter.cc + +noinst_HEADERS = qname.h \ + xmlbuilder.h \ + xmlconstants.h \ + xmlelement.h \ + xmlnsstack.h \ + xmlparser.h \ + xmlprinter.h +AM_CPPFLAGS = -DPOSIX -I$(srcdir)/../.. + +noinst_LTLIBRARIES = libcricketxmllite.la diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc new file mode 100644 index 00000000..626cfa96 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc @@ -0,0 +1,167 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <string> +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlconstants.h" + +//#define new TRACK_NEW + +namespace buzz { + +static int QName_Hash(const std::string & ns, const char * local) { + int result = ns.size() * 101; + while (*local) { + result *= 19; + result += *local; + local += 1; + } + return result; +} + +static const int bits = 9; +static QName::Data * get_qname_table() { + static QName::Data qname_table[1 << bits]; + return qname_table; +} + +static QName::Data * +AllocateOrFind(const std::string & ns, const char * local) { + int index = QName_Hash(ns, local); + int increment = index >> (bits - 1) | 1; + QName::Data * qname_table = get_qname_table(); + for (;;) { + index &= ((1 << bits) - 1); + if (!qname_table[index].Occupied()) { + return new QName::Data(ns, local); + } + if (qname_table[index].localPart_ == local && + qname_table[index].namespace_ == ns) { + qname_table[index].AddRef(); + return qname_table + index; + } + index += increment; + } +} + +static QName::Data * +Add(const std::string & ns, const char * local) { + int index = QName_Hash(ns, local); + int increment = index >> (bits - 1) | 1; + QName::Data * qname_table = get_qname_table(); + for (;;) { + index &= ((1 << bits) - 1); + if (!qname_table[index].Occupied()) { + qname_table[index].namespace_ = ns; + qname_table[index].localPart_ = local; + qname_table[index].AddRef(); // AddRef twice so it's never deleted + qname_table[index].AddRef(); + return qname_table + index; + } + if (qname_table[index].localPart_ == local && + qname_table[index].namespace_ == ns) { + qname_table[index].AddRef(); + return qname_table + index; + } + index += increment; + } +} + +QName::~QName() { + data_->Release(); +} + +QName::QName() : data_(QN_EMPTY.data_) { + data_->AddRef(); +} + +QName::QName(bool add, const std::string & ns, const char * local) : + data_(add ? Add(ns, local) : AllocateOrFind(ns, local)) {} + +QName::QName(bool add, const std::string & ns, const std::string & local) : + data_(add ? Add(ns, local.c_str()) : AllocateOrFind(ns, local.c_str())) {} + +QName::QName(const std::string & ns, const char * local) : + data_(AllocateOrFind(ns, local)) {} + +static std::string +QName_LocalPart(const std::string & name) { + size_t i = name.rfind(':'); + if (i == std::string::npos) + return name; + return name.substr(i + 1); +} + +static std::string +QName_Namespace(const std::string & name) { + size_t i = name.rfind(':'); + if (i == std::string::npos) + return STR_EMPTY; + return name.substr(0, i); +} + +QName::QName(const std::string & mergedOrLocal) : + data_(AllocateOrFind(QName_Namespace(mergedOrLocal), + QName_LocalPart(mergedOrLocal).c_str())) {} + +std::string +QName::Merged() const { + if (data_->namespace_ == STR_EMPTY) + return data_->localPart_; + + std::string result(data_->namespace_); + result.reserve(result.length() + 1 + data_->localPart_.length()); + result += ':'; + result += data_->localPart_; + return result; +} + +bool +QName::operator==(const QName & other) const { + return other.data_ == data_ || + data_->localPart_ == other.data_->localPart_ && + data_->namespace_ == other.data_->namespace_; +} + +int +QName::Compare(const QName & other) const { + if (data_ == other.data_) + return 0; + + int result = data_->localPart_.compare(other.data_->localPart_); + if (result) + return result; + + return data_->namespace_.compare(other.data_->namespace_); +} + +} + + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h new file mode 100644 index 00000000..b1bcec61 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h @@ -0,0 +1,87 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _qname_h_ +#define _qname_h_ + +#include <string> + +namespace buzz { + + +class QName +{ +public: + explicit QName(); + QName(const QName & qname) : data_(qname.data_) { data_->AddRef(); } + explicit QName(bool add, const std::string & ns, const char * local); + explicit QName(bool add, const std::string & ns, const std::string & local); + explicit QName(const std::string & ns, const char * local); + explicit QName(const std::string & mergedOrLocal); + QName & operator=(const QName & qn) { + qn.data_->AddRef(); + data_->Release(); + data_ = qn.data_; + return *this; + } + ~QName(); + + const std::string & Namespace() const { return data_->namespace_; } + const std::string & LocalPart() const { return data_->localPart_; } + std::string Merged() const; + int Compare(const QName & other) const; + bool operator==(const QName & other) const; + bool operator!=(const QName & other) const { return !operator==(other); } + bool operator<(const QName & other) const { return Compare(other) < 0; } + + class Data { + public: + Data(const std::string & ns, const std::string & local) : + refcount_(1), + namespace_(ns), + localPart_(local) {} + + Data() : refcount_(0) {} + + std::string namespace_; + std::string localPart_; + void AddRef() { refcount_++; } + void Release() { if (!--refcount_) { delete this; } } + bool Occupied() { return !!refcount_; } + + private: + int refcount_; + }; + +private: + Data * data_; +}; + + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc new file mode 100644 index 00000000..313c4013 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc @@ -0,0 +1,151 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include <vector> +#include <set> +#include <expat.h> +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlbuilder.h" + +#define new TRACK_NEW + +namespace buzz { + +XmlBuilder::XmlBuilder() : + pelCurrent_(NULL), + pelRoot_(NULL), + pvParents_(new std::vector<XmlElement *>()) { +} + +void +XmlBuilder::Reset() { + pelRoot_.reset(); + pelCurrent_ = NULL; + pvParents_->clear(); +} + +XmlElement * +XmlBuilder::BuildElement(XmlParseContext * pctx, + const char * name, const char ** atts) { + QName tagName(pctx->ResolveQName(name, false)); + if (tagName == QN_EMPTY) + return NULL; + + XmlElement * pelNew = new XmlElement(tagName); + + if (!*atts) + return pelNew; + + std::set<QName> seenNonlocalAtts; + + while (*atts) { + QName attName(pctx->ResolveQName(*atts, true)); + if (attName == QN_EMPTY) { + delete pelNew; + return NULL; + } + + // verify that namespaced names are unique + if (!attName.Namespace().empty()) { + if (seenNonlocalAtts.count(attName)) { + delete pelNew; + return NULL; + } + seenNonlocalAtts.insert(attName); + } + + pelNew->AddAttr(attName, std::string(*(atts + 1))); + atts += 2; + } + + return pelNew; +} + +void +XmlBuilder::StartElement(XmlParseContext * pctx, + const char * name, const char ** atts) { + XmlElement * pelNew = BuildElement(pctx, name, atts); + if (pelNew == NULL) { + pctx->RaiseError(XML_ERROR_SYNTAX); + return; + } + + if (!pelCurrent_) { + pelCurrent_ = pelNew; + pelRoot_.reset(pelNew); + pvParents_->push_back(NULL); + } else { + pelCurrent_->AddElement(pelNew); + pvParents_->push_back(pelCurrent_); + pelCurrent_ = pelNew; + } +} + +void +XmlBuilder::EndElement(XmlParseContext * pctx, const char * name) { + UNUSED(pctx); + UNUSED(name); + pelCurrent_ = pvParents_->back(); + pvParents_->pop_back(); +} + +void +XmlBuilder::CharacterData(XmlParseContext * pctx, + const char * text, int len) { + UNUSED(pctx); + if (pelCurrent_) { + pelCurrent_->AddParsedText(text, len); + } +} + +void +XmlBuilder::Error(XmlParseContext * pctx, XML_Error err) { + UNUSED(pctx); + UNUSED(err); + pelRoot_.reset(NULL); + pelCurrent_ = NULL; + pvParents_->clear(); +} + +XmlElement * +XmlBuilder::CreateElement() { + return pelRoot_.release(); +} + +XmlElement * +XmlBuilder::BuiltElement() { + return pelRoot_.get(); +} + +XmlBuilder::~XmlBuilder() { +} + + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h new file mode 100644 index 00000000..b5b1be59 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h @@ -0,0 +1,79 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmlbuilder_h_ +#define _xmlbuilder_h_ + +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/base/stl_decl.h" +#include "talk/xmllite/xmlparser.h" + +#ifdef OSX +#include "talk/third_party/expat/expat.h" +#else +#include <expat.h> +#endif + +namespace buzz { + +class XmlElement; +class XmlParseContext; + + +class XmlBuilder : public XmlParseHandler { +public: + XmlBuilder(); + + static XmlElement * BuildElement(XmlParseContext * pctx, + const char * name, const char ** atts); + virtual void StartElement(XmlParseContext * pctx, + const char * name, const char ** atts); + virtual void EndElement(XmlParseContext * pctx, const char * name); + virtual void CharacterData(XmlParseContext * pctx, + const char * text, int len); + virtual void Error(XmlParseContext * pctx, XML_Error); + virtual ~XmlBuilder(); + + void Reset(); + + // Take ownership of the built element; second call returns NULL + XmlElement * CreateElement(); + + // Peek at the built element without taking ownership + XmlElement * BuiltElement(); + +private: + XmlElement * pelCurrent_; + scoped_ptr<XmlElement> pelRoot_; + scoped_ptr<std::vector<XmlElement *, std::allocator<XmlElement *> > > + pvParents_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc new file mode 100644 index 00000000..503f832f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc @@ -0,0 +1,65 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "xmlconstants.h" + +using namespace buzz; + +const std::string & XmlConstants::str_empty() { + static const std::string str_empty_; + return str_empty_; +} + +const std::string & XmlConstants::ns_xml() { + static const std::string ns_xml_("http://www.w3.org/XML/1998/namespace"); + return ns_xml_; +} + +const std::string & XmlConstants::ns_xmlns() { + static const std::string ns_xmlns_("http://www.w3.org/2000/xmlns/"); + return ns_xmlns_; +} + +const std::string & XmlConstants::str_xmlns() { + static const std::string str_xmlns_("xmlns"); + return str_xmlns_; +} + +const std::string & XmlConstants::str_xml() { + static const std::string str_xml_("xml"); + return str_xml_; +} + +const std::string & XmlConstants::str_version() { + static const std::string str_version_("version"); + return str_version_; +} + +const std::string & XmlConstants::str_encoding() { + static const std::string str_encoding_("encoding"); + return str_encoding_; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h new file mode 100644 index 00000000..8514d6f4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h @@ -0,0 +1,61 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +// Because global constant initialization order is undefined +// globals cannot depend on other objects to be instantiated. +// This class creates string objects within static methods +// such that globals may refer to these constants by the +// accessor function and they are guaranteed to be initialized. + +#ifndef TALK_XMLLITE_CONSTANTS_H_ +#define TALK_XMLLITE_CONSTANTS_H_ + +#include <string> + +#define STR_EMPTY XmlConstants::str_empty() +#define NS_XML XmlConstants::ns_xml() +#define NS_XMLNS XmlConstants::ns_xmlns() +#define STR_XMLNS XmlConstants::str_xmlns() +#define STR_XML XmlConstants::str_xml() +#define STR_VERSION XmlConstants::str_version() +#define STR_ENCODING XmlConstants::str_encoding() +namespace buzz { + +class XmlConstants { + public: + static const std::string & str_empty(); + static const std::string & ns_xml(); + static const std::string & ns_xmlns(); + static const std::string & str_xmlns(); + static const std::string & str_xml(); + static const std::string & str_version(); + static const std::string & str_encoding(); +}; + +} + +#endif // TALK_XMLLITE_CONSTANTS_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc new file mode 100644 index 00000000..d3619a92 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc @@ -0,0 +1,491 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <string> +#include <iostream> +#include <vector> +#include <sstream> + +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlparser.h" +#include "talk/xmllite/xmlbuilder.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmllite/xmlconstants.h" + +#define new TRACK_NEW + +namespace buzz { + +const QName QN_EMPTY(true, STR_EMPTY, STR_EMPTY); +const QName QN_XMLNS(true, STR_EMPTY, STR_XMLNS); + + +XmlChild::~XmlChild() { +} + +bool +XmlText::IsTextImpl() const { + return true; +} + +XmlElement * +XmlText::AsElementImpl() const { + return NULL; +} + +XmlText * +XmlText::AsTextImpl() const { + return const_cast<XmlText *>(this); +} + +void +XmlText::SetText(const std::string & text) { + text_ = text; +} + +void +XmlText::AddParsedText(const char * buf, int len) { + text_.append(buf, len); +} + +void +XmlText::AddText(const std::string & text) { + text_ += text; +} + +XmlText::~XmlText() { +} + +XmlElement::XmlElement(const QName & name) : + name_(name), + pFirstAttr_(NULL), + pLastAttr_(NULL), + pFirstChild_(NULL), + pLastChild_(NULL) { +} + +XmlElement::XmlElement(const XmlElement & elt) : + XmlChild(), + name_(elt.name_), + pFirstAttr_(NULL), + pLastAttr_(NULL), + pFirstChild_(NULL), + pLastChild_(NULL) { + + // copy attributes + XmlAttr * pAttr; + XmlAttr ** ppLastAttr = &pFirstAttr_; + XmlAttr * newAttr = NULL; + for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) { + newAttr = new XmlAttr(*pAttr); + *ppLastAttr = newAttr; + ppLastAttr = &(newAttr->pNextAttr_); + } + pLastAttr_ = newAttr; + + // copy children + XmlChild * pChild; + XmlChild ** ppLast = &pFirstChild_; + XmlChild * newChild = NULL; + + for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) { + if (pChild->IsText()) { + newChild = new XmlText(*(pChild->AsText())); + } else { + newChild = new XmlElement(*(pChild->AsElement())); + } + *ppLast = newChild; + ppLast = &(newChild->pNextChild_); + } + pLastChild_ = newChild; + +} + +XmlElement::XmlElement(const QName & name, bool useDefaultNs) : + name_(name), + pFirstAttr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL), + pLastAttr_(pFirstAttr_), + pFirstChild_(NULL), + pLastChild_(NULL) { +} + +bool +XmlElement::IsTextImpl() const { + return false; +} + +XmlElement * +XmlElement::AsElementImpl() const { + return const_cast<XmlElement *>(this); +} + +XmlText * +XmlElement::AsTextImpl() const { + return NULL; +} + +const std::string & +XmlElement::BodyText() const { + if (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { + return pFirstChild_->AsText()->Text(); + } + + return STR_EMPTY; +} + +void +XmlElement::SetBodyText(const std::string & text) { + if (text == STR_EMPTY) { + ClearChildren(); + } else if (pFirstChild_ == NULL) { + AddText(text); + } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { + pFirstChild_->AsText()->SetText(text); + } else { + ClearChildren(); + AddText(text); + } +} + +const QName & +XmlElement::FirstElementName() const { + const XmlElement * element = FirstElement(); + if (element == NULL) + return QN_EMPTY; + return element->Name(); +} + +XmlAttr * +XmlElement::FirstAttr() { + return pFirstAttr_; +} + +const std::string & +XmlElement::Attr(const QName & name) const { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + return pattr->value_; + } + return STR_EMPTY; +} + +bool +XmlElement::HasAttr(const QName & name) const { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + return true; + } + return false; +} + +void +XmlElement::SetAttr(const QName & name, const std::string & value) { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + break; + } + if (!pattr) { + pattr = new XmlAttr(name, value); + if (pLastAttr_) + pLastAttr_->pNextAttr_ = pattr; + else + pFirstAttr_ = pattr; + pLastAttr_ = pattr; + return; + } + pattr->value_ = value; +} + +void +XmlElement::ClearAttr(const QName & name) { + XmlAttr * pattr; + XmlAttr *pLastAttr = NULL; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + break; + pLastAttr = pattr; + } + if (!pattr) + return; + if (!pLastAttr) + pFirstAttr_ = pattr->pNextAttr_; + else + pLastAttr->pNextAttr_ = pattr->pNextAttr_; + if (pLastAttr_ == pattr) + pLastAttr_ = pLastAttr; + delete pattr; +} + +XmlChild * +XmlElement::FirstChild() { + return pFirstChild_; +} + +XmlElement * +XmlElement::FirstElement() { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText()) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextElement() { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText()) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::FirstWithNamespace(const std::string & ns) { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextWithNamespace(const std::string & ns) { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::FirstNamed(const QName & name) { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextNamed(const QName & name) { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement(); + } + return NULL; +} + +const std::string & +XmlElement::TextNamed(const QName & name) const { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement()->BodyText(); + } + return STR_EMPTY; +} + +void +XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) { + if (pPredecessor == NULL) { + pNext->pNextChild_ = pFirstChild_; + pFirstChild_ = pNext; + } + else { + pNext->pNextChild_ = pPredecessor->pNextChild_; + pPredecessor->pNextChild_ = pNext; + } +} + +void +XmlElement::RemoveChildAfter(XmlChild * pPredecessor) { + XmlChild * pNext; + + if (pPredecessor == NULL) { + pNext = pFirstChild_; + pFirstChild_ = pNext->pNextChild_; + } + else { + pNext = pPredecessor->pNextChild_; + pPredecessor->pNextChild_ = pNext->pNextChild_; + } + + if (pLastChild_ == pNext) + pLastChild_ = pPredecessor; + + delete pNext; +} + +void +XmlElement::AddAttr(const QName & name, const std::string & value) { + ASSERT(!HasAttr(name)); + + XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_; + pLastAttr_ = (*pprev = new XmlAttr(name, value)); +} + +void +XmlElement::AddAttr(const QName & name, const std::string & value, + int depth) { + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddAttr(name, value); +} + +void +XmlElement::AddParsedText(const char * cstr, int len) { + if (len == 0) + return; + + if (pLastChild_ && pLastChild_->IsText()) { + pLastChild_->AsText()->AddParsedText(cstr, len); + return; + } + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = new XmlText(cstr, len); +} + +void +XmlElement::AddText(const std::string & text) { + if (text == STR_EMPTY) + return; + + if (pLastChild_ && pLastChild_->IsText()) { + pLastChild_->AsText()->AddText(text); + return; + } + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = new XmlText(text); +} + +void +XmlElement::AddText(const std::string & text, int depth) { + // note: the first syntax is ambigious for msvc 6 + // XmlElement * pel(this); + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddText(text); +} + +void +XmlElement::AddElement(XmlElement *pelChild) { + if (pelChild == NULL) + return; + + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = pelChild; + pelChild->pNextChild_ = NULL; +} + +void +XmlElement::AddElement(XmlElement *pelChild, int depth) { + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddElement(pelChild); +} + +void +XmlElement::ClearNamedChildren(const QName & name) { + XmlChild * prev_child = NULL; + XmlChild * next_child; + XmlChild * child; + for (child = FirstChild(); child; child = next_child) { + next_child = child->NextChild(); + if (!child->IsText() && child->AsElement()->Name() == name) + { + RemoveChildAfter(prev_child); + continue; + } + prev_child = child; + } +} + +void +XmlElement::ClearChildren() { + XmlChild * pchild; + for (pchild = pFirstChild_; pchild; ) { + XmlChild * pToDelete = pchild; + pchild = pchild->pNextChild_; + delete pToDelete; + } + pFirstChild_ = pLastChild_ = NULL; +} + +std::string +XmlElement::Str() const { + std::stringstream ss; + Print(&ss, NULL, 0); + return ss.str(); +} + +XmlElement * +XmlElement::ForStr(const std::string & str) { + XmlBuilder builder; + XmlParser::ParseXml(&builder, str); + return builder.CreateElement(); +} + +void +XmlElement::Print( + std::ostream * pout, std::string xmlns[], int xmlnsCount) const { + XmlPrinter::PrintXml(pout, this, xmlns, xmlnsCount); +} + +XmlElement::~XmlElement() { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; ) { + XmlAttr * pToDelete = pattr; + pattr = pattr->pNextAttr_; + delete pToDelete; + } + + XmlChild * pchild; + for (pchild = pFirstChild_; pchild; ) { + XmlChild * pToDelete = pchild; + pchild = pchild->pNextChild_; + delete pToDelete; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h new file mode 100644 index 00000000..06545d89 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h @@ -0,0 +1,231 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmlelement_h_ +#define _xmlelement_h_ + +#include <iosfwd> +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/xmllite/qname.h" + +namespace buzz { + +extern const QName QN_EMPTY; +extern const QName QN_XMLNS; + + +class XmlChild; +class XmlText; +class XmlElement; +class XmlAttr; + +class XmlChild { +friend class XmlElement; + +public: + + XmlChild * NextChild() { return pNextChild_; } + const XmlChild * NextChild() const { return pNextChild_; } + + bool IsText() const { return IsTextImpl(); } + + XmlElement * AsElement() { return AsElementImpl(); } + const XmlElement * AsElement() const { return AsElementImpl(); } + + XmlText * AsText() { return AsTextImpl(); } + const XmlText * AsText() const { return AsTextImpl(); } + + +protected: + + XmlChild() : + pNextChild_(NULL) { + } + + virtual bool IsTextImpl() const = 0; + virtual XmlElement * AsElementImpl() const = 0; + virtual XmlText * AsTextImpl() const = 0; + + + virtual ~XmlChild(); + +private: + XmlChild(const XmlChild & noimpl); + + XmlChild * pNextChild_; + +}; + +class XmlText : public XmlChild { +public: + explicit XmlText(const std::string & text) : + XmlChild(), + text_(text) { + } + explicit XmlText(const XmlText & t) : + XmlChild(), + text_(t.text_) { + } + explicit XmlText(const char * cstr, size_t len) : + XmlChild(), + text_(cstr, len) { + } + virtual ~XmlText(); + + const std::string & Text() const { return text_; } + void SetText(const std::string & text); + void AddParsedText(const char * buf, int len); + void AddText(const std::string & text); + +protected: + virtual bool IsTextImpl() const; + virtual XmlElement * AsElementImpl() const; + virtual XmlText * AsTextImpl() const; + +private: + std::string text_; +}; + +class XmlAttr { +friend class XmlElement; + +public: + XmlAttr * NextAttr() const { return pNextAttr_; } + const QName & Name() const { return name_; } + const std::string & Value() const { return value_; } + +private: + explicit XmlAttr(const QName & name, const std::string & value) : + pNextAttr_(NULL), + name_(name), + value_(value) { + } + explicit XmlAttr(const XmlAttr & att) : + pNextAttr_(NULL), + name_(att.name_), + value_(att.value_) { + } + + XmlAttr * pNextAttr_; + QName name_; + std::string value_; +}; + +class XmlElement : public XmlChild { +public: + explicit XmlElement(const QName & name); + explicit XmlElement(const QName & name, bool useDefaultNs); + explicit XmlElement(const XmlElement & elt); + + virtual ~XmlElement(); + + const QName & Name() const { return name_; } + + const std::string & BodyText() const; + void SetBodyText(const std::string & text); + + const QName & FirstElementName() const; + + XmlAttr * FirstAttr(); + const XmlAttr * FirstAttr() const + { return const_cast<XmlElement *>(this)->FirstAttr(); } + + //! Attr will return STR_EMPTY if the attribute isn't there: + //! use HasAttr to test presence of an attribute. + const std::string & Attr(const QName & name) const; + bool HasAttr(const QName & name) const; + void SetAttr(const QName & name, const std::string & value); + void ClearAttr(const QName & name); + + XmlChild * FirstChild(); + const XmlChild * FirstChild() const + { return const_cast<XmlElement *>(this)->FirstChild(); } + + XmlElement * FirstElement(); + const XmlElement * FirstElement() const + { return const_cast<XmlElement *>(this)->FirstElement(); } + + XmlElement * NextElement(); + const XmlElement * NextElement() const + { return const_cast<XmlElement *>(this)->NextElement(); } + + XmlElement * FirstWithNamespace(const std::string & ns); + const XmlElement * FirstWithNamespace(const std::string & ns) const + { return const_cast<XmlElement *>(this)->FirstWithNamespace(ns); } + + XmlElement * NextWithNamespace(const std::string & ns); + const XmlElement * NextWithNamespace(const std::string & ns) const + { return const_cast<XmlElement *>(this)->NextWithNamespace(ns); } + + XmlElement * FirstNamed(const QName & name); + const XmlElement * FirstNamed(const QName & name) const + { return const_cast<XmlElement *>(this)->FirstNamed(name); } + + XmlElement * NextNamed(const QName & name); + const XmlElement * NextNamed(const QName & name) const + { return const_cast<XmlElement *>(this)->NextNamed(name); } + + const std::string & TextNamed(const QName & name) const; + + void InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNewChild); + void RemoveChildAfter(XmlChild * pPredecessor); + + void AddParsedText(const char * buf, int len); + void AddText(const std::string & text); + void AddText(const std::string & text, int depth); + void AddElement(XmlElement * pelChild); + void AddElement(XmlElement * pelChild, int depth); + void AddAttr(const QName & name, const std::string & value); + void AddAttr(const QName & name, const std::string & value, int depth); + void ClearNamedChildren(const QName & name); + void ClearChildren(); + + static XmlElement * ForStr(const std::string & str); + std::string Str() const; + + void Print(std::ostream * pout, std::string xmlns[], int xmlnsCount) const; + +protected: + virtual bool IsTextImpl() const; + virtual XmlElement * AsElementImpl() const; + virtual XmlText * AsTextImpl() const; + +private: + QName name_; + XmlAttr * pFirstAttr_; + XmlAttr * pLastAttr_; + XmlChild * pFirstChild_; + XmlChild * pLastChild_; + +}; + + + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc new file mode 100644 index 00000000..4dcb6490 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc @@ -0,0 +1,205 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include <string> +#include <iostream> +#include <vector> +#include <sstream> +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +XmlnsStack::XmlnsStack() : + pxmlnsStack_(new std::vector<std::string>), + pxmlnsDepthStack_(new std::vector<size_t>) { +} + +XmlnsStack::~XmlnsStack() {} + +void +XmlnsStack::PushFrame() { + pxmlnsDepthStack_->push_back(pxmlnsStack_->size()); +} + +void +XmlnsStack::PopFrame() { + size_t prev_size = pxmlnsDepthStack_->back(); + pxmlnsDepthStack_->pop_back(); + if (prev_size < pxmlnsStack_->size()) { + pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size, + pxmlnsStack_->end()); + } +} +const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false); +const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true); +const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true); + +const std::string * +XmlnsStack::NsForPrefix(const std::string & prefix) { + if (prefix.length() >= 3 && + (prefix[0] == 'x' || prefix[0] == 'X') && + (prefix[1] == 'm' || prefix[1] == 'M') && + (prefix[2] == 'l' || prefix[2] == 'L')) { + if (prefix == "xml") + return &(NS_XML); + if (prefix == "xmlns") + return &(NS_XMLNS); + return NULL; + } + + std::vector<std::string>::iterator pos; + for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { + pos -= 2; + if (*pos == prefix) + return &(*(pos + 1)); + } + + if (prefix == STR_EMPTY) + return &(STR_EMPTY); // default namespace + + return NULL; // none found +} + +bool +XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) { + const std::string * match = NsForPrefix(prefix); + if (match == NULL) + return false; + return (*match == ns); +} + +std::pair<std::string, bool> +XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) { + if (ns == NS_XML) + return std::make_pair(std::string("xml"), true); + if (ns == NS_XMLNS) + return std::make_pair(std::string("xmlns"), true); + if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns)) + return std::make_pair(STR_EMPTY, true); + + std::vector<std::string>::iterator pos; + for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { + pos -= 2; + if (*(pos + 1) == ns && + (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns)) + return std::make_pair(*pos, true); + } + + return std::make_pair(STR_EMPTY, false); // none found +} + +std::string +XmlnsStack::FormatQName(const QName & name, bool isAttr) { + std::string prefix(PrefixForNs(name.Namespace(), isAttr).first); + if (prefix == STR_EMPTY) + return name.LocalPart(); + else + return prefix + ':' + name.LocalPart(); +} + +void +XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) { + pxmlnsStack_->push_back(prefix); + pxmlnsStack_->push_back(ns); +} + +void +XmlnsStack::RemoveXmlns() { + pxmlnsStack_->pop_back(); + pxmlnsStack_->pop_back(); +} + +static bool IsAsciiLetter(char ch) { + return ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z')); +} + +static std::string AsciiLower(const std::string & s) { + std::string result(s); + size_t i; + for (i = 0; i < result.length(); i++) { + if (result[i] >= 'A' && result[i] <= 'Z') + result[i] += 'a' - 'A'; + } + return result; +} + +static std::string SuggestPrefix(const std::string & ns) { + size_t len = ns.length(); + size_t i = ns.find_last_of('.'); + if (i != std::string::npos && len - i <= 4 + 1) + len = i; // chop off ".html" or ".xsd" or ".?{0,4}" + size_t last = len; + while (last > 0) { + last -= 1; + if (IsAsciiLetter(ns[last])) { + size_t first = last; + last += 1; + while (first > 0) { + if (!IsAsciiLetter(ns[first - 1])) + break; + first -= 1; + } + if (last - first > 4) + last = first + 3; + std::string candidate(AsciiLower(ns.substr(first, last - first))); + if (candidate.find("xml") != 0) + return candidate; + break; + } + } + return "ns"; +} + + +std::pair<std::string, bool> +XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) { + if (PrefixForNs(ns, isAttr).second) + return std::make_pair(STR_EMPTY, false); + + std::string base(SuggestPrefix(ns)); + std::string result(base); + int i = 2; + while (NsForPrefix(result) != NULL) { + std::stringstream ss; + ss << base; + ss << (i++); + ss >> result; + } + AddXmlns(result, ns); + return std::make_pair(result, true); +} + +void XmlnsStack::Reset() { + pxmlnsStack_->clear(); + pxmlnsDepthStack_->clear(); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h new file mode 100644 index 00000000..299ec1ce --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h @@ -0,0 +1,62 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmlnsstack_h_ +#define _xmlnsstack_h_ + +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/base/stl_decl.h" +#include "talk/xmllite/qname.h" + +namespace buzz { + +class XmlnsStack { +public: + XmlnsStack(); + ~XmlnsStack(); + + void AddXmlns(const std::string & prefix, const std::string & ns); + void RemoveXmlns(); + void PushFrame(); + void PopFrame(); + void Reset(); + + const std::string * NsForPrefix(const std::string & prefix); + bool PrefixMatchesNs(const std::string & prefix, const std::string & ns); + std::pair<std::string, bool> PrefixForNs(const std::string & ns, bool isAttr); + std::pair<std::string, bool> AddNewPrefix(const std::string & ns, bool isAttr); + std::string FormatQName(const QName & name, bool isAttr); + +private: + + scoped_ptr<std::vector<std::string, std::allocator<std::string> > > pxmlnsStack_; + scoped_ptr<std::vector<size_t, std::allocator<size_t> > > pxmlnsDepthStack_; +}; +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc new file mode 100644 index 00000000..f2b56778 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc @@ -0,0 +1,250 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include <string> +#include <vector> +#include <iostream> +#include <expat.h> +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmllite/xmlparser.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +#include <expat.h> + +#define new TRACK_NEW + +namespace buzz { + + +static void +StartElementCallback(void * userData, const char *name, const char **atts) { + (static_cast<XmlParser *>(userData))->ExpatStartElement(name, atts); +} + +static void +EndElementCallback(void * userData, const char *name) { + (static_cast<XmlParser *>(userData))->ExpatEndElement(name); +} + +static void +CharacterDataCallback(void * userData, const char *text, int len) { + (static_cast<XmlParser *>(userData))->ExpatCharacterData(text, len); +} + +static void +XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) { + (static_cast<XmlParser *>(userData))->ExpatXmlDecl(ver, enc, st); +} + +XmlParser::XmlParser(XmlParseHandler *pxph) : + context_(this), pxph_(pxph), sentError_(false) { + expat_ = XML_ParserCreate(NULL); + XML_SetUserData(expat_, this); + XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); + XML_SetCharacterDataHandler(expat_, CharacterDataCallback); + XML_SetXmlDeclHandler(expat_, XmlDeclCallback); +} + +void +XmlParser::Reset() { + if (!XML_ParserReset(expat_, NULL)) { + XML_ParserFree(expat_); + expat_ = XML_ParserCreate(NULL); + } + XML_SetUserData(expat_, this); + XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); + XML_SetCharacterDataHandler(expat_, CharacterDataCallback); + XML_SetXmlDeclHandler(expat_, XmlDeclCallback); + context_.Reset(); + sentError_ = false; +} + +static bool +XmlParser_StartsWithXmlns(const char *name) { + return name[0] == 'x' && + name[1] == 'm' && + name[2] == 'l' && + name[3] == 'n' && + name[4] == 's'; +} + +void +XmlParser::ExpatStartElement(const char *name, const char **atts) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + const char **att; + context_.StartElement(); + for (att = atts; *att; att += 2) { + if (XmlParser_StartsWithXmlns(*att)) { + if ((*att)[5] == '\0') { + context_.StartNamespace("", *(att + 1)); + } + else if ((*att)[5] == ':') { + if (**(att + 1) == '\0') { + // In XML 1.0 empty namespace illegal with prefix (not in 1.1) + context_.RaiseError(XML_ERROR_SYNTAX); + return; + } + context_.StartNamespace((*att) + 6, *(att + 1)); + } + } + } + pxph_->StartElement(&context_, name, atts); +} + +void +XmlParser::ExpatEndElement(const char *name) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + context_.EndElement(); + pxph_->EndElement(&context_, name); +} + +void +XmlParser::ExpatCharacterData(const char *text, int len) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + pxph_->CharacterData(&context_, text, len); +} + +void +XmlParser::ExpatXmlDecl(const char * ver, const char * enc, int standalone) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + + if (ver && std::string("1.0") != ver) { + context_.RaiseError(XML_ERROR_SYNTAX); + return; + } + + if (standalone == 0) { + context_.RaiseError(XML_ERROR_SYNTAX); + return; + } + + if (enc && !((enc[0] == 'U' || enc[0] == 'u') && + (enc[1] == 'T' || enc[1] == 't') && + (enc[2] == 'F' || enc[2] == 'f') && + enc[3] == '-' && enc[4] =='8')) { + context_.RaiseError(XML_ERROR_INCORRECT_ENCODING); + return; + } + +} + +bool +XmlParser::Parse(const char *data, size_t len, bool isFinal) { + if (sentError_) + return false; + + if (XML_Parse(expat_, data, static_cast<int>(len), isFinal) != XML_STATUS_OK) + context_.RaiseError(XML_GetErrorCode(expat_)); + + if (context_.RaisedError() != XML_ERROR_NONE) { + sentError_ = true; + pxph_->Error(&context_, context_.RaisedError()); + return false; + } + + return true; +} + +XmlParser::~XmlParser() { + XML_ParserFree(expat_); +} + +void +XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) { + XmlParser parser(pxph); + parser.Parse(text.c_str(), text.length(), true); +} + +XmlParser::ParseContext::ParseContext(XmlParser *parser) : + parser_(parser), + xmlnsstack_(), + raised_(XML_ERROR_NONE) { +} + +void +XmlParser::ParseContext::StartNamespace(const char *prefix, const char *ns) { + xmlnsstack_.AddXmlns( + *prefix ? std::string(prefix) : STR_EMPTY, +// ns == NS_CLIENT ? NS_CLIENT : +// ns == NS_ROSTER ? NS_ROSTER : +// ns == NS_GR ? NS_GR : + std::string(ns)); +} + +void +XmlParser::ParseContext::StartElement() { + xmlnsstack_.PushFrame(); +} + +void +XmlParser::ParseContext::EndElement() { + xmlnsstack_.PopFrame(); +} + +QName +XmlParser::ParseContext::ResolveQName(const char *qname, bool isAttr) { + const char *c; + for (c = qname; *c; ++c) { + if (*c == ':') { + const std::string * result; + result = xmlnsstack_.NsForPrefix(std::string(qname, c - qname)); + if (result == NULL) + return QN_EMPTY; + const char * localname = c + 1; + return QName(*result, localname); + } + } + if (isAttr) { + return QName(STR_EMPTY, qname); + } + + const std::string * result; + result = xmlnsstack_.NsForPrefix(STR_EMPTY); + if (result == NULL) + return QN_EMPTY; + + return QName(*result, qname); +} + +void +XmlParser::ParseContext::Reset() { + xmlnsstack_.Reset(); + raised_ = XML_ERROR_NONE; +} + +XmlParser::ParseContext::~ParseContext() { +} + +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h new file mode 100644 index 00000000..760802e4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h @@ -0,0 +1,108 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmlparser_h_ +#define _xmlparser_h_ + +#include <string> +#include "talk/xmllite/xmlnsstack.h" +#include <expat.h> + +struct XML_ParserStruct; +typedef struct XML_ParserStruct * XML_Parser; + +namespace buzz { + +class XmlParseHandler; +class XmlParseContext; +class XmlParser; + +class XmlParseContext { +public: + virtual QName ResolveQName(const char * qname, bool isAttr) = 0; + virtual void RaiseError(XML_Error err) = 0; +}; + +class XmlParseHandler { +public: + virtual void StartElement(XmlParseContext * pctx, + const char * name, const char ** atts) = 0; + virtual void EndElement(XmlParseContext * pctx, + const char * name) = 0; + virtual void CharacterData(XmlParseContext * pctx, + const char * text, int len) = 0; + virtual void Error(XmlParseContext * pctx, + XML_Error errorCode) = 0; +}; + +class XmlParser { +public: + static void ParseXml(XmlParseHandler * pxph, std::string text); + + explicit XmlParser(XmlParseHandler * pxph); + bool Parse(const char * data, size_t len, bool isFinal); + void Reset(); + virtual ~XmlParser(); + + // expat callbacks + void ExpatStartElement(const char * name, const char ** atts); + void ExpatEndElement(const char * name); + void ExpatCharacterData(const char * text, int len); + void ExpatXmlDecl(const char * ver, const char * enc, int standalone); + +private: + + class ParseContext : public XmlParseContext { + public: + ParseContext(XmlParser * parser); + virtual ~ParseContext(); + virtual QName ResolveQName(const char * qname, bool isAttr); + virtual void RaiseError(XML_Error err) { if (!raised_) raised_ = err; } + XML_Error RaisedError() { return raised_; } + void Reset(); + + void StartElement(); + void EndElement(); + void StartNamespace(const char * prefix, const char * ns); + + private: + const XmlParser * parser_; + XmlnsStack xmlnsstack_; + XML_Error raised_; + }; + + ParseContext context_; + XML_Parser expat_; + XmlParseHandler * pxph_; + bool sentError_; + + +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc new file mode 100644 index 00000000..892e2ebb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc @@ -0,0 +1,190 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include <string> +#include <iostream> +#include <vector> +#include <sstream> +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +class XmlPrinterImpl { +public: + XmlPrinterImpl(std::ostream * pout, + const std::string * const xmlns, int xmlnsCount); + void PrintElement(const XmlElement * element); + void PrintQuotedValue(const std::string & text); + void PrintBodyText(const std::string & text); + +private: + std::ostream *pout_; + XmlnsStack xmlnsStack_; +}; + +void +XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) { + PrintXml(pout, element, NULL, 0); +} + +void +XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element, + const std::string * const xmlns, int xmlnsCount) { + XmlPrinterImpl printer(pout, xmlns, xmlnsCount); + printer.PrintElement(element); +} + +XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout, + const std::string * const xmlns, int xmlnsCount) : + pout_(pout), + xmlnsStack_() { + int i; + for (i = 0; i < xmlnsCount; i += 2) { + xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]); + } +} + +void +XmlPrinterImpl::PrintElement(const XmlElement * element) { + xmlnsStack_.PushFrame(); + + // first go through attrs of pel to add xmlns definitions + const XmlAttr * pattr; + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + if (pattr->Name() == QN_XMLNS) + xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value()); + else if (pattr->Name().Namespace() == NS_XMLNS) + xmlnsStack_.AddXmlns(pattr->Name().LocalPart(), + pattr->Value()); + } + + // then go through qnames to make sure needed xmlns definitons are added + std::vector<std::string> newXmlns; + std::pair<std::string, bool> prefix; + prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false); + if (prefix.second) { + newXmlns.push_back(prefix.first); + newXmlns.push_back(element->Name().Namespace()); + } + + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true); + if (prefix.second) { + newXmlns.push_back(prefix.first); + newXmlns.push_back(element->Name().Namespace()); + } + } + + // print the element name + *pout_ << '<' << xmlnsStack_.FormatQName(element->Name(), false); + + // and the attributes + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + *pout_ << ' ' << xmlnsStack_.FormatQName(pattr->Name(), true) << "=\""; + PrintQuotedValue(pattr->Value()); + *pout_ << '"'; + } + + // and the extra xmlns declarations + std::vector<std::string>::iterator i(newXmlns.begin()); + while (i < newXmlns.end()) { + if (*i == STR_EMPTY) + *pout_ << " xmlns=\"" << *(i + 1) << '"'; + else + *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"'; + i += 2; + } + + // now the children + const XmlChild * pchild = element->FirstChild(); + + if (pchild == NULL) + *pout_ << "/>"; + else { + *pout_ << '>'; + while (pchild) { + if (pchild->IsText()) + PrintBodyText(pchild->AsText()->Text()); + else + PrintElement(pchild->AsElement()); + pchild = pchild->NextChild(); + } + *pout_ << "</" << xmlnsStack_.FormatQName(element->Name(), false) << '>'; + } + + xmlnsStack_.PopFrame(); +} + +void +XmlPrinterImpl::PrintQuotedValue(const std::string & text) { + size_t safe = 0; + for (;;) { + size_t unsafe = text.find_first_of("<>&\"", safe); + if (unsafe == std::string::npos) + unsafe = text.length(); + *pout_ << text.substr(safe, unsafe - safe); + if (unsafe == text.length()) + return; + switch (text[unsafe]) { + case '<': *pout_ << "<"; break; + case '>': *pout_ << ">"; break; + case '&': *pout_ << "&"; break; + case '"': *pout_ << """; break; + } + safe = unsafe + 1; + if (safe == text.length()) + return; + } +} + +void +XmlPrinterImpl::PrintBodyText(const std::string & text) { + size_t safe = 0; + for (;;) { + size_t unsafe = text.find_first_of("<>&", safe); + if (unsafe == std::string::npos) + unsafe = text.length(); + *pout_ << text.substr(safe, unsafe - safe); + if (unsafe == text.length()) + return; + switch (text[unsafe]) { + case '<': *pout_ << "<"; break; + case '>': *pout_ << ">"; break; + case '&': *pout_ << "&"; break; + } + safe = unsafe + 1; + if (safe == text.length()) + return; + } +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h new file mode 100644 index 00000000..96900d0d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h @@ -0,0 +1,49 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmlprinter_h_ +#define _xmlprinter_h_ + +#include <iosfwd> +#include <string> +#include "talk/base/scoped_ptr.h" + +namespace buzz { + +class XmlElement; + +class XmlPrinter { +public: + static void PrintXml(std::ostream * pout, const XmlElement * pelt); + + static void PrintXml(std::ostream * pout, const XmlElement * pelt, + const std::string * const xmlns, int xmlnsCount); +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am new file mode 100644 index 00000000..527f7053 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am @@ -0,0 +1,34 @@ +## Does not compile with final +KDE_OPTIONS = nofinal + +libcricketxmpp_la_SOURCES = constants.cc \ + jid.cc \ + saslmechanism.cc \ + xmppclient.cc \ + xmppengineimpl.cc \ + xmppengineimpl_iq.cc \ + xmpplogintask.cc \ + xmppstanzaparser.cc \ + xmpptask.cc + +noinst_HEADERS = asyncsocket.h \ + prexmppauth.h \ + saslhandler.h \ + xmpplogintask.h \ + jid.h \ + saslmechanism.h \ + xmppclient.h \ + xmpppassword.h \ + constants.h \ + saslplainmechanism.h \ + xmppclientsettings.h \ + xmppstanzaparser.h \ + xmppengine.h \ + xmpptask.h \ + plainsaslhandler.h \ + saslcookiemechanism.h \ + xmppengineimpl.h + + +AM_CPPFLAGS = -DPOSIX -I$(srcdir)/../.. +noinst_LTLIBRARIES = libcricketxmpp.la diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h new file mode 100644 index 00000000..fd91929b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ASYNCSOCKET_H_ +#define _ASYNCSOCKET_H_ + +#include "talk/base/sigslot.h" + +namespace cricket { + class SocketAddress; +} + +namespace buzz { + +class AsyncSocket { +public: + enum State { + STATE_CLOSED = 0, //!< Socket is not open. + STATE_CLOSING, //!< Socket is closing but can have buffered data + STATE_CONNECTING, //!< In the process of + STATE_OPEN, //!< Socket is connected +#if defined(FEATURE_ENABLE_SSL) + STATE_TLS_CONNECTING, //!< Establishing TLS connection + STATE_TLS_OPEN, //!< TLS connected +#endif + }; + + enum Error { + ERROR_NONE = 0, //!< No error + ERROR_WINSOCK, //!< Winsock error + ERROR_DNS, //!< Couldn't resolve host name + ERROR_WRONGSTATE, //!< Call made while socket is in the wrong state +#if defined(FEATURE_ENABLE_SSL) + ERROR_SSL, //!< Something went wrong with OpenSSL +#endif + }; + + virtual ~AsyncSocket() {} + virtual State state() = 0; + virtual Error error() = 0; + + virtual bool Connect(const cricket::SocketAddress& addr) = 0; + virtual bool Read(char * data, size_t len, size_t* len_read) = 0; + virtual bool Write(const char * data, size_t len) = 0; + virtual bool Close() = 0; +#if defined(FEATURE_ENABLE_SSL) + // We allow matching any passed domain. + // If both names are passed as empty, we do not require a match. + virtual bool StartTls(const std::string & domainname) = 0; +#endif + + sigslot::signal0<> SignalConnected; + sigslot::signal0<> SignalSSLConnected; + sigslot::signal0<> SignalClosed; + sigslot::signal0<> SignalRead; + sigslot::signal0<> SignalError; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc new file mode 100644 index 00000000..b2c833f7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc @@ -0,0 +1,331 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <string> +#include "talk/base/basicdefs.h" +#include "talk/xmllite/xmlconstants.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" +namespace buzz { + +const Jid JID_EMPTY(STR_EMPTY); + +const std::string & Constants::ns_client() { + static const std::string ns_client_("jabber:client"); + return ns_client_; +} + +const std::string & Constants::ns_server() { + static const std::string ns_server_("jabber:server"); + return ns_server_; +} + +const std::string & Constants::ns_stream() { + static const std::string ns_stream_("http://etherx.jabber.org/streams"); + return ns_stream_; +} + +const std::string & Constants::ns_xstream() { + static const std::string ns_xstream_("urn:ietf:params:xml:ns:xmpp-streams"); + return ns_xstream_; +} + +const std::string & Constants::ns_tls() { + static const std::string ns_tls_("urn:ietf:params:xml:ns:xmpp-tls"); + return ns_tls_; +} + +const std::string & Constants::ns_sasl() { + static const std::string ns_sasl_("urn:ietf:params:xml:ns:xmpp-sasl"); + return ns_sasl_; +} + +const std::string & Constants::ns_bind() { + static const std::string ns_bind_("urn:ietf:params:xml:ns:xmpp-bind"); + return ns_bind_; +} + +const std::string & Constants::ns_dialback() { + static const std::string ns_dialback_("jabber:server:dialback"); + return ns_dialback_; +} + +const std::string & Constants::ns_session() { + static const std::string ns_session_("urn:ietf:params:xml:ns:xmpp-session"); + return ns_session_; +} + +const std::string & Constants::ns_stanza() { + static const std::string ns_stanza_("urn:ietf:params:xml:ns:xmpp-stanzas"); + return ns_stanza_; +} + +const std::string & Constants::ns_privacy() { + static const std::string ns_privacy_("jabber:iq:privacy"); + return ns_privacy_; +} + +const std::string & Constants::ns_roster() { + static const std::string ns_roster_("jabber:iq:roster"); + return ns_roster_; +} + +const std::string & Constants::ns_vcard() { + static const std::string ns_vcard_("vcard-temp"); + return ns_vcard_; +} + +const std::string & Constants::str_client() { + static const std::string str_client_("client"); + return str_client_; +} + +const std::string & Constants::str_server() { + static const std::string str_server_("server"); + return str_server_; +} + +const std::string & Constants::str_stream() { + static const std::string str_stream_("stream"); + return str_stream_; +} + +const std::string STR_GET("get"); +const std::string STR_SET("set"); +const std::string STR_RESULT("result"); +const std::string STR_ERROR("error"); + +const std::string STR_FROM("from"); +const std::string STR_TO("to"); +const std::string STR_BOTH("both"); +const std::string STR_REMOVE("remove"); + +const std::string STR_UNAVAILABLE("unavailable"); +const std::string STR_INVISIBLE("invisible"); + +const std::string STR_GOOGLE_COM("google.com"); +const std::string STR_GMAIL_COM("gmail.com"); +const std::string STR_GOOGLEMAIL_COM("googlemail.com"); +const std::string STR_DEFAULT_DOMAIN("default.talk.google.com"); +const std::string STR_X("x"); + +const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM); +const QName QN_STREAM_FEATURES(true, NS_STREAM, "features"); +const QName QN_STREAM_ERROR(true, NS_STREAM, "error"); + +const QName QN_XSTREAM_BAD_FORMAT(true, NS_XSTREAM, "bad-format"); +const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX(true, NS_XSTREAM, "bad-namespace-prefix"); +const QName QN_XSTREAM_CONFLICT(true, NS_XSTREAM, "conflict"); +const QName QN_XSTREAM_CONNECTION_TIMEOUT(true, NS_XSTREAM, "connection-timeout"); +const QName QN_XSTREAM_HOST_GONE(true, NS_XSTREAM, "host-gone"); +const QName QN_XSTREAM_HOST_UNKNOWN(true, NS_XSTREAM, "host-unknown"); +const QName QN_XSTREAM_IMPROPER_ADDRESSIING(true, NS_XSTREAM, "improper-addressing"); +const QName QN_XSTREAM_INTERNAL_SERVER_ERROR(true, NS_XSTREAM, "internal-server-error"); +const QName QN_XSTREAM_INVALID_FROM(true, NS_XSTREAM, "invalid-from"); +const QName QN_XSTREAM_INVALID_ID(true, NS_XSTREAM, "invalid-id"); +const QName QN_XSTREAM_INVALID_NAMESPACE(true, NS_XSTREAM, "invalid-namespace"); +const QName QN_XSTREAM_INVALID_XML(true, NS_XSTREAM, "invalid-xml"); +const QName QN_XSTREAM_NOT_AUTHORIZED(true, NS_XSTREAM, "not-authorized"); +const QName QN_XSTREAM_POLICY_VIOLATION(true, NS_XSTREAM, "policy-violation"); +const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED(true, NS_XSTREAM, "remote-connection-failed"); +const QName QN_XSTREAM_RESOURCE_CONSTRAINT(true, NS_XSTREAM, "resource-constraint"); +const QName QN_XSTREAM_RESTRICTED_XML(true, NS_XSTREAM, "restricted-xml"); +const QName QN_XSTREAM_SEE_OTHER_HOST(true, NS_XSTREAM, "see-other-host"); +const QName QN_XSTREAM_SYSTEM_SHUTDOWN(true, NS_XSTREAM, "system-shutdown"); +const QName QN_XSTREAM_UNDEFINED_CONDITION(true, NS_XSTREAM, "undefined-condition"); +const QName QN_XSTREAM_UNSUPPORTED_ENCODING(true, NS_XSTREAM, "unsupported-encoding"); +const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE(true, NS_XSTREAM, "unsupported-stanza-type"); +const QName QN_XSTREAM_UNSUPPORTED_VERSION(true, NS_XSTREAM, "unsupported-version"); +const QName QN_XSTREAM_XML_NOT_WELL_FORMED(true, NS_XSTREAM, "xml-not-well-formed"); +const QName QN_XSTREAM_TEXT(true, NS_XSTREAM, "text"); + +const QName QN_TLS_STARTTLS(true, NS_TLS, "starttls"); +const QName QN_TLS_REQUIRED(true, NS_TLS, "required"); +const QName QN_TLS_PROCEED(true, NS_TLS, "proceed"); +const QName QN_TLS_FAILURE(true, NS_TLS, "failure"); + +const QName QN_SASL_MECHANISMS(true, NS_SASL, "mechanisms"); +const QName QN_SASL_MECHANISM(true, NS_SASL, "mechanism"); +const QName QN_SASL_AUTH(true, NS_SASL, "auth"); +const QName QN_SASL_CHALLENGE(true, NS_SASL, "challenge"); +const QName QN_SASL_RESPONSE(true, NS_SASL, "response"); +const QName QN_SASL_ABORT(true, NS_SASL, "abort"); +const QName QN_SASL_SUCCESS(true, NS_SASL, "success"); +const QName QN_SASL_FAILURE(true, NS_SASL, "failure"); +const QName QN_SASL_ABORTED(true, NS_SASL, "aborted"); +const QName QN_SASL_INCORRECT_ENCODING(true, NS_SASL, "incorrect-encoding"); +const QName QN_SASL_INVALID_AUTHZID(true, NS_SASL, "invalid-authzid"); +const QName QN_SASL_INVALID_MECHANISM(true, NS_SASL, "invalid-mechanism"); +const QName QN_SASL_MECHANISM_TOO_WEAK(true, NS_SASL, "mechanism-too-weak"); +const QName QN_SASL_NOT_AUTHORIZED(true, NS_SASL, "not-authorized"); +const QName QN_SASL_TEMPORARY_AUTH_FAILURE(true, NS_SASL, "temporary-auth-failure"); + +const QName QN_DIALBACK_RESULT(true, NS_DIALBACK, "result"); +const QName QN_DIALBACK_VERIFY(true, NS_DIALBACK, "verify"); + +const QName QN_STANZA_BAD_REQUEST(true, NS_STANZA, "bad-request"); +const QName QN_STANZA_CONFLICT(true, NS_STANZA, "conflict"); +const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED(true, NS_STANZA, "feature-not-implemented"); +const QName QN_STANZA_FORBIDDEN(true, NS_STANZA, "forbidden"); +const QName QN_STANZA_GONE(true, NS_STANZA, "gone"); +const QName QN_STANZA_INTERNAL_SERVER_ERROR(true, NS_STANZA, "internal-server-error"); +const QName QN_STANZA_ITEM_NOT_FOUND(true, NS_STANZA, "item-not-found"); +const QName QN_STANZA_JID_MALFORMED(true, NS_STANZA, "jid-malformed"); +const QName QN_STANZA_NOT_ACCEPTABLE(true, NS_STANZA, "not-acceptable"); +const QName QN_STANZA_NOT_ALLOWED(true, NS_STANZA, "not-allowed"); +const QName QN_STANZA_PAYMENT_REQUIRED(true, NS_STANZA, "payment-required"); +const QName QN_STANZA_RECIPIENT_UNAVAILABLE(true, NS_STANZA, "recipient-unavailable"); +const QName QN_STANZA_REDIRECT(true, NS_STANZA, "redirect"); +const QName QN_STANZA_REGISTRATION_REQUIRED(true, NS_STANZA, "registration-required"); +const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND(true, NS_STANZA, "remote-server-not-found"); +const QName QN_STANZA_REMOTE_SERVER_TIMEOUT(true, NS_STANZA, "remote-server-timeout"); +const QName QN_STANZA_RESOURCE_CONSTRAINT(true, NS_STANZA, "resource-constraint"); +const QName QN_STANZA_SERVICE_UNAVAILABLE(true, NS_STANZA, "service-unavailable"); +const QName QN_STANZA_SUBSCRIPTION_REQUIRED(true, NS_STANZA, "subscription-required"); +const QName QN_STANZA_UNDEFINED_CONDITION(true, NS_STANZA, "undefined-condition"); +const QName QN_STANZA_UNEXPECTED_REQUEST(true, NS_STANZA, "unexpected-request"); +const QName QN_STANZA_TEXT(true, NS_STANZA, "text"); + +const QName QN_BIND_BIND(true, NS_BIND, "bind"); +const QName QN_BIND_RESOURCE(true, NS_BIND, "resource"); +const QName QN_BIND_JID(true, NS_BIND, "jid"); + +const QName QN_MESSAGE(true, NS_CLIENT, "message"); +const QName QN_BODY(true, NS_CLIENT, "body"); +const QName QN_SUBJECT(true, NS_CLIENT, "subject"); +const QName QN_THREAD(true, NS_CLIENT, "thread"); +const QName QN_PRESENCE(true, NS_CLIENT, "presence"); +const QName QN_SHOW(true, NS_CLIENT, "show"); +const QName QN_STATUS(true, NS_CLIENT, "status"); +const QName QN_LANG(true, NS_CLIENT, "lang"); +const QName QN_PRIORITY(true, NS_CLIENT, "priority"); +const QName QN_IQ(true, NS_CLIENT, "iq"); +const QName QN_ERROR(true, NS_CLIENT, "error"); + +const QName QN_SERVER_MESSAGE(true, NS_SERVER, "message"); +const QName QN_SERVER_BODY(true, NS_SERVER, "body"); +const QName QN_SERVER_SUBJECT(true, NS_SERVER, "subject"); +const QName QN_SERVER_THREAD(true, NS_SERVER, "thread"); +const QName QN_SERVER_PRESENCE(true, NS_SERVER, "presence"); +const QName QN_SERVER_SHOW(true, NS_SERVER, "show"); +const QName QN_SERVER_STATUS(true, NS_SERVER, "status"); +const QName QN_SERVER_LANG(true, NS_SERVER, "lang"); +const QName QN_SERVER_PRIORITY(true, NS_SERVER, "priority"); +const QName QN_SERVER_IQ(true, NS_SERVER, "iq"); +const QName QN_SERVER_ERROR(true, NS_SERVER, "error"); + +const QName QN_SESSION_SESSION(true, NS_SESSION, "session"); + +const QName QN_PRIVACY_QUERY(true, NS_PRIVACY, "query"); +const QName QN_PRIVACY_ACTIVE(true, NS_PRIVACY, "active"); +const QName QN_PRIVACY_DEFAULT(true, NS_PRIVACY, "default"); +const QName QN_PRIVACY_LIST(true, NS_PRIVACY, "list"); +const QName QN_PRIVACY_ITEM(true, NS_PRIVACY, "item"); +const QName QN_PRIVACY_IQ(true, NS_PRIVACY, "iq"); +const QName QN_PRIVACY_MESSAGE(true, NS_PRIVACY, "message"); +const QName QN_PRIVACY_PRESENCE_IN(true, NS_PRIVACY, "presence-in"); +const QName QN_PRIVACY_PRESENCE_OUT(true, NS_PRIVACY, "presence-out"); + +const QName QN_ROSTER_QUERY(true, NS_ROSTER, "query"); +const QName QN_ROSTER_ITEM(true, NS_ROSTER, "item"); +const QName QN_ROSTER_GROUP(true, NS_ROSTER, "group"); + +const QName QN_VCARD_QUERY(true, NS_VCARD, "vCard"); +const QName QN_VCARD_FN(true, NS_VCARD, "FN"); + +const QName QN_XML_LANG(true, NS_XML, "lang"); + +const std::string STR_TYPE("type"); +const std::string STR_ID("id"); +const std::string STR_NAME("name"); +const std::string STR_JID("jid"); +const std::string STR_SUBSCRIPTION("subscription"); +const std::string STR_ASK("ask"); + +const QName QN_ENCODING(true, STR_EMPTY, STR_ENCODING); +const QName QN_VERSION(true, STR_EMPTY, STR_VERSION); +const QName QN_TO(true, STR_EMPTY, "to"); +const QName QN_FROM(true, STR_EMPTY, "from"); +const QName QN_TYPE(true, STR_EMPTY, "type"); +const QName QN_ID(true, STR_EMPTY, "id"); +const QName QN_CODE(true, STR_EMPTY, "code"); +const QName QN_NAME(true, STR_EMPTY, "name"); +const QName QN_VALUE(true, STR_EMPTY, "value"); +const QName QN_ACTION(true, STR_EMPTY, "action"); +const QName QN_ORDER(true, STR_EMPTY, "order"); +const QName QN_MECHANISM(true, STR_EMPTY, "mechanism"); +const QName QN_ASK(true, STR_EMPTY, "ask"); +const QName QN_JID(true, STR_EMPTY, "jid"); +const QName QN_SUBSCRIPTION(true, STR_EMPTY, "subscription"); +const QName QN_SOURCE(true, STR_EMPTY, "source"); + +const QName QN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT); +const QName QN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER); +const QName QN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM); + +// Presence +const std::string STR_SHOW_AWAY("away"); +const std::string STR_SHOW_CHAT("chat"); +const std::string STR_SHOW_DND("dnd"); +const std::string STR_SHOW_XA("xa"); + +// Subscription +const std::string STR_SUBSCRIBE("subscribe"); +const std::string STR_SUBSCRIBED("subscribed"); +const std::string STR_UNSUBSCRIBE("unsubscribe"); +const std::string STR_UNSUBSCRIBED("unsubscribed"); + + +// JEP 0030 +const QName QN_NODE(true, STR_EMPTY, "node"); +const QName QN_CATEGORY(true, STR_EMPTY, "category"); +const QName QN_VAR(true, STR_EMPTY, "var"); +const std::string NS_DISCO_INFO("http://jabber.org/protocol/disco#info"); +const std::string NS_DISCO_ITEMS("http://jabber.org/protocol/disco#items"); +const QName QN_DISCO_INFO_QUERY(true, NS_DISCO_INFO, "query"); +const QName QN_DISCO_IDENTITY(true, NS_DISCO_INFO, "identity"); +const QName QN_DISCO_FEATURE(true, NS_DISCO_INFO, "feature"); + +const QName QN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query"); +const QName QN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item"); + + +// JEP 0115 +const std::string NS_CAPS("http://jabber.org/protocol/caps"); +const QName QN_CAPS_C(true, NS_CAPS, "c"); +const QName QN_VER(true, STR_EMPTY, "ver"); +const QName QN_EXT(true, STR_EMPTY, "ext"); + +// JEP 0091 Delayed Delivery +const std::string kNSDelay("jabber:x:delay"); +const QName kQnDelayX(true, kNSDelay, "x"); +const QName kQnStamp(true, STR_EMPTY, "stamp"); + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h new file mode 100644 index 00000000..b05af965 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h @@ -0,0 +1,300 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_ +#define _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_ + +#include <string> +#include "talk/xmllite/qname.h" +#include "talk/xmpp/jid.h" + + +#define NS_CLIENT Constants::ns_client() +#define NS_SERVER Constants::ns_server() +#define NS_STREAM Constants::ns_stream() +#define NS_XSTREAM Constants::ns_xstream() +#define NS_TLS Constants::ns_tls() +#define NS_SASL Constants::ns_sasl() +#define NS_BIND Constants::ns_bind() +#define NS_DIALBACK Constants::ns_dialback() +#define NS_SESSION Constants::ns_session() +#define NS_STANZA Constants::ns_stanza() +#define NS_PRIVACY Constants::ns_privacy() +#define NS_ROSTER Constants::ns_roster() +#define NS_VCARD Constants::ns_vcard() +#define STR_CLIENT Constants::str_client() +#define STR_SERVER Constants::str_server() +#define STR_STREAM Constants::str_stream() + +namespace buzz { + +extern const Jid JID_EMPTY; + +class Constants { + public: + static const std::string & ns_client(); + static const std::string & ns_server(); + static const std::string & ns_stream(); + static const std::string & ns_xstream(); + static const std::string & ns_tls(); + static const std::string & ns_sasl(); + static const std::string & ns_bind(); + static const std::string & ns_dialback(); + static const std::string & ns_session(); + static const std::string & ns_stanza(); + static const std::string & ns_privacy(); + static const std::string & ns_roster(); + static const std::string & ns_vcard(); + + static const std::string & str_client(); + static const std::string & str_server(); + static const std::string & str_stream(); +}; + +extern const std::string STR_GET; +extern const std::string STR_SET; +extern const std::string STR_RESULT; +extern const std::string STR_ERROR; + +extern const std::string STR_FROM; +extern const std::string STR_TO; +extern const std::string STR_BOTH; +extern const std::string STR_REMOVE; + +extern const std::string STR_MESSAGE; +extern const std::string STR_BODY; +extern const std::string STR_PRESENCE; +extern const std::string STR_STATUS; +extern const std::string STR_SHOW; +extern const std::string STR_PRIOIRTY; +extern const std::string STR_IQ; + +extern const std::string STR_TYPE; +extern const std::string STR_NAME; +extern const std::string STR_ID; +extern const std::string STR_JID; +extern const std::string STR_SUBSCRIPTION; +extern const std::string STR_ASK; +extern const std::string STR_X; +extern const std::string STR_GOOGLE_COM; +extern const std::string STR_GMAIL_COM; +extern const std::string STR_GOOGLEMAIL_COM; +extern const std::string STR_DEFAULT_DOMAIN; + +extern const std::string STR_UNAVAILABLE; +extern const std::string STR_INVISIBLE; + +extern const QName QN_STREAM_STREAM; +extern const QName QN_STREAM_FEATURES; +extern const QName QN_STREAM_ERROR; + +extern const QName QN_XSTREAM_BAD_FORMAT; +extern const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX; +extern const QName QN_XSTREAM_CONFLICT; +extern const QName QN_XSTREAM_CONNECTION_TIMEOUT; +extern const QName QN_XSTREAM_HOST_GONE; +extern const QName QN_XSTREAM_HOST_UNKNOWN; +extern const QName QN_XSTREAM_IMPROPER_ADDRESSIING; +extern const QName QN_XSTREAM_INTERNAL_SERVER_ERROR; +extern const QName QN_XSTREAM_INVALID_FROM; +extern const QName QN_XSTREAM_INVALID_ID; +extern const QName QN_XSTREAM_INVALID_NAMESPACE; +extern const QName QN_XSTREAM_INVALID_XML; +extern const QName QN_XSTREAM_NOT_AUTHORIZED; +extern const QName QN_XSTREAM_POLICY_VIOLATION; +extern const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED; +extern const QName QN_XSTREAM_RESOURCE_CONSTRAINT; +extern const QName QN_XSTREAM_RESTRICTED_XML; +extern const QName QN_XSTREAM_SEE_OTHER_HOST; +extern const QName QN_XSTREAM_SYSTEM_SHUTDOWN; +extern const QName QN_XSTREAM_UNDEFINED_CONDITION; +extern const QName QN_XSTREAM_UNSUPPORTED_ENCODING; +extern const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE; +extern const QName QN_XSTREAM_UNSUPPORTED_VERSION; +extern const QName QN_XSTREAM_XML_NOT_WELL_FORMED; +extern const QName QN_XSTREAM_TEXT; + +extern const QName QN_TLS_STARTTLS; +extern const QName QN_TLS_REQUIRED; +extern const QName QN_TLS_PROCEED; +extern const QName QN_TLS_FAILURE; + +extern const QName QN_SASL_MECHANISMS; +extern const QName QN_SASL_MECHANISM; +extern const QName QN_SASL_AUTH; +extern const QName QN_SASL_CHALLENGE; +extern const QName QN_SASL_RESPONSE; +extern const QName QN_SASL_ABORT; +extern const QName QN_SASL_SUCCESS; +extern const QName QN_SASL_FAILURE; +extern const QName QN_SASL_ABORTED; +extern const QName QN_SASL_INCORRECT_ENCODING; +extern const QName QN_SASL_INVALID_AUTHZID; +extern const QName QN_SASL_INVALID_MECHANISM; +extern const QName QN_SASL_MECHANISM_TOO_WEAK; +extern const QName QN_SASL_NOT_AUTHORIZED; +extern const QName QN_SASL_TEMPORARY_AUTH_FAILURE; + +extern const QName QN_DIALBACK_RESULT; +extern const QName QN_DIALBACK_VERIFY; + +extern const QName QN_STANZA_BAD_REQUEST; +extern const QName QN_STANZA_CONFLICT; +extern const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED; +extern const QName QN_STANZA_FORBIDDEN; +extern const QName QN_STANZA_GONE; +extern const QName QN_STANZA_INTERNAL_SERVER_ERROR; +extern const QName QN_STANZA_ITEM_NOT_FOUND; +extern const QName QN_STANZA_JID_MALFORMED; +extern const QName QN_STANZA_NOT_ACCEPTABLE; +extern const QName QN_STANZA_NOT_ALLOWED; +extern const QName QN_STANZA_PAYMENT_REQUIRED; +extern const QName QN_STANZA_RECIPIENT_UNAVAILABLE; +extern const QName QN_STANZA_REDIRECT; +extern const QName QN_STANZA_REGISTRATION_REQUIRED; +extern const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND; +extern const QName QN_STANZA_REMOTE_SERVER_TIMEOUT; +extern const QName QN_STANZA_RESOURCE_CONSTRAINT; +extern const QName QN_STANZA_SERVICE_UNAVAILABLE; +extern const QName QN_STANZA_SUBSCRIPTION_REQUIRED; +extern const QName QN_STANZA_UNDEFINED_CONDITION; +extern const QName QN_STANZA_UNEXPECTED_REQUEST; +extern const QName QN_STANZA_TEXT; + +extern const QName QN_BIND_BIND; +extern const QName QN_BIND_RESOURCE; +extern const QName QN_BIND_JID; + +extern const QName QN_MESSAGE; +extern const QName QN_BODY; +extern const QName QN_SUBJECT; +extern const QName QN_THREAD; +extern const QName QN_PRESENCE; +extern const QName QN_SHOW; +extern const QName QN_STATUS; +extern const QName QN_LANG; +extern const QName QN_PRIORITY; +extern const QName QN_IQ; +extern const QName QN_ERROR; + +extern const QName QN_SERVER_MESSAGE; +extern const QName QN_SERVER_BODY; +extern const QName QN_SERVER_SUBJECT; +extern const QName QN_SERVER_THREAD; +extern const QName QN_SERVER_PRESENCE; +extern const QName QN_SERVER_SHOW; +extern const QName QN_SERVER_STATUS; +extern const QName QN_SERVER_LANG; +extern const QName QN_SERVER_PRIORITY; +extern const QName QN_SERVER_IQ; +extern const QName QN_SERVER_ERROR; + +extern const QName QN_SESSION_SESSION; + +extern const QName QN_PRIVACY_QUERY; +extern const QName QN_PRIVACY_ACTIVE; +extern const QName QN_PRIVACY_DEFAULT; +extern const QName QN_PRIVACY_LIST; +extern const QName QN_PRIVACY_ITEM; +extern const QName QN_PRIVACY_IQ; +extern const QName QN_PRIVACY_MESSAGE; +extern const QName QN_PRIVACY_PRESENCE_IN; +extern const QName QN_PRIVACY_PRESENCE_OUT; + +extern const QName QN_ROSTER_QUERY; +extern const QName QN_ROSTER_ITEM; +extern const QName QN_ROSTER_GROUP; + +extern const QName QN_VCARD_QUERY; +extern const QName QN_VCARD_FN; + +extern const QName QN_XML_LANG; + +extern const QName QN_ENCODING; +extern const QName QN_VERSION; +extern const QName QN_TO; +extern const QName QN_FROM; +extern const QName QN_TYPE; +extern const QName QN_ID; +extern const QName QN_CODE; +extern const QName QN_NAME; +extern const QName QN_VALUE; +extern const QName QN_ACTION; +extern const QName QN_ORDER; +extern const QName QN_MECHANISM; +extern const QName QN_ASK; +extern const QName QN_JID; +extern const QName QN_SUBSCRIPTION; + + +extern const QName QN_XMLNS_CLIENT; +extern const QName QN_XMLNS_SERVER; +extern const QName QN_XMLNS_STREAM; + +// Presence +extern const std::string STR_SHOW_AWAY; +extern const std::string STR_SHOW_CHAT; +extern const std::string STR_SHOW_DND; +extern const std::string STR_SHOW_XA; + +// Subscription +extern const std::string STR_SUBSCRIBE; +extern const std::string STR_SUBSCRIBED; +extern const std::string STR_UNSUBSCRIBE; +extern const std::string STR_UNSUBSCRIBED; + + +// JEP 0030 +extern const QName QN_NODE; +extern const QName QN_CATEGORY; +extern const QName QN_VAR; +extern const std::string NS_DISCO_INFO; +extern const std::string NS_DISCO_ITEMS; + +extern const QName QN_DISCO_INFO_QUERY; +extern const QName QN_DISCO_IDENTITY; +extern const QName QN_DISCO_FEATURE; + +extern const QName QN_DISCO_ITEMS_QUERY; +extern const QName QN_DISCO_ITEM; + + +// JEP 0115 +extern const std::string NS_CAPS; +extern const QName QN_CAPS_C; +extern const QName QN_VER; +extern const QName QN_EXT; + + +// JEP 0091 Delayed Delivery +extern const std::string kNSDelay; +extern const QName kQnDelayX; +extern const QName kQnStamp; + +} + +#endif // _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc new file mode 100644 index 00000000..b742e03a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc @@ -0,0 +1,477 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +extern "C" { +#include <ctype.h> +} +#include <string> +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" +#include "talk/base/common.h" +#include <algorithm> + +#define new TRACK_NEW + +namespace buzz { + +static int AsciiToLower(int x) { + return (x <= 'Z' && x >= 'A') ? (x + ('a' - 'A')) : x; +} + +Jid::Jid() : data_(NULL) { +} + +Jid::Jid(bool is_special, const std::string & special) { + data_ = is_special ? new Data(special, STR_EMPTY, STR_EMPTY) : NULL; +} + +Jid::Jid(const std::string & jid_string) { + if (jid_string == STR_EMPTY) { + data_ = NULL; + return; + } + + // First find the slash and slice of that part + size_t slash = jid_string.find('/'); + std::string resource_name = (slash == std::string::npos ? STR_EMPTY : + jid_string.substr(slash + 1)); + + // Now look for the node + std::string node_name; + size_t at = jid_string.find('@'); + size_t domain_begin; + if (at < slash && at != std::string::npos) { + node_name = jid_string.substr(0, at); + domain_begin = at + 1; + } else { + domain_begin = 0; + } + + // Now take what is left as the domain + size_t domain_length = + ( slash == std::string::npos + ? jid_string.length() - domain_begin + : slash - domain_begin); + + // avoid allocating these constants repeatedly + std::string domain_name; + + if (domain_length == 9 && jid_string.find("gmail.com", domain_begin) == domain_begin) { + domain_name = STR_GMAIL_COM; + } + else if (domain_length == 14 && jid_string.find("googlemail.com", domain_begin) == domain_begin) { + domain_name = STR_GOOGLEMAIL_COM; + } + else if (domain_length == 10 && jid_string.find("google.com", domain_begin) == domain_begin) { + domain_name = STR_GOOGLE_COM; + } + else { + domain_name = jid_string.substr(domain_begin, domain_length); + } + + // If the domain is empty we have a non-valid jid and we should empty + // everything else out + if (domain_name.empty()) { + data_ = NULL; + return; + } + + bool valid_node; + std::string validated_node = prepNode(node_name, + node_name.begin(), node_name.end(), &valid_node); + bool valid_domain; + std::string validated_domain = prepDomain(domain_name, + domain_name.begin(), domain_name.end(), &valid_domain); + bool valid_resource; + std::string validated_resource = prepResource(resource_name, + resource_name.begin(), resource_name.end(), &valid_resource); + + if (!valid_node || !valid_domain || !valid_resource) { + data_ = NULL; + return; + } + + data_ = new Data(validated_node, validated_domain, validated_resource); +} + +Jid::Jid(const std::string & node_name, + const std::string & domain_name, + const std::string & resource_name) { + if (domain_name.empty()) { + data_ = NULL; + return; + } + + bool valid_node; + std::string validated_node = prepNode(node_name, + node_name.begin(), node_name.end(), &valid_node); + bool valid_domain; + std::string validated_domain = prepDomain(domain_name, + domain_name.begin(), domain_name.end(), &valid_domain); + bool valid_resource; + std::string validated_resource = prepResource(resource_name, + resource_name.begin(), resource_name.end(), &valid_resource); + + if (!valid_node || !valid_domain || !valid_resource) { + data_ = NULL; + return; + } + + data_ = new Data(validated_node, validated_domain, validated_resource); +} + +std::string Jid::Str() const { + if (!IsValid()) + return STR_EMPTY; + + std::string ret; + + if (!data_->node_name_.empty()) + ret = data_->node_name_ + "@"; + + ASSERT(data_->domain_name_ != STR_EMPTY); + ret += data_->domain_name_; + + if (!data_->resource_name_.empty()) + ret += "/" + data_->resource_name_; + + return ret; +} + +bool +Jid::IsValid() const { + return data_ != NULL && !data_->domain_name_.empty(); +} + +bool +Jid::IsBare() const { + return IsValid() && + data_->resource_name_.empty(); +} + +bool +Jid::IsFull() const { + return IsValid() && + !data_->resource_name_.empty(); +} + +Jid +Jid::BareJid() const { + if (!IsValid()) + return Jid(); + if (!IsFull()) + return *this; + return Jid(data_->node_name_, data_->domain_name_, STR_EMPTY); +} + +#if 0 +void +Jid::set_node(const std::string & node_name) { + data_->node_name_ = node_name; +} +void +Jid::set_domain(const std::string & domain_name) { + data_->domain_name_ = domain_name; +} +void +Jid::set_resource(const std::string & res_name) { + data_->resource_name_ = res_name; +} +#endif + +bool +Jid::BareEquals(const Jid & other) const { + return (other.data_ == data_ || + data_ != NULL && + other.data_ != NULL && + other.data_->node_name_ == data_->node_name_ && + other.data_->domain_name_ == data_->domain_name_); +} + +bool +Jid::operator==(const Jid & other) const { + return (other.data_ == data_ || + data_ != NULL && + other.data_ != NULL && + other.data_->node_name_ == data_->node_name_ && + other.data_->domain_name_ == data_->domain_name_ && + other.data_->resource_name_ == data_->resource_name_); +} + +int +Jid::Compare(const Jid & other) const { + if (other.data_ == data_) + return 0; + if (data_ == NULL) + return -1; + if (other.data_ == NULL) + return 1; + + int compare_result; + compare_result = data_->node_name_.compare(other.data_->node_name_); + if (0 != compare_result) + return compare_result; + compare_result = data_->domain_name_.compare(other.data_->domain_name_); + if (0 != compare_result) + return compare_result; + compare_result = data_->resource_name_.compare(other.data_->resource_name_); + return compare_result; +} + + +// --- JID parsing code: --- + +// Checks and normalizes the node part of a JID. +std::string +Jid::prepNode(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + char ch = *i; + if (ch <= 0x7F) { + result += prepNodeAscii(ch, &char_valid); + } + else { + // TODO: implement the correct stringprep protocol for these + result += tolower(ch); + } + if (!char_valid) { + return STR_EMPTY; + } + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + + +// Returns the appropriate mapping for an ASCII character in a node. +char +Jid::prepNodeAscii(char ch, bool *valid) { + *valid = true; + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + return (char)(ch + ('a' - 'A')); + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case ' ': case '&': case '/': case ':': case '<': case '>': case '@': + case '\"': case '\'': + case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + + +// Checks and normalizes the resource part of a JID. +std::string +Jid::prepResource(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + char ch = *i; + if (ch <= 0x7F) { + result += prepResourceAscii(ch, &char_valid); + } + else { + // TODO: implement the correct stringprep protocol for these + result += ch; + } + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + +// Returns the appropriate mapping for an ASCII character in a resource. +char +Jid::prepResourceAscii(char ch, bool *valid) { + *valid = true; + switch (ch) { + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + +// Checks and normalizes the domain part of a JID. +std::string +Jid::prepDomain(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + // TODO: if the domain contains a ':', then we should parse it + // as an IPv6 address rather than giving an error about illegal domain. + prepDomain(str, start, end, &result, valid); + if (!*valid) { + return STR_EMPTY; + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + + +// Checks and normalizes an IDNA domain. +void +Jid::prepDomain(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, std::string *buf, bool *valid) { + *valid = false; + std::string::const_iterator last = start; + for (std::string::const_iterator i = start; i < end; i++) { + bool label_valid = true; + char ch = *i; + switch (ch) { + case 0x002E: +#if 0 // FIX: This isn't UTF-8-aware. + case 0x3002: + case 0xFF0E: + case 0xFF61: +#endif + prepDomainLabel(str, last, i, buf, &label_valid); + *buf += '.'; + last = i + 1; + break; + } + if (!label_valid) { + return; + } + } + prepDomainLabel(str, last, end, buf, valid); +} + +// Checks and normalizes a domain label. +void +Jid::prepDomainLabel(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, std::string *buf, bool *valid) { + *valid = false; + + int startLen = buf->length(); + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + char ch = *i; + if (ch <= 0x7F) { + *buf += prepDomainLabelAscii(ch, &char_valid); + } + else { + // TODO: implement ToASCII for these + *buf += ch; + } + if (!char_valid) { + return; + } + } + + int count = buf->length() - startLen; + if (count == 0) { + return; + } + else if (count > 63) { + return; + } + + // Is this check needed? See comment in prepDomainLabelAscii. + if ((*buf)[startLen] == '-') { + return; + } + if ((*buf)[buf->length() - 1] == '-') { + return; + } + *valid = true; +} + + +// Returns the appropriate mapping for an ASCII character in a domain label. +char +Jid::prepDomainLabelAscii(char ch, bool *valid) { + *valid = true; + // TODO: A literal reading of the spec seems to say that we do + // not need to check for these illegal characters (an "internationalized + // domain label" runs ToASCII with UseSTD3... set to false). But that + // can't be right. We should at least be checking that there are no '/' + // or '@' characters in the domain. Perhaps we should see what others + // do in this case. + + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + return (char)(ch + ('a' - 'A')); + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: + case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: + case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A: + case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: + case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: + case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h new file mode 100644 index 00000000..ae7944bf --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h @@ -0,0 +1,144 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _jid_h_ +#define _jid_h_ + +#include <string> +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +//! The Jid class encapsulates and provides parsing help for Jids +//! A Jid consists of three parts. The node, the domain and the resource. +//! +//! node@domain/resource +//! +//! The node and resource are both optional. A valid jid is defined to have +//! a domain. A bare jid is defined to not have a resource and a full jid +//! *does* have a resource. +class Jid { +public: + explicit Jid(); + explicit Jid(const std::string & jid_string); + explicit Jid(const std::string & node_name, + const std::string & domain_name, + const std::string & resource_name); + explicit Jid(bool special, const std::string & special_string); + Jid(const Jid & jid) : data_(jid.data_) { + if (data_ != NULL) { + data_->AddRef(); + } + } + Jid & operator=(const Jid & jid) { + if (jid.data_ != NULL) { + jid.data_->AddRef(); + } + if (data_ != NULL) { + data_->Release(); + } + data_ = jid.data_; + return *this; + } + ~Jid() { + if (data_ != NULL) { + data_->Release(); + } + } + + + const std::string & node() const { return !data_ ? STR_EMPTY : data_->node_name_; } + // void set_node(const std::string & node_name); + const std::string & domain() const { return !data_ ? STR_EMPTY : data_->domain_name_; } + // void set_domain(const std::string & domain_name); + const std::string & resource() const { return !data_ ? STR_EMPTY : data_->resource_name_; } + // void set_resource(const std::string & res_name); + + std::string Str() const; + Jid BareJid() const; + + bool IsValid() const; + bool IsBare() const; + bool IsFull() const; + + bool BareEquals(const Jid & other) const; + + bool operator==(const Jid & other) const; + bool operator!=(const Jid & other) const { return !operator==(other); } + + bool operator<(const Jid & other) const { return Compare(other) < 0; }; + bool operator>(const Jid & other) const { return Compare(other) > 0; }; + + int Compare(const Jid & other) const; + + +private: + + static std::string prepNode(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + bool *valid); + static char prepNodeAscii(char ch, bool *valid); + static std::string prepResource(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + bool *valid); + static char prepResourceAscii(char ch, bool *valid); + static std::string prepDomain(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + bool *valid); + static void prepDomain(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + std::string *buf, bool *valid); + static void prepDomainLabel(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + std::string *buf, bool *valid); + static char prepDomainLabelAscii(char ch, bool *valid); + + class Data { + public: + Data() : refcount_(1) {} + Data(const std::string & node, const std::string &domain, const std::string & resource) : + node_name_(node), + domain_name_(domain), + resource_name_(resource), + refcount_(1) {} + const std::string node_name_; + const std::string domain_name_; + const std::string resource_name_; + + void AddRef() { refcount_++; } + void Release() { if (!--refcount_) delete this; } + private: + int refcount_; + }; + + Data * data_; +}; + +} + + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h new file mode 100644 index 00000000..659820f5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h @@ -0,0 +1,80 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PLAINSASLHANDLER_H_ +#define _PLAINSASLHANDLER_H_ + +#include "talk/xmpp/saslhandler.h" +#include <algorithm> + +namespace buzz { + +class PlainSaslHandler : public SaslHandler { +public: + PlainSaslHandler(const Jid & jid, const XmppPassword & password) : + jid_(jid), password_(password) {} + + virtual ~PlainSaslHandler() {} + + // Should pick the best method according to this handler + // returns the empty string if none are suitable + virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) { + + // Do not send @google.com passwords unencrypted + if (!encrypted && jid_.domain() == "google.com") { + return ""; + } + + std::vector<std::string>::const_iterator it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN"); + if (it == mechanisms.end()) { + return ""; + } + else { + return "PLAIN"; + } + } + + // Creates a SaslMechanism for the given mechanism name (you own it + // once you get it). If not handled, return NULL. + virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) { + if (mechanism == "PLAIN") { + return new SaslPlainMechanism(jid_, password_); + } + return NULL; + } + +private: + Jid jid_; + XmppPassword password_; + +}; + + +} + +#endif + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h new file mode 100644 index 00000000..8d2aa9d4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PREXMPPAUTH_H_ +#define _PREXMPPAUTH_H_ + +#include "talk/base/sigslot.h" +#include "talk/xmpp/saslhandler.h" +#include "talk/xmpp/xmpppassword.h" + +namespace cricket { + class SocketAddress; +} + +namespace buzz { + +class Jid; +class SaslMechanism; + +class CaptchaChallenge { + public: + CaptchaChallenge() : captcha_needed_(false) {} + CaptchaChallenge(const std::string& token, const std::string& url) + : captcha_needed_(true), captcha_token_(token), captcha_image_url_(url) { + } + + bool captcha_needed() const { return captcha_needed_; } + const std::string& captcha_token() const { return captcha_token_; } + + // This url is relative to the gaia server. Once we have better tools + // for cracking URLs, we should probably make this a full URL + const std::string& captcha_image_url() const { return captcha_image_url_; } + + private: + bool captcha_needed_; + std::string captcha_token_; + std::string captcha_image_url_; +}; + +class PreXmppAuth : public SaslHandler { +public: + virtual ~PreXmppAuth() {} + + virtual void StartPreXmppAuth( + const Jid & jid, + const cricket::SocketAddress & server, + const XmppPassword & pass, + const std::string & auth_cookie) = 0; + + sigslot::signal0<> SignalAuthDone; + + virtual bool IsAuthDone() = 0; + virtual bool IsAuthorized() = 0; + virtual bool HadError() = 0; + virtual CaptchaChallenge GetCaptchaChallenge() = 0; + virtual std::string GetAuthCookie() = 0; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h new file mode 100644 index 00000000..a6630d90 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h @@ -0,0 +1,67 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SASLCOOKIEMECHANISM_H_ +#define _SASLCOOKIEMECHANISM_H_ + +#include "talk/xmpp/saslmechanism.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" + +namespace buzz { + +class SaslCookieMechanism : public SaslMechanism { + +public: + SaslCookieMechanism(const std::string & mechanism, const std::string & username, const std::string & cookie) : + mechanism_(mechanism), username_(username), cookie_(cookie) {} + + virtual std::string GetMechanismName() { return mechanism_; } + + virtual XmlElement * StartSaslAuth() { + // send initial request + XmlElement * el = new XmlElement(QN_SASL_AUTH, true); + el->AddAttr(QN_MECHANISM, mechanism_); + + std::string credential; + credential.append("\0", 1); + credential.append(username_); + credential.append("\0", 1); + credential.append(cookie_); + el->AddText(Base64Encode(credential)); + return el; + } + +private: + std::string mechanism_; + std::string username_; + std::string cookie_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h new file mode 100644 index 00000000..b57d3baf --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SASLHANDLER_H_ +#define _SASLHANDLER_H_ + +#include <string> + +namespace buzz { + +class XmlElement; +class SaslMechanism; + +// Creates mechanisms to deal with a given mechanism +class SaslHandler { + +public: + + // Intended to be subclassed + virtual ~SaslHandler() {} + + // Should pick the best method according to this handler + // returns the empty string if none are suitable + virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) = 0; + + // Creates a SaslMechanism for the given mechanism name (you own it + // once you get it). + // If not handled, return NULL. + virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) = 0; +}; + +} + +#endif + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc new file mode 100644 index 00000000..092df104 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc @@ -0,0 +1,68 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/base64.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/saslmechanism.h" + +namespace buzz { + +XmlElement * +SaslMechanism::StartSaslAuth() { + return new XmlElement(QN_SASL_AUTH, true); +} + +XmlElement * +SaslMechanism::HandleSaslChallenge(const XmlElement * challenge) { + return new XmlElement(QN_SASL_ABORT, true); +} + +void +SaslMechanism::HandleSaslSuccess(const XmlElement * success) { +} + +void +SaslMechanism::HandleSaslFailure(const XmlElement * failure) { +} + +std::string +SaslMechanism::Base64Encode(const std::string & plain) { + return Base64::encode(plain); +} + +std::string +SaslMechanism::Base64Decode(const std::string & encoded) { + return Base64::decode(encoded); +} + +std::string +SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) { + return Base64::encodeFromArray(plain, length); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h new file mode 100644 index 00000000..f2e5adce --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h @@ -0,0 +1,74 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SASLMECHANISM_H_ +#define _SASLMECHANISM_H_ + +#include <string> + +namespace buzz { + +class XmlElement; + + +// Defines a mechnanism to do SASL authentication. +// Subclass instances should have a self-contained way to present +// credentials. +class SaslMechanism { + +public: + + // Intended to be subclassed + virtual ~SaslMechanism() {} + + // Should return the name of the SASL mechanism, e.g., "PLAIN" + virtual std::string GetMechanismName() = 0; + + // Should generate the initial "auth" request. Default is just <auth/>. + virtual XmlElement * StartSaslAuth(); + + // Should respond to a SASL "<challenge>" request. Default is + // to abort (for mechanisms that do not do challenge-response) + virtual XmlElement * HandleSaslChallenge(const XmlElement * challenge); + + // Notification of a SASL "<success>". Sometimes information + // is passed on success. + virtual void HandleSaslSuccess(const XmlElement * success); + + // Notification of a SASL "<failure>". Sometimes information + // for the user is passed on failure. + virtual void HandleSaslFailure(const XmlElement * failure); + +protected: + static std::string Base64Encode(const std::string & plain); + static std::string Base64Decode(const std::string & encoded); + static std::string Base64EncodeFromArray(const char * plain, size_t length); +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h new file mode 100644 index 00000000..7e0b0562 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h @@ -0,0 +1,65 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SASLPLAINMECHANISM_H_ +#define _SASLPLAINMECHANISM_H_ + +#include "talk/xmpp/saslmechanism.h" +#include "talk/xmpp/xmpppassword.h" + +namespace buzz { + +class SaslPlainMechanism : public SaslMechanism { + +public: + SaslPlainMechanism(const buzz::Jid user_jid, const XmppPassword & password) : + user_jid_(user_jid), password_(password) {} + + virtual std::string GetMechanismName() { return "PLAIN"; } + + virtual XmlElement * StartSaslAuth() { + // send initial request + XmlElement * el = new XmlElement(QN_SASL_AUTH, true); + el->AddAttr(QN_MECHANISM, "PLAIN"); + + FormatXmppPassword credential; + credential.Append("\0", 1); + credential.Append(user_jid_.Str()); + credential.Append("\0", 1); + credential.Append(&password_); + el->AddText(Base64EncodeFromArray(credential.GetData(), credential.GetLength())); + return el; + } + +private: + Jid user_jid_; + XmppPassword password_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc new file mode 100644 index 00000000..959b6f88 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc @@ -0,0 +1,372 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "xmppclient.h" +#include "xmpptask.h" +#include "talk/xmpp/constants.h" +#include "talk/base/sigslot.h" +#include "talk/xmpp/saslplainmechanism.h" +#include "talk/xmpp/prexmppauth.h" +#include "talk/base/scoped_ptr.h" +#include "talk/xmpp/plainsaslhandler.h" + +namespace buzz { + +Task * +XmppClient::GetParent(int code) { + if (code == XMPP_CLIENT_TASK_CODE) + return this; + else + return Task::GetParent(code); +} + +class XmppClient::Private : + public sigslot::has_slots<>, + public XmppSessionHandler, + public XmppOutputHandler { +public: + + Private(XmppClient * client) : + client_(client), + socket_(NULL), + engine_(NULL), + proxy_port_(0), + pre_engine_error_(XmppEngine::ERROR_NONE), + signal_closed_(false) {} + + // the owner + XmppClient * const client_; + + // the two main objects + scoped_ptr<AsyncSocket> socket_; + scoped_ptr<XmppEngine> engine_; + scoped_ptr<PreXmppAuth> pre_auth_; + XmppPassword pass_; + std::string auth_cookie_; + cricket::SocketAddress server_; + std::string proxy_host_; + int proxy_port_; + XmppEngine::Error pre_engine_error_; + CaptchaChallenge captcha_challenge_; + bool signal_closed_; + + // implementations of interfaces + void OnStateChange(int state); + void WriteOutput(const char * bytes, size_t len); + void StartTls(const std::string & domainname); + void CloseConnection(); + + // slots for socket signals + void OnSocketConnected(); + void OnSocketRead(); + void OnSocketClosed(); +}; + +XmppReturnStatus +XmppClient::Connect(const XmppClientSettings & settings, AsyncSocket * socket, PreXmppAuth * pre_auth) { + if (socket == NULL) + return XMPP_RETURN_BADARGUMENT; + if (d_->socket_.get() != NULL) + return XMPP_RETURN_BADSTATE; + + d_->socket_.reset(socket); + + d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected); + d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead); + d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed); + + d_->engine_.reset(XmppEngine::Create()); + d_->engine_->SetSessionHandler(d_.get()); + d_->engine_->SetOutputHandler(d_.get()); + d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY)); + if (!settings.resource().empty()) { + d_->engine_->SetRequestedResource(settings.resource()); + } + d_->engine_->SetUseTls(settings.use_tls()); + + + d_->pass_ = settings.pass(); + d_->auth_cookie_ = settings.auth_cookie(); + d_->server_ = settings.server(); + d_->proxy_host_ = settings.proxy_host(); + d_->proxy_port_ = settings.proxy_port(); + d_->pre_auth_.reset(pre_auth); + + return XMPP_RETURN_OK; +} + +XmppEngine::State +XmppClient::GetState() { + if (d_->engine_.get() == NULL) + return XmppEngine::STATE_NONE; + return d_->engine_->GetState(); +} + +XmppEngine::Error +XmppClient::GetError() { + if (d_->engine_.get() == NULL) + return XmppEngine::ERROR_NONE; + if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) + return d_->pre_engine_error_; + return d_->engine_->GetError(); +} + +CaptchaChallenge XmppClient::GetCaptchaChallenge() { + if (d_->engine_.get() == NULL) + return CaptchaChallenge(); + return d_->captcha_challenge_; +} + +std::string +XmppClient::GetAuthCookie() { + if (d_->engine_.get() == NULL) + return ""; + return d_->auth_cookie_; +} + +static void +ForgetPassword(std::string & to_erase) { + size_t len = to_erase.size(); + for (size_t i = 0; i < len; i++) { + // get rid of characters + to_erase[i] = 'x'; + } + // get rid of length + to_erase.erase(); +} + +int +XmppClient::ProcessStart() { + if (d_->pre_auth_.get()) { + d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone); + d_->pre_auth_->StartPreXmppAuth( + d_->engine_->GetUser(), d_->server_, d_->pass_, d_->auth_cookie_); + d_->pass_.Clear(); // done with this; + return STATE_PRE_XMPP_LOGIN; + } + else { + d_->engine_->SetSaslHandler(new PlainSaslHandler( + d_->engine_->GetUser(), d_->pass_)); + d_->pass_.Clear(); // done with this; + return STATE_START_XMPP_LOGIN; + } +} + +void +XmppClient::OnAuthDone() { + Wake(); +} + +int +XmppClient::ProcessCookieLogin() { + // Don't know how this could happen, but crash reports show it as NULL + if (!d_->pre_auth_.get()) { + d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; + EnsureClosed(); + return STATE_ERROR; + } + + // Wait until pre authentication is done is done + if (!d_->pre_auth_->IsAuthDone()) + return STATE_BLOCKED; + + if (!d_->pre_auth_->IsAuthorized()) { + // maybe split out a case when gaia is down? + if (d_->pre_auth_->HadError()) { + d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; + } + else { + d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED; + d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge(); + } + d_->pre_auth_.reset(NULL); // done with this + EnsureClosed(); + return STATE_ERROR; + } + + // Save auth cookie as a result + d_->auth_cookie_ = d_->pre_auth_->GetAuthCookie(); + + // transfer ownership of pre_auth_ to engine + d_->engine_->SetSaslHandler(d_->pre_auth_.release()); + + return STATE_START_XMPP_LOGIN; +} + +int +XmppClient::ProcessStartXmppLogin() { + // Done with pre-connect tasks - connect! + if (!d_->socket_->Connect(d_->server_)) { + EnsureClosed(); + return STATE_ERROR; + } + + return STATE_RESPONSE; +} + +int +XmppClient::ProcessResponse() { + // Hang around while we are connected. + if (!delivering_signal_ && (d_->engine_.get() == NULL || + d_->engine_->GetState() == XmppEngine::STATE_CLOSED)) + return STATE_DONE; + return STATE_BLOCKED; +} + +XmppReturnStatus +XmppClient::Disconnect() { + if (d_->socket_.get() == NULL) + return XMPP_RETURN_BADSTATE; + d_->engine_->Disconnect(); + return XMPP_RETURN_OK; +} + +XmppClient::XmppClient(Task * parent) : Task(parent), + delivering_signal_(false) { + d_.reset(new Private(this)); +} + +XmppClient::~XmppClient() {} + +const Jid & +XmppClient::jid() { + return d_->engine_->FullJid(); +} + + +std::string +XmppClient::NextId() { + return d_->engine_->NextId(); +} + +XmppReturnStatus +XmppClient::SendStanza(const XmlElement * stanza) { + return d_->engine_->SendStanza(stanza); +} + +XmppReturnStatus +XmppClient::SendStanzaError(const XmlElement * old_stanza, XmppStanzaError xse, const std::string & message) { + return d_->engine_->SendStanzaError(old_stanza, xse, message); +} + +XmppReturnStatus +XmppClient::SendRaw(const std::string & text) { + return d_->engine_->SendRaw(text); +} + +XmppEngine* +XmppClient::engine() { + return d_->engine_.get(); +} + +void +XmppClient::Private::OnSocketConnected() { + engine_->Connect(); +} + +void +XmppClient::Private::OnSocketRead() { + char bytes[4096]; + size_t bytes_read; + for (;;) { + if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) { + // TODO: deal with error information + return; + } + + if (bytes_read == 0) + return; + +//#ifdef _DEBUG + client_->SignalLogInput(bytes, bytes_read); +//#endif + + engine_->HandleInput(bytes, bytes_read); + } +} + +void +XmppClient::Private::OnSocketClosed() { + engine_->ConnectionClosed(); +} + +void +XmppClient::Private::OnStateChange(int state) { + if (state == XmppEngine::STATE_CLOSED) { + client_->EnsureClosed(); + } + else { + client_->SignalStateChange((XmppEngine::State)state); + } + client_->Wake(); +} + +void +XmppClient::Private::WriteOutput(const char * bytes, size_t len) { + +//#ifdef _DEBUG + client_->SignalLogOutput(bytes, len); +//#endif + + socket_->Write(bytes, len); + // TODO: deal with error information +} + +void +XmppClient::Private::StartTls(const std::string & domain) { +#if defined(FEATURE_ENABLE_SSL) + socket_->StartTls(domain); +#endif +} + +void +XmppClient::Private::CloseConnection() { + socket_->Close(); +} + +void +XmppClient::AddXmppTask(XmppTask * task, XmppEngine::HandlerLevel level) { + d_->engine_->AddStanzaHandler(task, level); +} + +void +XmppClient::RemoveXmppTask(XmppTask * task) { + d_->engine_->RemoveStanzaHandler(task); +} + +void +XmppClient::EnsureClosed() { + if (!d_->signal_closed_) { + d_->signal_closed_ = true; + delivering_signal_ = true; + SignalStateChange(XmppEngine::STATE_CLOSED); + delivering_signal_ = false; + } +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h new file mode 100644 index 00000000..f8b4798c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h @@ -0,0 +1,157 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPCLIENT_H_ +#define _XMPPCLIENT_H_ + +#include <string> +#include "talk/base/basicdefs.h" +#include "talk/base/sigslot.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/asyncsocket.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/base/task.h" + +namespace buzz { + +class XmppTask; +class PreXmppAuth; +class CaptchaChallenge; + +// Just some non-colliding number. Could have picked "1". +#define XMPP_CLIENT_TASK_CODE 0x366c1e47 + +///////////////////////////////////////////////////////////////////// +// +// XMPPCLIENT +// +///////////////////////////////////////////////////////////////////// +// +// See Task first. XmppClient is a parent task for XmppTasks. +// +// XmppClient is a task which is designed to be the parent task for +// all tasks that depend on a single Xmpp connection. If you want to, +// for example, listen for subscription requests forever, then your +// listener should be a task that is a child of the XmppClient that owns +// the connection you are using. XmppClient has all the utility methods +// that basically drill through to XmppEngine. +// +// XmppClient is just a wrapper for XmppEngine, and if I were writing it +// all over again, I would make XmppClient == XmppEngine. Why? +// XmppEngine needs tasks too, for example it has an XmppLoginTask which +// should just be the same kind of Task instead of an XmppEngine specific +// thing. It would help do certain things like GAIA auth cleaner. +// +///////////////////////////////////////////////////////////////////// + +class XmppClient : public Task, public sigslot::has_slots<> +{ +public: + XmppClient(Task * parent); + ~XmppClient(); + + XmppReturnStatus Connect(const XmppClientSettings & settings, + AsyncSocket * socket, + PreXmppAuth * preauth); + + virtual Task * GetParent(int code); + virtual int ProcessStart(); + virtual int ProcessResponse(); + XmppReturnStatus Disconnect(); + const Jid & jid(); + + sigslot::signal1<XmppEngine::State> SignalStateChange; + XmppEngine::State GetState(); + XmppEngine::Error GetError(); + + // When there is an authentication error, we may have captcha info + // that the user can use to unlock their account + CaptchaChallenge GetCaptchaChallenge(); + + // When authentication is successful, this returns the service cookie + // (if we used GAIA authentication) + std::string GetAuthCookie(); + + std::string NextId(); + XmppReturnStatus SendStanza(const XmlElement *stanza); + XmppReturnStatus SendRaw(const std::string & text); + XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal, + XmppStanzaError code, + const std::string & text); + + XmppEngine* engine(); + + sigslot::signal2<const char *, int> SignalLogInput; + sigslot::signal2<const char *, int> SignalLogOutput; + +private: + friend class XmppTask; + + void OnAuthDone(); + + // managed tasks and dispatching + void AddXmppTask(XmppTask *, XmppEngine::HandlerLevel); + void RemoveXmppTask(XmppTask *); + + sigslot::signal0<> SignalDisconnected; + +private: + // Internal state management + enum { + STATE_PRE_XMPP_LOGIN = STATE_NEXT, + STATE_START_XMPP_LOGIN = STATE_NEXT + 1, + }; + int Process(int state) { + switch (state) { + case STATE_PRE_XMPP_LOGIN: return ProcessCookieLogin(); + case STATE_START_XMPP_LOGIN: return ProcessStartXmppLogin(); + default: return Task::Process(state); + } + } + + std::string GetStateName(int state) const { + switch (state) { + case STATE_PRE_XMPP_LOGIN: return "PRE_XMPP_LOGIN"; + case STATE_START_XMPP_LOGIN: return "START_XMPP_LOGIN"; + default: return Task::GetStateName(state); + } + } + + int ProcessCookieLogin(); + int ProcessStartXmppLogin(); + void EnsureClosed(); + + class Private; + friend class Private; + scoped_ptr<Private> d_; + + bool delivering_signal_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h new file mode 100644 index 00000000..9795682b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPCLIENTSETTINGS_H_ +#define _XMPPCLIENTSETTINGS_H_ + +#include "talk/p2p/base/port.h" +#include "talk/xmpp/xmpppassword.h" + +namespace buzz { + +class XmppClientSettings { +public: + XmppClientSettings() : + use_tls_(false), use_cookie_auth_(false), protocol_(cricket::PROTO_TCP), + proxy_(cricket::PROXY_NONE), proxy_port_(80), use_proxy_auth_(false) {} + + void set_user(const std::string & user) { user_ = user; } + void set_host(const std::string & host) { host_ = host; } + void set_pass(const XmppPassword & pass) { pass_ = pass; } + void set_auth_cookie(const std::string & cookie) { auth_cookie_ = cookie; } + void set_resource(const std::string & resource) { resource_ = resource; } + void set_use_tls(bool use_tls) { use_tls_ = use_tls; } + void set_server(const cricket::SocketAddress & server) { + server_ = server; + } + void set_protocol(cricket::ProtocolType protocol) { protocol_ = protocol; } + void set_proxy(cricket::ProxyType f) { proxy_ = f; } + void set_proxy_host(const std::string & host) { proxy_host_ = host; } + void set_proxy_port(int port) { proxy_port_ = port; }; + void set_use_proxy_auth(bool f) { use_proxy_auth_ = f; } + void set_proxy_user(const std::string & user) { proxy_user_ = user; } + void set_proxy_pass(const XmppPassword & pass) { proxy_pass_ = pass; } + + const std::string & user() const { return user_; } + const std::string & host() const { return host_; } + const XmppPassword & pass() const { return pass_; } + const std::string & auth_cookie() const { return auth_cookie_; } + const std::string & resource() const { return resource_; } + bool use_tls() const { return use_tls_; } + const cricket::SocketAddress & server() const { return server_; } + cricket::ProtocolType protocol() const { return protocol_; } + cricket::ProxyType proxy() const { return proxy_; } + const std::string & proxy_host() const { return proxy_host_; } + int proxy_port() const { return proxy_port_; } + bool use_proxy_auth() const { return use_proxy_auth_; } + const std::string & proxy_user() const { return proxy_user_; } + const XmppPassword & proxy_pass() const { return proxy_pass_; } + +private: + std::string user_; + std::string host_; + XmppPassword pass_; + std::string auth_cookie_; + std::string resource_; + bool use_tls_; + bool use_cookie_auth_; + cricket::SocketAddress server_; + cricket::ProtocolType protocol_; + cricket::ProxyType proxy_; + std::string proxy_host_; + int proxy_port_; + bool use_proxy_auth_; + std::string proxy_user_; + XmppPassword proxy_pass_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h new file mode 100644 index 00000000..ef8f2ea8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h @@ -0,0 +1,332 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmppengine_h_ +#define _xmppengine_h_ + +// also part of the API +#include "talk/xmpp/jid.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" + + +namespace buzz { + +class XmppEngine; +class SaslHandler; +typedef void * XmppIqCookie; + +//! XMPP stanza error codes. +//! Used in XmppEngine.SendStanzaError(). +enum XmppStanzaError { + XSE_BAD_REQUEST, + XSE_CONFLICT, + XSE_FEATURE_NOT_IMPLEMENTED, + XSE_FORBIDDEN, + XSE_GONE, + XSE_INTERNAL_SERVER_ERROR, + XSE_ITEM_NOT_FOUND, + XSE_JID_MALFORMED, + XSE_NOT_ACCEPTABLE, + XSE_NOT_ALLOWED, + XSE_PAYMENT_REQUIRED, + XSE_RECIPIENT_UNAVAILABLE, + XSE_REDIRECT, + XSE_REGISTRATION_REQUIRED, + XSE_SERVER_NOT_FOUND, + XSE_SERVER_TIMEOUT, + XSE_RESOURCE_CONSTRAINT, + XSE_SERVICE_UNAVAILABLE, + XSE_SUBSCRIPTION_REQUIRED, + XSE_UNDEFINED_CONDITION, + XSE_UNEXPECTED_REQUEST, +}; + +// XmppReturnStatus +// This is used by API functions to synchronously return status. +enum XmppReturnStatus { + XMPP_RETURN_OK, + XMPP_RETURN_BADARGUMENT, + XMPP_RETURN_BADSTATE, + XMPP_RETURN_PENDING, + XMPP_RETURN_UNEXPECTED, + XMPP_RETURN_NOTYETIMPLEMENTED, +}; + +//! Callback for socket output for an XmppEngine connection. +//! Register via XmppEngine.SetOutputHandler. An XmppEngine +//! can call back to this handler while it is processing +//! Connect, SendStanza, SendIq, Disconnect, or HandleInput. +class XmppOutputHandler { +public: + + //! Deliver the specified bytes to the XMPP socket. + virtual void WriteOutput(const char * bytes, size_t len) = 0; + + //! Initiate TLS encryption on the socket. + //! The implementation must verify that the SSL + //! certificate matches the given domainname. + virtual void StartTls(const std::string & domainname) = 0; + + //! Called when engine wants the connecton closed. + virtual void CloseConnection() = 0; +}; + +//! Callback to deliver engine state change notifications +//! to the object managing the engine. +class XmppSessionHandler { +public: + //! Called when engine changes state. Argument is new state. + virtual void OnStateChange(int state) = 0; +}; + +//! Callback to deliver stanzas to an Xmpp application module. +//! Register via XmppEngine.SetDefaultSessionHandler or via +//! XmppEngine.AddSessionHAndler. +class XmppStanzaHandler { +public: + + //! Process the given stanza. + //! The handler must return true if it has handled the stanza. + //! A false return value causes the stanza to be passed on to + //! the next registered handler. + virtual bool HandleStanza(const XmlElement * stanza) = 0; +}; + +//! Callback to deliver iq responses (results and errors). +//! Register while sending an iq via XmppEngine.SendIq. +//! Iq responses are routed to matching XmppIqHandlers in preference +//! to sending to any registered SessionHandlers. +class XmppIqHandler { +public: + //! Called to handle the iq response. + //! The response may be either a result or an error, and will have + //! an 'id' that matches the request and a 'from' that matches the + //! 'to' of the request. Called no more than once; once this is + //! called, the handler is automatically unregistered. + virtual void IqResponse(XmppIqCookie cookie, const XmlElement * pelStanza) = 0; +}; + +//! The XMPP connection engine. +//! This engine implements the client side of the 'core' XMPP protocol. +//! To use it, register an XmppOutputHandler to handle socket output +//! and pass socket input to HandleInput. Then application code can +//! set up the connection with a user, password, and other settings, +//! and then call Connect() to initiate the connection. +//! An application can listen for events and receive stanzas by +//! registering an XmppStanzaHandler via AddStanzaHandler(). +class XmppEngine { +public: + static XmppEngine * Create(); + virtual ~XmppEngine() {} + + //! Error codes. See GetError(). + enum Error { + ERROR_NONE = 0, //!< No error + ERROR_XML, //!< Malformed XML or encoding error + ERROR_STREAM, //!< XMPP stream error - see GetStreamError() + ERROR_VERSION, //!< XMPP version error + ERROR_UNAUTHORIZED, //!< User is not authorized (rejected credentials) + ERROR_TLS, //!< TLS could not be negotiated + ERROR_AUTH, //!< Authentication could not be negotiated + ERROR_BIND, //!< Resource or session binding could not be negotiated + ERROR_CONNECTION_CLOSED,//!< Connection closed by output handler. + ERROR_DOCUMENT_CLOSED, //!< Closed by </stream:stream> + ERROR_SOCKET, //!< Socket error + }; + + //! States. See GetState(). + enum State { + STATE_NONE = 0, //!< Nonexistent state + STATE_START, //!< Initial state. + STATE_OPENING, //!< Exchanging stream headers, authenticating and so on. + STATE_OPEN, //!< Authenticated and bound. + STATE_CLOSED, //!< Session closed, possibly due to error. + }; + + // SOCKET INPUT AND OUTPUT ------------------------------------------------ + + //! Registers the handler for socket output + virtual XmppReturnStatus SetOutputHandler(XmppOutputHandler *pxoh) = 0; + + //! Provides socket input to the engine + virtual XmppReturnStatus HandleInput(const char * bytes, size_t len) = 0; + + //! Advises the engine that the socket has closed + virtual XmppReturnStatus ConnectionClosed() = 0; + + // SESSION SETUP --------------------------------------------------------- + + //! Indicates the (bare) JID for the user to use. + virtual XmppReturnStatus SetUser(const Jid & jid)= 0; + + //! Get the login (bare) JID. + virtual const Jid & GetUser() = 0; + + //! Provides different methods for credentials for login. + //! Takes ownership of this object; deletes when login is done + virtual XmppReturnStatus SetSaslHandler(SaslHandler * h) = 0; + + //! Sets whether TLS will be used within the connection (default true). + virtual XmppReturnStatus SetUseTls(bool useTls) = 0; + + //! Sets an alternate domain from which we allows TLS certificates. + //! This is for use in the case where a we want to allow a proxy to + //! serve up its own certificate rather than one owned by the underlying + //! domain. + virtual XmppReturnStatus SetTlsServerDomain(const std::string & proxy_domain) = 0; + + //! Gets whether TLS will be used within the connection. + virtual bool GetUseTls() = 0; + + //! Sets the request resource name, if any (optional). + //! Note that the resource name may be overridden by the server; after + //! binding, the actual resource name is available as part of FullJid(). + virtual XmppReturnStatus SetRequestedResource(const std::string& resource) = 0; + + //! Gets the request resource name. + virtual const std::string & GetRequestedResource() = 0; + + // SESSION MANAGEMENT --------------------------------------------------- + + //! Set callback for state changes. + virtual XmppReturnStatus SetSessionHandler(XmppSessionHandler* handler) = 0; + + //! Initiates the XMPP connection. + //! After supplying connection settings, call this once to initiate, + //! (optionally) encrypt, authenticate, and bind the connection. + virtual XmppReturnStatus Connect() = 0; + + //! The current engine state. + virtual State GetState() = 0; + + //! Returns true if the connection is encrypted (under TLS) + virtual bool IsEncrypted() = 0; + + //! The error code. + //! Consult this after XmppOutputHandler.OnClose(). + virtual Error GetError() = 0; + + //! The stream:error stanza, when the error is XMPP_ERROR_STREAM. + //! Notice the stanza returned is owned by the XmppEngine and + //! is deleted when the engine is destroyed. + virtual const XmlElement * GetStreamError() = 0; + + //! Closes down the connection. + //! Sends CloseConnection to output, and disconnects and registered + //! session handlers. After Disconnect completes, it is guaranteed + //! that no further callbacks will be made. + virtual XmppReturnStatus Disconnect() = 0; + + // APPLICATION USE ------------------------------------------------------- + + enum HandlerLevel { + HL_NONE = 0, + HL_PEEK, //!< Sees messages before all other processing; cannot abort + HL_SINGLE, //!< Watches for a single message, e.g., by id and sender + HL_SENDER, //!< Watches for a type of message from a specific sender + HL_TYPE, //!< Watches a type of message, e.g., all groupchat msgs + HL_ALL, //!< Watches all messages - gets last shot + HL_COUNT, //!< Count of handler levels + }; + + //! Adds a listener for session events. + //! Stanza delivery is chained to session handlers; the first to + //! return 'true' is the last to get each stanza. + virtual XmppReturnStatus AddStanzaHandler(XmppStanzaHandler* handler, HandlerLevel level = HL_PEEK) = 0; + + //! Removes a listener for session events. + virtual XmppReturnStatus RemoveStanzaHandler(XmppStanzaHandler* handler) = 0; + + //! Sends a stanza to the server. + virtual XmppReturnStatus SendStanza(const XmlElement * pelStanza) = 0; + + //! Sends raw text to the server + virtual XmppReturnStatus SendRaw(const std::string & text) = 0; + + //! Sends an iq to the server, and registers a callback for the result. + //! Returns the cookie passed to the result handler. + virtual XmppReturnStatus SendIq(const XmlElement* pelStanza, + XmppIqHandler* iq_handler, + XmppIqCookie* cookie) = 0; + + //! Unregisters an iq callback handler given its cookie. + //! No callback will come to this handler after it's unregistered. + virtual XmppReturnStatus RemoveIqHandler(XmppIqCookie cookie, + XmppIqHandler** iq_handler) = 0; + + + //! Forms and sends an error in response to the given stanza. + //! Swaps to and from, sets type to "error", and adds error information + //! based on the passed code. Text is optional and may be STR_EMPTY. + virtual XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal, + XmppStanzaError code, + const std::string & text) = 0; + + //! The fullly bound JID. + //! This JID is only valid after binding has succeeded. If the value + //! is JID_NULL, the binding has not succeeded. + virtual const Jid & FullJid() = 0; + + //! The next unused iq id for this connection. + //! Call this when building iq stanzas, to ensure that each iq + //! gets its own unique id. + virtual std::string NextId() = 0; + +}; + +} + + +// Move these to a better location + +#define XMPP_FAILED(x) \ + ( (x) == buzz::XMPP_RETURN_OK ? false : true) \ + + +#define XMPP_SUCCEEDED(x) \ + ( (x) == buzz::XMPP_RETURN_OK ? true : false) \ + +#define IFR(x) \ + do { \ + xmpp_status = (x); \ + if (XMPP_FAILED(xmpp_status)) { \ + return xmpp_status; \ + } \ + } while (false) \ + + +#define IFC(x) \ + do { \ + xmpp_status = (x); \ + if (XMPP_FAILED(xmpp_status)) { \ + goto Cleanup; \ + } \ + } while (false) \ + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc new file mode 100644 index 00000000..173d711b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc @@ -0,0 +1,480 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#define TRACK_ARRAY_ALLOC_PROBLEM + +#include <vector> +#include <sstream> +#include <algorithm> +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmpp/xmppengineimpl.h" +#include "talk/xmpp/xmpplogintask.h" +#include "talk/xmpp/constants.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmpp/saslhandler.h" +// #include "buzz/saslmechanism.h" + +#define new TRACK_NEW + +namespace buzz { + +static const std::string XMPP_CLIENT_NAMESPACES[] = { + "stream", "http://etherx.jabber.org/streams", + "", "jabber:client", +}; + +static const size_t XMPP_CLIENT_NAMESPACES_LEN = 4; + +XmppEngine * XmppEngine::Create() { + return new XmppEngineImpl(); +} + + +XmppEngineImpl::XmppEngineImpl() : + stanzaParseHandler_(this), + stanzaParser_(&stanzaParseHandler_), + engine_entered_(0), + user_jid_(JID_EMPTY), + password_(), + requested_resource_(STR_EMPTY), + tls_needed_(true), + login_task_(new XmppLoginTask(this)), + next_id_(0), + bound_jid_(JID_EMPTY), + state_(STATE_START), + encrypted_(false), + error_code_(ERROR_NONE), + stream_error_(NULL), + raised_reset_(false), + output_handler_(NULL), + session_handler_(NULL), + iq_entries_(new IqEntryVector()), + output_(new std::stringstream()), + sasl_handler_(NULL) { + for (int i = 0; i < HL_COUNT; i+= 1) { + stanza_handlers_[i].reset(new StanzaHandlerVector()); + } +} + +XmppEngineImpl::~XmppEngineImpl() { + DeleteIqCookies(); +} + +XmppReturnStatus +XmppEngineImpl::SetOutputHandler(XmppOutputHandler* output_handler) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + output_handler_ = output_handler; + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetSessionHandler(XmppSessionHandler* session_handler) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + session_handler_ = session_handler; + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::HandleInput(const char * bytes, size_t len) { + if (state_ < STATE_OPENING || state_ > STATE_OPEN) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + stanzaParser_.Parse(bytes, len, false); + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::ConnectionClosed() { + if (state_ != STATE_CLOSED) { + EnterExit ee(this); + // If told that connection closed and not already closed, + // then connection was unpexectedly dropped. + SignalError(ERROR_CONNECTION_CLOSED); + } + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetUseTls(bool useTls) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + tls_needed_ = useTls; + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetTlsServerDomain(const std::string & tls_server_domain) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + tls_server_domain_= tls_server_domain; + + return XMPP_RETURN_OK; +} + +bool +XmppEngineImpl::GetUseTls() { + return tls_needed_; +} + +XmppReturnStatus +XmppEngineImpl::SetUser(const Jid & jid) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + user_jid_ = jid; + + return XMPP_RETURN_OK; +} + +const Jid & +XmppEngineImpl::GetUser() { + return user_jid_; +} + +XmppReturnStatus +XmppEngineImpl::SetSaslHandler(SaslHandler * sasl_handler) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + sasl_handler_.reset(sasl_handler); + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetRequestedResource(const std::string & resource) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + requested_resource_ = resource; + + return XMPP_RETURN_OK; +} + +const std::string & +XmppEngineImpl::GetRequestedResource() { + return requested_resource_; +} + +XmppReturnStatus +XmppEngineImpl::AddStanzaHandler(XmppStanzaHandler * stanza_handler, + XmppEngine::HandlerLevel level) { + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + + stanza_handlers_[level]->push_back(stanza_handler); + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::RemoveStanzaHandler(XmppStanzaHandler * stanza_handler) { + + bool found = false; + + for (int level = 0; level < HL_COUNT; level += 1) { + StanzaHandlerVector::iterator new_end = + std::remove(stanza_handlers_[level]->begin(), + stanza_handlers_[level]->end(), + stanza_handler); + + if (new_end != stanza_handlers_[level]->end()) { + stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end()); + found = true; + } + } + + if (!found) { + return XMPP_RETURN_BADARGUMENT; + } + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::Connect() { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + // get the login task started + state_ = STATE_OPENING; + if (login_task_.get()) { + login_task_->IncomingStanza(NULL, false); + if (login_task_->IsDone()) + login_task_.reset(); + } + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SendStanza(const XmlElement * element) { + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + if (login_task_.get()) { + // still handshaking - then outbound stanzas are queued + login_task_->OutgoingStanza(element); + } else { + // handshake done - send straight through + InternalSendStanza(element); + } + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SendRaw(const std::string & text) { + if (state_ == STATE_CLOSED || login_task_.get()) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + (*output_) << text; + + return XMPP_RETURN_OK; +} + +std::string +XmppEngineImpl::NextId() { + std::stringstream ss; + ss << next_id_++; + return ss.str(); +} + +XmppReturnStatus +XmppEngineImpl::Disconnect() { + + if (state_ != STATE_CLOSED) { + EnterExit ee(this); + if (state_ == STATE_OPEN) + *output_ << "</stream:stream>"; + state_ = STATE_CLOSED; + } + + return XMPP_RETURN_OK; +} + +void +XmppEngineImpl::IncomingStart(const XmlElement * pelStart) { + if (HasError() || raised_reset_) + return; + + if (login_task_.get()) { + // start-stream should go to login task + login_task_->IncomingStanza(pelStart, true); + if (login_task_->IsDone()) + login_task_.reset(); + } + else { + // if not logging in, it's an error to see a start + SignalError(ERROR_XML); + } +} + +void +XmppEngineImpl::IncomingStanza(const XmlElement * stanza) { + if (HasError() || raised_reset_) + return; + + if (stanza->Name() == QN_STREAM_ERROR) { + // Explicit XMPP stream error + SignalStreamError(stanza); + } else if (login_task_.get()) { + // Handle login handshake + login_task_->IncomingStanza(stanza, false); + if (login_task_->IsDone()) + login_task_.reset(); + } else if (HandleIqResponse(stanza)) { + // iq is handled by above call + } else { + // give every "peek" handler a shot at all stanzas + for (size_t i = 0; i < stanza_handlers_[HL_PEEK]->size(); i += 1) { + (*stanza_handlers_[HL_PEEK])[i]->HandleStanza(stanza); + } + + // give other handlers a shot in precedence order, stopping after handled + for (int level = HL_SINGLE; level <= HL_ALL; level += 1) { + for (size_t i = 0; i < stanza_handlers_[level]->size(); i += 1) { + if ((*stanza_handlers_[level])[i]->HandleStanza(stanza)) + goto Handled; + } + } + + // If nobody wants to handle a stanza then send back an error. + // Only do this for IQ stanzas as messages should probably just be dropped + // and presence stanzas should certainly be dropped. + std::string type = stanza->Attr(QN_TYPE); + if (stanza->Name() == QN_IQ && + !(type == "error" || type == "result")) { + SendStanzaError(stanza, XSE_FEATURE_NOT_IMPLEMENTED, STR_EMPTY); + } + } + Handled: + ; // handled - we're done +} + +void +XmppEngineImpl::IncomingEnd(bool isError) { + if (HasError() || raised_reset_) + return; + + SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED); +} + +void +XmppEngineImpl::InternalSendStart(const std::string & to) { + // send stream-beginning + // note, we put a \r\n at tne end fo the first line to cause non-XMPP + // line-oriented servers (e.g., Apache) to reveal themselves more quickly. + *output_ << "<stream:stream to=\"" << to << "\" version=\"1.0\" " + "xmlns:stream=\"http://etherx.jabber.org/streams\" " + "xmlns=\"jabber:client\">\r\n"; +} + +void +XmppEngineImpl::InternalSendStanza(const XmlElement * element) { + // It should really never be necessary to set a FROM attribute on a stanza. + // It is implied by the bind on the stream and if you get it wrong + // (by flipping from/to on a message?) the server will close the stream. + ASSERT(!element->HasAttr(QN_FROM)); + + // TODO: consider caching the XmlPrinter + XmlPrinter::PrintXml(output_.get(), element, + XMPP_CLIENT_NAMESPACES, XMPP_CLIENT_NAMESPACES_LEN); +} + +std::string +XmppEngineImpl::ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) { + return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted); +} + +SaslMechanism * +XmppEngineImpl::GetSaslMechanism(const std::string & name) { + return sasl_handler_->CreateSaslMechanism(name); +} + +void +XmppEngineImpl::SignalBound(const Jid & fullJid) { + if (state_ == STATE_OPENING) { + bound_jid_ = fullJid; + state_ = STATE_OPEN; + } +} + +void +XmppEngineImpl::SignalStreamError(const XmlElement * pelStreamError) { + if (state_ != STATE_CLOSED) { + stream_error_.reset(new XmlElement(*pelStreamError)); + SignalError(ERROR_STREAM); + } +} + +void +XmppEngineImpl::SignalError(Error errorCode) { + if (state_ != STATE_CLOSED) { + error_code_ = errorCode; + state_ = STATE_CLOSED; + } +} + +bool +XmppEngineImpl::HasError() { + return error_code_ != ERROR_NONE; +} + +void +XmppEngineImpl::StartTls(const std::string & domain) { + if (output_handler_) { + output_handler_->StartTls( + tls_server_domain_.empty() ? domain : tls_server_domain_); + encrypted_ = true; + } +} + +XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) + : engine_(engine), + state_(engine->state_), + error_(engine->error_code_) { + engine->engine_entered_ += 1; +} + +XmppEngineImpl::EnterExit::~EnterExit() { + XmppEngineImpl* engine = engine_; + + engine->engine_entered_ -= 1; + + bool closing = (engine->state_ != state_ && + engine->state_ == STATE_CLOSED); + bool flushing = closing || (engine->engine_entered_ == 0); + + if (engine->output_handler_ && flushing) { + std::string output = engine->output_->str(); + if (output.length() > 0) + engine->output_handler_->WriteOutput(output.c_str(), output.length()); + engine->output_->str(""); + + if (closing) { + engine->output_handler_->CloseConnection(); + engine->output_handler_ = 0; + } + } + + if (engine->engine_entered_) + return; + + if (engine->raised_reset_) { + engine->stanzaParser_.Reset(); + engine->raised_reset_ = false; + } + + if (engine->session_handler_) { + if (engine->state_ != state_) + engine->session_handler_->OnStateChange(engine->state_); + // Note: Handling of OnStateChange(CLOSED) should allow for the + // deletion of the engine, so no members should be accessed + // after this line. + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h new file mode 100644 index 00000000..c36f168c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h @@ -0,0 +1,262 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmppengineimpl_h_ +#define _xmppengineimpl_h_ + +#include <sstream> +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmppstanzaparser.h" + +namespace buzz { + +class XmppLoginTask; +class XmppEngine; +class XmppIqEntry; +class SaslHandler; +class SaslMechanism; + + +//! The XMPP connection engine. +//! This engine implements the client side of the 'core' XMPP protocol. +//! To use it, register an XmppOutputHandler to handle socket output +//! and pass socket input to HandleInput. Then application code can +//! set up the connection with a user, password, and other settings, +//! and then call Connect() to initiate the connection. +//! An application can listen for events and receive stanzas by +//! registering an XmppStanzaHandler via AddStanzaHandler(). +class XmppEngineImpl : public XmppEngine { +public: + XmppEngineImpl(); + virtual ~XmppEngineImpl(); + + // SOCKET INPUT AND OUTPUT ------------------------------------------------ + + //! Registers the handler for socket output + virtual XmppReturnStatus SetOutputHandler(XmppOutputHandler *pxoh); + + //! Provides socket input to the engine + virtual XmppReturnStatus HandleInput(const char * bytes, size_t len); + + //! Advises the engine that the socket has closed + virtual XmppReturnStatus ConnectionClosed(); + + // SESSION SETUP --------------------------------------------------------- + + //! Indicates the (bare) JID for the user to use. + virtual XmppReturnStatus SetUser(const Jid & jid); + + //! Get the login (bare) JID. + virtual const Jid & GetUser(); + + //! Indicates the autentication to use. Takes ownership of the object. + virtual XmppReturnStatus SetSaslHandler(SaslHandler * sasl_handler); + + //! Sets whether TLS will be used within the connection (default true). + virtual XmppReturnStatus SetUseTls(bool useTls); + + //! Sets an alternate domain from which we allows TLS certificates. + //! This is for use in the case where a we want to allow a proxy to + //! serve up its own certificate rather than one owned by the underlying + //! domain. + virtual XmppReturnStatus SetTlsServerDomain(const std::string & proxy_domain); + + //! Gets whether TLS will be used within the connection. + virtual bool GetUseTls(); + + //! Sets the request resource name, if any (optional). + //! Note that the resource name may be overridden by the server; after + //! binding, the actual resource name is available as part of FullJid(). + virtual XmppReturnStatus SetRequestedResource(const std::string& resource); + + //! Gets the request resource name. + virtual const std::string & GetRequestedResource(); + + // SESSION MANAGEMENT --------------------------------------------------- + + //! Set callback for state changes. + virtual XmppReturnStatus SetSessionHandler(XmppSessionHandler* handler); + + //! Initiates the XMPP connection. + //! After supplying connection settings, call this once to initiate, + //! (optionally) encrypt, authenticate, and bind the connection. + virtual XmppReturnStatus Connect(); + + //! The current engine state. + virtual State GetState() { return state_; } + + //! Returns true if the connection is encrypted (under TLS) + virtual bool IsEncrypted() { return encrypted_; } + + //! The error code. + //! Consult this after XmppOutputHandler.OnClose(). + virtual Error GetError() { return error_code_; } + + //! The stream:error stanza, when the error is XMPP_ERROR_STREAM. + //! Notice the stanza returned is owned by the XmppEngine and + //! is deleted when the engine is destroyed. + virtual const XmlElement * GetStreamError() { return stream_error_.get(); } + + //! Closes down the connection. + //! Sends CloseConnection to output, and disconnects and registered + //! session handlers. After Disconnect completes, it is guaranteed + //! that no further callbacks will be made. + virtual XmppReturnStatus Disconnect(); + + // APPLICATION USE ------------------------------------------------------- + + //! Adds a listener for session events. + //! Stanza delivery is chained to session handlers; the first to + //! return 'true' is the last to get each stanza. + virtual XmppReturnStatus AddStanzaHandler(XmppStanzaHandler* handler, + XmppEngine::HandlerLevel level); + + //! Removes a listener for session events. + virtual XmppReturnStatus RemoveStanzaHandler(XmppStanzaHandler* handler); + + //! Sends a stanza to the server. + virtual XmppReturnStatus SendStanza(const XmlElement * pelStanza); + + //! Sends raw text to the server + virtual XmppReturnStatus SendRaw(const std::string & text); + + //! Sends an iq to the server, and registers a callback for the result. + //! Returns the cookie passed to the result handler. + virtual XmppReturnStatus SendIq(const XmlElement* pelStanza, + XmppIqHandler* iq_handler, + XmppIqCookie* cookie); + + //! Unregisters an iq callback handler given its cookie. + //! No callback will come to this handler after it's unregistered. + virtual XmppReturnStatus RemoveIqHandler(XmppIqCookie cookie, + XmppIqHandler** iq_handler); + + //! Forms and sends an error in response to the given stanza. + //! Swaps to and from, sets type to "error", and adds error information + //! based on the passed code. Text is optional and may be STR_EMPTY. + virtual XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal, + XmppStanzaError code, + const std::string & text); + + //! The fullly bound JID. + //! This JID is only valid after binding has succeeded. If the value + //! is JID_NULL, the binding has not succeeded. + virtual const Jid & FullJid() { return bound_jid_; } + + //! The next unused iq id for this connection. + //! Call this when building iq stanzas, to ensure that each iq + //! gets its own unique id. + virtual std::string NextId(); + +private: + friend class XmppLoginTask; + friend class XmppIqEntry; + + void IncomingStanza(const XmlElement *pelStanza); + void IncomingStart(const XmlElement *pelStanza); + void IncomingEnd(bool isError); + + void InternalSendStart(const std::string & domainName); + void InternalSendStanza(const XmlElement * pelStanza); + std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted); + SaslMechanism * GetSaslMechanism(const std::string & name); + void SignalBound(const Jid & fullJid); + void SignalStreamError(const XmlElement * pelStreamError); + void SignalError(Error errorCode); + bool HasError(); + void DeleteIqCookies(); + bool HandleIqResponse(const XmlElement * element); + void StartTls(const std::string & domain); + void RaiseReset() { raised_reset_ = true; } + + class StanzaParseHandler : public XmppStanzaParseHandler { + public: + StanzaParseHandler(XmppEngineImpl * outer) : outer_(outer) {} + virtual void StartStream(const XmlElement * pelStream) + { outer_->IncomingStart(pelStream); } + virtual void Stanza(const XmlElement * pelStanza) + { outer_->IncomingStanza(pelStanza); } + virtual void EndStream() + { outer_->IncomingEnd(false); } + virtual void XmlError() + { outer_->IncomingEnd(true); } + private: + XmppEngineImpl * const outer_; + }; + + class EnterExit { + public: + EnterExit(XmppEngineImpl* engine); + ~EnterExit(); + private: + XmppEngineImpl* engine_; + State state_; + Error error_; + + }; + + friend class StanzaParseHandler; + friend class EnterExit; + + StanzaParseHandler stanzaParseHandler_; + XmppStanzaParser stanzaParser_; + + + // state + int engine_entered_; + Jid user_jid_; + std::string password_; + std::string requested_resource_; + bool tls_needed_; + std::string tls_server_domain_; + scoped_ptr<XmppLoginTask> login_task_; + + int next_id_; + Jid bound_jid_; + State state_; + bool encrypted_; + Error error_code_; + scoped_ptr<XmlElement> stream_error_; + bool raised_reset_; + XmppOutputHandler* output_handler_; + XmppSessionHandler* session_handler_; + + typedef STD_VECTOR(XmppStanzaHandler*) StanzaHandlerVector; + scoped_ptr<StanzaHandlerVector> stanza_handlers_[HL_COUNT]; + + typedef STD_VECTOR(XmppIqEntry*) IqEntryVector; + scoped_ptr<IqEntryVector> iq_entries_; + + scoped_ptr<SaslHandler> sasl_handler_; + + scoped_ptr<std::stringstream> output_; +}; + +} + + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc new file mode 100644 index 00000000..eb623ed9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc @@ -0,0 +1,279 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <vector> +#include <algorithm> +#include "talk/base/common.h" +#include "talk/xmpp/xmppengineimpl.h" +#include "talk/xmpp/constants.h" + +#define new TRACK_NEW + +namespace buzz { + +class XmppIqEntry { + XmppIqEntry(const std::string & id, const std::string & to, + XmppEngine * pxce, XmppIqHandler * iq_handler) : + id_(id), + to_(to), + engine_(pxce), + iq_handler_(iq_handler) { + } + +private: + friend class XmppEngineImpl; + + const std::string id_; + const std::string to_; + XmppEngine * const engine_; + XmppIqHandler * const iq_handler_; +}; + + +XmppReturnStatus +XmppEngineImpl::SendIq(const XmlElement * element, XmppIqHandler * iq_handler, + XmppIqCookie* cookie) { + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + if (NULL == iq_handler) + return XMPP_RETURN_BADARGUMENT; + if (!element || element->Name() != QN_IQ) + return XMPP_RETURN_BADARGUMENT; + + const std::string& type = element->Attr(QN_TYPE); + if (type != "get" && type != "set") + return XMPP_RETURN_BADARGUMENT; + + if (!element->HasAttr(QN_ID)) + return XMPP_RETURN_BADARGUMENT; + const std::string& id = element->Attr(QN_ID); + + XmppIqEntry * iq_entry = new XmppIqEntry(id, + element->Attr(QN_TO), + this, iq_handler); + iq_entries_->push_back(iq_entry); + SendStanza(element); + + if (cookie) + *cookie = iq_entry; + + return XMPP_RETURN_OK; +} + + +XmppReturnStatus +XmppEngineImpl::RemoveIqHandler(XmppIqCookie cookie, + XmppIqHandler ** iq_handler) { + + std::vector<XmppIqEntry*, std::allocator<XmppIqEntry*> >::iterator pos; + + pos = std::find(iq_entries_->begin(), + iq_entries_->end(), + reinterpret_cast<XmppIqEntry*>(cookie)); + + if (pos == iq_entries_->end()) + return XMPP_RETURN_BADARGUMENT; + + XmppIqEntry* entry = *pos; + iq_entries_->erase(pos); + if (iq_handler) + *iq_handler = entry->iq_handler_; + delete entry; + + return XMPP_RETURN_OK; +} + +void +XmppEngineImpl::DeleteIqCookies() { + for (size_t i = 0; i < iq_entries_->size(); i += 1) { + XmppIqEntry * iq_entry_ = (*iq_entries_)[i]; + (*iq_entries_)[i] = NULL; + delete iq_entry_; + } + iq_entries_->clear(); +} + +static void +AecImpl(XmlElement * error_element, const QName & name, + const char * type, const char * code) { + error_element->AddElement(new XmlElement(QN_ERROR)); + error_element->AddAttr(QN_CODE, code, 1); + error_element->AddAttr(QN_TYPE, type, 1); + error_element->AddElement(new XmlElement(name, true), 1); +} + + +static void +AddErrorCode(XmlElement * error_element, XmppStanzaError code) { + switch (code) { + case XSE_BAD_REQUEST: + AecImpl(error_element, QN_STANZA_BAD_REQUEST, "modify", "400"); + break; + case XSE_CONFLICT: + AecImpl(error_element, QN_STANZA_CONFLICT, "cancel", "409"); + break; + case XSE_FEATURE_NOT_IMPLEMENTED: + AecImpl(error_element, QN_STANZA_FEATURE_NOT_IMPLEMENTED, + "cancel", "501"); + break; + case XSE_FORBIDDEN: + AecImpl(error_element, QN_STANZA_FORBIDDEN, "auth", "403"); + break; + case XSE_GONE: + AecImpl(error_element, QN_STANZA_GONE, "modify", "302"); + break; + case XSE_INTERNAL_SERVER_ERROR: + AecImpl(error_element, QN_STANZA_INTERNAL_SERVER_ERROR, "wait", "500"); + break; + case XSE_ITEM_NOT_FOUND: + AecImpl(error_element, QN_STANZA_ITEM_NOT_FOUND, "cancel", "404"); + break; + case XSE_JID_MALFORMED: + AecImpl(error_element, QN_STANZA_JID_MALFORMED, "modify", "400"); + break; + case XSE_NOT_ACCEPTABLE: + AecImpl(error_element, QN_STANZA_NOT_ACCEPTABLE, "cancel", "406"); + break; + case XSE_NOT_ALLOWED: + AecImpl(error_element, QN_STANZA_NOT_ALLOWED, "cancel", "405"); + break; + case XSE_PAYMENT_REQUIRED: + AecImpl(error_element, QN_STANZA_PAYMENT_REQUIRED, "auth", "402"); + break; + case XSE_RECIPIENT_UNAVAILABLE: + AecImpl(error_element, QN_STANZA_RECIPIENT_UNAVAILABLE, "wait", "404"); + break; + case XSE_REDIRECT: + AecImpl(error_element, QN_STANZA_REDIRECT, "modify", "302"); + break; + case XSE_REGISTRATION_REQUIRED: + AecImpl(error_element, QN_STANZA_REGISTRATION_REQUIRED, "auth", "407"); + break; + case XSE_SERVER_NOT_FOUND: + AecImpl(error_element, QN_STANZA_REMOTE_SERVER_NOT_FOUND, + "cancel", "404"); + break; + case XSE_SERVER_TIMEOUT: + AecImpl(error_element, QN_STANZA_REMOTE_SERVER_TIMEOUT, "wait", "502"); + break; + case XSE_RESOURCE_CONSTRAINT: + AecImpl(error_element, QN_STANZA_RESOURCE_CONSTRAINT, "wait", "500"); + break; + case XSE_SERVICE_UNAVAILABLE: + AecImpl(error_element, QN_STANZA_SERVICE_UNAVAILABLE, "cancel", "503"); + break; + case XSE_SUBSCRIPTION_REQUIRED: + AecImpl(error_element, QN_STANZA_SUBSCRIPTION_REQUIRED, "auth", "407"); + break; + case XSE_UNDEFINED_CONDITION: + AecImpl(error_element, QN_STANZA_UNDEFINED_CONDITION, "wait", "500"); + break; + case XSE_UNEXPECTED_REQUEST: + AecImpl(error_element, QN_STANZA_UNEXPECTED_REQUEST, "wait", "400"); + break; + } +} + + +XmppReturnStatus +XmppEngineImpl::SendStanzaError(const XmlElement * element_original, + XmppStanzaError code, + const std::string & text) { + + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + + XmlElement error_element(element_original->Name()); + error_element.AddAttr(QN_TYPE, "error"); + + // copy attrs, copy 'from' to 'to' and strip 'from' + for (const XmlAttr * attribute = element_original->FirstAttr(); + attribute; attribute = attribute->NextAttr()) { + QName name = attribute->Name(); + if (name == QN_TO) + continue; // no need to put a from attr. Server will stamp stanza + else if (name == QN_FROM) + name = QN_TO; + else if (name == QN_TYPE) + continue; + error_element.AddAttr(name, attribute->Value()); + } + + // copy children + for (const XmlChild * child = element_original->FirstChild(); + child; + child = child->NextChild()) { + if (child->IsText()) { + error_element.AddText(child->AsText()->Text()); + } else { + error_element.AddElement(new XmlElement(*(child->AsElement()))); + } + } + + // add error information + AddErrorCode(&error_element, code); + if (text != STR_EMPTY) { + XmlElement * text_element = new XmlElement(QN_STANZA_TEXT, true); + text_element->AddText(text); + error_element.AddElement(text_element); + } + + SendStanza(&error_element); + + return XMPP_RETURN_OK; +} + + +bool +XmppEngineImpl::HandleIqResponse(const XmlElement * element) { + if (iq_entries_->empty()) + return false; + if (element->Name() != QN_IQ) + return false; + std::string type = element->Attr(QN_TYPE); + if (type != "result" && type != "error") + return false; + if (!element->HasAttr(QN_ID)) + return false; + std::string id = element->Attr(QN_ID); + std::string from = element->Attr(QN_FROM); + + for (std::vector<XmppIqEntry *>::iterator it = iq_entries_->begin(); + it != iq_entries_->end(); it += 1) { + XmppIqEntry * iq_entry = *it; + if (iq_entry->id_ == id && iq_entry->to_ == from) { + iq_entries_->erase(it); + iq_entry->iq_handler_->IqResponse(iq_entry, element); + delete iq_entry; + return true; + } + } + + return false; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc new file mode 100644 index 00000000..470c2dc2 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc @@ -0,0 +1,357 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <string> +#include <vector> +#include <iostream> +#include "talk/xmpp/jid.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmpp/xmppengineimpl.h" +#include "talk/xmpp/constants.h" +#include "talk/base/base64.h" +#include "talk/xmpp/xmpplogintask.h" +#include "talk/xmpp/saslmechanism.h" + +#define new TRACK_NEW + +namespace buzz { + +XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) : + pctx_(pctx), + authNeeded_(true), + state_(LOGINSTATE_INIT), + pelStanza_(NULL), + isStart_(false), + iqId_(STR_EMPTY), + pelFeatures_(NULL), + fullJid_(STR_EMPTY), + streamId_(STR_EMPTY), + pvecQueuedStanzas_(new std::vector<XmlElement *>()), + sasl_mech_(NULL) { +} + +XmppLoginTask::~XmppLoginTask() { + for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) + delete (*pvecQueuedStanzas_)[i]; +} + +void +XmppLoginTask::IncomingStanza(const XmlElement *element, bool isStart) { + pelStanza_ = element; + isStart_ = isStart; + Advance(); + pelStanza_ = NULL; + isStart_ = false; +} + +const XmlElement * +XmppLoginTask::NextStanza() { + const XmlElement * result = pelStanza_; + pelStanza_ = NULL; + return result; +} + +bool +XmppLoginTask::Advance() { + + for (;;) { + + const XmlElement * element = NULL; + + switch (state_) { + + case LOGINSTATE_INIT: { + pctx_->RaiseReset(); + pelFeatures_.reset(NULL); + + pctx_->InternalSendStart(pctx_->user_jid_.domain()); + state_ = LOGINSTATE_STREAMSTART_SENT; + break; + } + + case LOGINSTATE_STREAMSTART_SENT: { + if (NULL == (element = NextStanza())) + return true; + + if (!isStart_ || !HandleStartStream(element)) + return Failure(XmppEngine::ERROR_VERSION); + + state_ = LOGINSTATE_STARTED_XMPP; + return true; + } + + case LOGINSTATE_STARTED_XMPP: { + if (NULL == (element = NextStanza())) + return true; + + if (!HandleFeatures(element)) + return Failure(XmppEngine::ERROR_VERSION); + + if (pctx_->tls_needed_) { + state_ = LOGINSTATE_TLS_INIT; + continue; + } + + if (authNeeded_) { + state_ = LOGINSTATE_AUTH_INIT; + continue; + } + + state_ = LOGINSTATE_BIND_INIT; + continue; + } + + case LOGINSTATE_TLS_INIT: { + const XmlElement * pelTls = GetFeature(QN_TLS_STARTTLS); + if (!pelTls) + return Failure(XmppEngine::ERROR_TLS); + + XmlElement el(QN_TLS_STARTTLS, true); + pctx_->InternalSendStanza(&el); + state_ = LOGINSTATE_TLS_REQUESTED; + continue; + } + + case LOGINSTATE_TLS_REQUESTED: { + if (NULL == (element = NextStanza())) + return true; + if (element->Name() != QN_TLS_PROCEED) + return Failure(XmppEngine::ERROR_TLS); + + // The proper domain to verify against is the real underlying + // domain - i.e., the domain that owns the JID. Our XmppEngineImpl + // also allows matching against a proxy domain instead, if it is told + // to do so - see the implementation of XmppEngineImpl::StartTls and + // XmppEngine::SetTlsServerDomain to see how you can use that feature + pctx_->StartTls(pctx_->user_jid_.domain()); + pctx_->tls_needed_ = false; + state_ = LOGINSTATE_INIT; + continue; + } + + case LOGINSTATE_AUTH_INIT: { + const XmlElement * pelSaslAuth = GetFeature(QN_SASL_MECHANISMS); + if (!pelSaslAuth) { + return Failure(XmppEngine::ERROR_AUTH); + } + + // Collect together the SASL auth mechanisms presented by the server + std::vector<std::string> mechanisms; + for (const XmlElement * pelMech = + pelSaslAuth->FirstNamed(QN_SASL_MECHANISM); + pelMech; + pelMech = pelMech->NextNamed(QN_SASL_MECHANISM)) { + + mechanisms.push_back(pelMech->BodyText()); + } + + // Given all the mechanisms, choose the best + std::string choice(pctx_->ChooseBestSaslMechanism(mechanisms, pctx_->IsEncrypted())); + if (choice.empty()) { + return Failure(XmppEngine::ERROR_AUTH); + } + + // No recognized auth mechanism - that's an error + sasl_mech_.reset(pctx_->GetSaslMechanism(choice)); + if (sasl_mech_.get() == NULL) { + return Failure(XmppEngine::ERROR_AUTH); + } + + // OK, let's start it. + XmlElement * auth = sasl_mech_->StartSaslAuth(); + if (auth == NULL) { + return Failure(XmppEngine::ERROR_AUTH); + } + + pctx_->InternalSendStanza(auth); + delete auth; + state_ = LOGINSTATE_SASL_RUNNING; + continue; + } + + case LOGINSTATE_SASL_RUNNING: { + if (NULL == (element = NextStanza())) + return true; + if (element->Name().Namespace() != NS_SASL) + return Failure(XmppEngine::ERROR_AUTH); + if (element->Name() == QN_SASL_CHALLENGE) { + XmlElement * response = sasl_mech_->HandleSaslChallenge(element); + if (response == NULL) { + return Failure(XmppEngine::ERROR_AUTH); + } + pctx_->InternalSendStanza(response); + delete response; + state_ = LOGINSTATE_SASL_RUNNING; + continue; + } + if (element->Name() != QN_SASL_SUCCESS) { + return Failure(XmppEngine::ERROR_UNAUTHORIZED); + } + + // Authenticated! + authNeeded_ = false; + state_ = LOGINSTATE_INIT; + continue; + } + + case LOGINSTATE_BIND_INIT: { + const XmlElement * pelBindFeature = GetFeature(QN_BIND_BIND); + const XmlElement * pelSessionFeature = GetFeature(QN_SESSION_SESSION); + if (!pelBindFeature || !pelSessionFeature) + return Failure(XmppEngine::ERROR_BIND); + + XmlElement iq(QN_IQ); + iq.AddAttr(QN_TYPE, "set"); + + iqId_ = pctx_->NextId(); + iq.AddAttr(QN_ID, iqId_); + iq.AddElement(new XmlElement(QN_BIND_BIND, true)); + + if (pctx_->requested_resource_ != STR_EMPTY) { + iq.AddElement(new XmlElement(QN_BIND_RESOURCE), 1); + iq.AddText(pctx_->requested_resource_, 2); + } + pctx_->InternalSendStanza(&iq); + state_ = LOGINSTATE_BIND_REQUESTED; + continue; + } + + case LOGINSTATE_BIND_REQUESTED: { + if (NULL == (element = NextStanza())) + return true; + + if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ || + element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set") + return true; + + if (element->Attr(QN_TYPE) != "result" || element->FirstElement() == NULL || + element->FirstElement()->Name() != QN_BIND_BIND) + return Failure(XmppEngine::ERROR_BIND); + + fullJid_ = Jid(element->FirstElement()->TextNamed(QN_BIND_JID)); + if (!fullJid_.IsFull()) { + return Failure(XmppEngine::ERROR_BIND); + } + + if (pctx_->user_jid_.domain() != STR_DEFAULT_DOMAIN && + fullJid_.BareJid() != pctx_->user_jid_) { + return Failure(XmppEngine::ERROR_BIND); + } + + // now request session + XmlElement iq(QN_IQ); + iq.AddAttr(QN_TYPE, "set"); + + iqId_ = pctx_->NextId(); + iq.AddAttr(QN_ID, iqId_); + iq.AddElement(new XmlElement(QN_SESSION_SESSION, true)); + pctx_->InternalSendStanza(&iq); + + state_ = LOGINSTATE_SESSION_REQUESTED; + continue; + } + + case LOGINSTATE_SESSION_REQUESTED: { + if (NULL == (element = NextStanza())) + return true; + if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ || + element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set") + return false; + + if (element->Attr(QN_TYPE) != "result") + return Failure(XmppEngine::ERROR_BIND); + + pctx_->SignalBound(fullJid_); + FlushQueuedStanzas(); + state_ = LOGINSTATE_DONE; + return true; + } + + case LOGINSTATE_DONE: + return false; + } + } +} + +bool +XmppLoginTask::HandleStartStream(const XmlElement *element) { + + if (element->Name() != QN_STREAM_STREAM) + return false; + + if (element->Attr(QN_XMLNS) != "jabber:client") + return false; + + if (element->Attr(QN_VERSION) != "1.0") + return false; + + if (!element->HasAttr(QN_ID)) + return false; + + streamId_ = element->Attr(QN_ID); + + return true; +} + +bool +XmppLoginTask::HandleFeatures(const XmlElement *element) { + if (element->Name() != QN_STREAM_FEATURES) + return false; + + pelFeatures_.reset(new XmlElement(*element)); + return true; +} + +const XmlElement * +XmppLoginTask::GetFeature(const QName & name) { + return pelFeatures_->FirstNamed(name); +} + +bool +XmppLoginTask::Failure(XmppEngine::Error reason) { + state_ = LOGINSTATE_DONE; + pctx_->SignalError(reason); + return false; +} + +void +XmppLoginTask::OutgoingStanza(const XmlElement * element) { + XmlElement * pelCopy = new XmlElement(*element); + pvecQueuedStanzas_->push_back(pelCopy); +} + +void +XmppLoginTask::FlushQueuedStanzas() { + for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) { + pctx_->InternalSendStanza((*pvecQueuedStanzas_)[i]); + delete (*pvecQueuedStanzas_)[i]; + } + pvecQueuedStanzas_->clear(); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h new file mode 100644 index 00000000..7f321a30 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h @@ -0,0 +1,95 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _logintask_h_ +#define _logintask_h_ + +#include <string> +#include "talk/xmpp/jid.h" +#include "talk/base/scoped_ptr.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/base/stl_decl.h" + +namespace buzz { + +class XmlElement; +class XmppEngineImpl; +class SaslMechanism; + + +class XmppLoginTask { + +public: + XmppLoginTask(XmppEngineImpl *pctx); + ~XmppLoginTask(); + + bool IsDone() + { return state_ == LOGINSTATE_DONE; } + void IncomingStanza(const XmlElement * element, bool isStart); + void OutgoingStanza(const XmlElement *element); + +private: + enum LoginTaskState { + LOGINSTATE_INIT = 0, + LOGINSTATE_STREAMSTART_SENT, + LOGINSTATE_STARTED_XMPP, + LOGINSTATE_TLS_INIT, + LOGINSTATE_AUTH_INIT, + LOGINSTATE_BIND_INIT, + LOGINSTATE_TLS_REQUESTED, + LOGINSTATE_SASL_RUNNING, + LOGINSTATE_BIND_REQUESTED, + LOGINSTATE_SESSION_REQUESTED, + LOGINSTATE_DONE, + }; + + const XmlElement * NextStanza(); + bool Advance(); + bool HandleStartStream(const XmlElement * element); + bool HandleFeatures(const XmlElement * element); + const XmlElement * GetFeature(const QName & name); + bool Failure(XmppEngine::Error reason); + void FlushQueuedStanzas(); + + XmppEngineImpl * pctx_; + bool authNeeded_; + LoginTaskState state_; + const XmlElement * pelStanza_; + bool isStart_; + std::string iqId_; + scoped_ptr<XmlElement> pelFeatures_; + Jid fullJid_; + std::string streamId_; + scoped_ptr<std::vector<XmlElement *, + std::allocator<XmlElement *> > > pvecQueuedStanzas_; + + scoped_ptr<SaslMechanism> sasl_mech_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h new file mode 100644 index 00000000..f431b4e5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h @@ -0,0 +1,163 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPPASSWORD_H_ +#define _XMPPPASSWORD_H_ + +#include "talk/base/linked_ptr.h" +#include "talk/base/scoped_ptr.h" + +namespace buzz { + +class XmppPasswordImpl { +public: + virtual ~XmppPasswordImpl() {} + virtual size_t GetLength() const = 0; + virtual void CopyTo(char * dest, bool nullterminate) const = 0; + virtual std::string UrlEncode() const = 0; + virtual XmppPasswordImpl * Copy() const = 0; +}; + +class EmptyXmppPasswordImpl : public XmppPasswordImpl { +public: + virtual ~EmptyXmppPasswordImpl() {} + virtual size_t GetLength() const { return 0; } + virtual void CopyTo(char * dest, bool nullterminate) const { + if (nullterminate) { + *dest = '\0'; + } + } + virtual std::string UrlEncode() const { return ""; } + virtual XmppPasswordImpl * Copy() const { return new EmptyXmppPasswordImpl(); } +}; + +class XmppPassword { +public: + XmppPassword() : impl_(new EmptyXmppPasswordImpl()) {} + size_t GetLength() const { return impl_->GetLength(); } + void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); } + XmppPassword(const XmppPassword & other) : impl_(other.impl_->Copy()) {} + explicit XmppPassword(const XmppPasswordImpl & impl) : impl_(impl.Copy()) {} + XmppPassword & operator=(const XmppPassword & other) { + if (this != &other) { + impl_.reset(other.impl_->Copy()); + } + return *this; + } + void Clear() { impl_.reset(new EmptyXmppPasswordImpl()); } + std::string UrlEncode() const { return impl_->UrlEncode(); } + +private: + scoped_ptr<const XmppPasswordImpl> impl_; +}; + + +// Used for constructing strings where a password is involved and we +// need to ensure that we zero memory afterwards +class FormatXmppPassword { +public: + FormatXmppPassword() { + storage_ = new char[32]; + capacity_ = 32; + length_ = 0; + storage_[0] = 0; + } + + void Append(const std::string & text) { + Append(text.data(), text.length()); + } + + void Append(const char * data, size_t length) { + EnsureStorage(length_ + length + 1); + memcpy(storage_ + length_, data, length); + length_ += length; + storage_[length_] = '\0'; + } + + void Append(const XmppPassword * password) { + size_t len = password->GetLength(); + EnsureStorage(length_ + len + 1); + password->CopyTo(storage_ + length_, true); + length_ += len; + } + + size_t GetLength() { + return length_; + } + + const char * GetData() { + return storage_; + } + + + // Ensures storage of at least n bytes + void EnsureStorage(size_t n) { + if (capacity_ >= n) { + return; + } + + size_t old_capacity = capacity_; + char * old_storage = storage_; + + for (;;) { + capacity_ *= 2; + if (capacity_ >= n) + break; + } + + storage_ = new char[capacity_]; + + if (old_capacity) { + memcpy(storage_, old_storage, length_); + + // zero memory in a way that an optimizer won't optimize it out + old_storage[0] = 0; + for (size_t i = 1; i < old_capacity; i++) { + old_storage[i] = old_storage[i - 1]; + } + delete[] old_storage; + } + } + + ~FormatXmppPassword() { + if (capacity_) { + storage_[0] = 0; + for (size_t i = 1; i < capacity_; i++) { + storage_[i] = storage_[i - 1]; + } + } + delete[] storage_; + } +private: + char * storage_; + size_t capacity_; + size_t length_; +}; + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc new file mode 100644 index 00000000..66ed44fb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc @@ -0,0 +1,104 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 <expat.h> +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmpp/xmppstanzaparser.h" +#include "talk/xmpp/constants.h" + +#define new TRACK_NEW + +namespace buzz { + +XmppStanzaParser::XmppStanzaParser(XmppStanzaParseHandler *psph) : + psph_(psph), + innerHandler_(this), + parser_(&innerHandler_), + depth_(0), + builder_() { +} + +void +XmppStanzaParser::Reset() { + parser_.Reset(); + depth_ = 0; + builder_.Reset(); +} + +void +XmppStanzaParser::IncomingStartElement( + XmlParseContext * pctx, const char * name, const char ** atts) { + if (depth_++ == 0) { + XmlElement * pelStream = XmlBuilder::BuildElement(pctx, name, atts); + if (pelStream == NULL) { + pctx->RaiseError(XML_ERROR_SYNTAX); + return; + } + psph_->StartStream(pelStream); + delete pelStream; + return; + } + + builder_.StartElement(pctx, name, atts); +} + +void +XmppStanzaParser::IncomingCharacterData( + XmlParseContext * pctx, const char * text, int len) { + if (depth_ > 1) { + builder_.CharacterData(pctx, text, len); + } +} + +void +XmppStanzaParser::IncomingEndElement( + XmlParseContext * pctx, const char * name) { + if (--depth_ == 0) { + psph_->EndStream(); + return; + } + + builder_.EndElement(pctx, name); + + if (depth_ == 1) { + XmlElement *element = builder_.CreateElement(); + psph_->Stanza(element); + delete element; + } +} + +void +XmppStanzaParser::IncomingError( + XmlParseContext * pctx, XML_Error errCode) { + UNUSED(pctx); + UNUSED(errCode); + psph_->XmlError(); +} + +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h new file mode 100644 index 00000000..1e109a3d --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h @@ -0,0 +1,96 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _xmppstanzaparser_h_ +#define _xmppstanzaparser_h_ + +#include "talk/xmllite/xmlparser.h" +#include "talk/xmllite/xmlbuilder.h" + + +namespace buzz { + +class XmlElement; + +class XmppStanzaParseHandler { +public: + virtual void StartStream(const XmlElement * pelStream) = 0; + virtual void Stanza(const XmlElement * pelStanza) = 0; + virtual void EndStream() = 0; + virtual void XmlError() = 0; +}; + +class XmppStanzaParser { +public: + XmppStanzaParser(XmppStanzaParseHandler *psph); + bool Parse(const char * data, size_t len, bool isFinal) + { return parser_.Parse(data, len, isFinal); } + void Reset(); + +private: + class ParseHandler : public XmlParseHandler { + public: + ParseHandler(XmppStanzaParser * outer) : outer_(outer) {} + virtual void StartElement(XmlParseContext * pctx, + const char * name, const char ** atts) + { outer_->IncomingStartElement(pctx, name, atts); } + virtual void EndElement(XmlParseContext * pctx, + const char * name) + { outer_->IncomingEndElement(pctx, name); } + virtual void CharacterData(XmlParseContext * pctx, + const char * text, int len) + { outer_->IncomingCharacterData(pctx, text, len); } + virtual void Error(XmlParseContext * pctx, + XML_Error errCode) + { outer_->IncomingError(pctx, errCode); } + private: + XmppStanzaParser * const outer_; + }; + + friend class ParseHandler; + + void IncomingStartElement(XmlParseContext * pctx, + const char * name, const char ** atts); + void IncomingEndElement(XmlParseContext * pctx, + const char * name); + void IncomingCharacterData(XmlParseContext * pctx, + const char * text, int len); + void IncomingError(XmlParseContext * pctx, + XML_Error errCode); + + XmppStanzaParseHandler * psph_; + ParseHandler innerHandler_; + XmlParser parser_; + int depth_; + XmlBuilder builder_; + + }; + + +} + +#endif diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc new file mode 100644 index 00000000..82207f3b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc @@ -0,0 +1,168 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/xmpp/xmpptask.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/constants.h" + +namespace buzz { + +XmppTask::XmppTask(Task * parent, XmppEngine::HandlerLevel level) + : Task(parent), client_(NULL) { + XmppClient * client = (XmppClient*)parent->GetParent(XMPP_CLIENT_TASK_CODE); + client_ = client; + id_ = client->NextId(); + client->AddXmppTask(this, level); + client->SignalDisconnected.connect(this, &XmppTask::OnDisconnect); +} + +XmppTask::~XmppTask() { + StopImpl(); +} + +void +XmppTask::StopImpl() { + while (NextStanza() != NULL) {} + if (client_) { + client_->RemoveXmppTask(this); + client_->SignalDisconnected.disconnect(this); + client_ = NULL; + } +} + +XmppReturnStatus +XmppTask::SendStanza(const XmlElement * stanza) { + if (client_ == NULL) + return XMPP_RETURN_BADSTATE; + return client_->SendStanza(stanza); +} + +XmppReturnStatus +XmppTask::SendStanzaError(const XmlElement * element_original, + XmppStanzaError code, + const std::string & text) { + if (client_ == NULL) + return XMPP_RETURN_BADSTATE; + return client_->SendStanzaError(element_original, code, text); +} + +void +XmppTask::Stop() { + StopImpl(); + Task::Stop(); +} + +void +XmppTask::OnDisconnect() { + Error(); +} + +void +XmppTask::QueueStanza(const XmlElement * stanza) { + stanza_queue_.push_back(new XmlElement(*stanza)); + Wake(); +} + +const XmlElement * +XmppTask::NextStanza() { + XmlElement * result = NULL; + if (!stanza_queue_.empty()) { + result = stanza_queue_.front(); + stanza_queue_.pop_front(); + } + next_stanza_.reset(result); + return result; +} + +XmlElement * +XmppTask::MakeIq(const std::string & type, + const buzz::Jid & to, const std::string id) { + XmlElement * result = new XmlElement(QN_IQ); + if (!type.empty()) + result->AddAttr(QN_TYPE, type); + if (to != JID_EMPTY) + result->AddAttr(QN_TO, to.Str()); + if (!id.empty()) + result->AddAttr(QN_ID, id); + return result; +} + +XmlElement * +XmppTask::MakeIqResult(const XmlElement * query) { + XmlElement * result = new XmlElement(QN_IQ); + result->AddAttr(QN_TYPE, STR_RESULT); + if (query->HasAttr(QN_FROM)) { + result->AddAttr(QN_TO, query->Attr(QN_FROM)); + } + result->AddAttr(QN_ID, query->Attr(QN_ID)); + return result; +} + +bool +XmppTask::MatchResponseIq(const XmlElement * stanza, + const Jid & to, const std::string & id) { + if (stanza->Name() != QN_IQ) + return false; + + if (stanza->Attr(QN_ID) != id) + return false; + + Jid from(stanza->Attr(QN_FROM)); + if (from != to) { + Jid me = client_->jid(); + // we address the server as "", but it is legal for the server + // to identify itself with "domain" or "myself@domain" + if (to != JID_EMPTY) { + return false; + } + + if (from != Jid(me.domain()) && from != me.BareJid()) { + return false; + } + } + + + return true; +} + +bool +XmppTask::MatchRequestIq(const XmlElement * stanza, + const std::string & type, const QName & qn) { + if (stanza->Name() != QN_IQ) + return false; + + if (stanza->Attr(QN_TYPE) != type) + return false; + + if (stanza->FirstNamed(qn) == NULL) + return false; + + return true; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h new file mode 100644 index 00000000..3b56a1c9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h @@ -0,0 +1,113 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XMPPTASK_H_ +#define _XMPPTASK_H_ + +#include <string> +#include <deque> +#include "talk/base/sigslot.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/base/task.h" + +namespace buzz { + +///////////////////////////////////////////////////////////////////// +// +// XMPPTASK +// +///////////////////////////////////////////////////////////////////// +// +// See Task and XmppClient first. +// +// XmppTask is a task that is designed to go underneath XmppClient and be +// useful there. It has a way of finding its XmppClient parent so you +// can have it nested arbitrarily deep under an XmppClient and it can +// still find the XMPP services. +// +// Tasks register themselves to listen to particular kinds of stanzas +// that are sent out by the client. Rather than processing stanzas +// right away, they should decide if they own the sent stanza, +// and if so, queue it and Wake() the task, or if a stanza does not belong +// to you, return false right away so the next XmppTask can take a crack. +// This technique (synchronous recognize, but asynchronous processing) +// allows you to have arbitrary logic for recognizing stanzas yet still, +// for example, disconnect a client while processing a stanza - +// without reentrancy problems. +// +///////////////////////////////////////////////////////////////////// + +class XmppClient; + +class XmppTask : + public Task, + public XmppStanzaHandler, + public sigslot::has_slots<> +{ +public: + XmppTask(Task * parent, XmppEngine::HandlerLevel level = XmppEngine::HL_NONE); + virtual ~XmppTask(); + + virtual XmppClient * GetClient() const { return client_; } + std::string task_id() const { return id_; } + +protected: + friend class XmppClient; + + XmppReturnStatus SendStanza(const XmlElement * stanza); + XmppReturnStatus SetResult(const std::string & code); + XmppReturnStatus SendStanzaError(const XmlElement * element_original, + XmppStanzaError code, + const std::string & text); + + virtual void Stop(); + virtual bool HandleStanza(const XmlElement * stanza) { return false; } + virtual void OnDisconnect(); + virtual int ProcessReponse() { return STATE_DONE; } + + void QueueStanza(const XmlElement * stanza); + const XmlElement * NextStanza(); + + bool MatchResponseIq(const XmlElement * stanza, const Jid & to, const std::string & task_id); + bool MatchRequestIq(const XmlElement * stanza, const std::string & type, const QName & qn); + XmlElement *MakeIqResult(const XmlElement * query); + XmlElement *MakeIq(const std::string & type, + const Jid & to, const std::string task_id); + +private: + void StopImpl(); + + XmppClient * client_; + std::deque<XmlElement *> stanza_queue_; + scoped_ptr<XmlElement> next_stanza_; + std::string id_; + +}; + +} + +#endif |