freej: new xmlrpc daemon to receive script commands from remote (testing..),

small fix in controller/javascript classes,
new --enable-dynamic-build to link to dynamic ogg/theora and ffmpeg libs



git-svn-id: svn://dyne.org/rastasoft/freej/trunk@930 383723c8-4afa-0310-b8a8-b1afb83214fc
This commit is contained in:
jaromil
2007-10-07 20:03:09 +00:00
parent fdc9524807
commit dbbae1876b
45 changed files with 6022 additions and 52 deletions
+1 -1
View File
@@ -21,4 +21,4 @@ dist_pkgdata_DATA = \
pkgdatadir = ${prefix}/share/freej
SUBDIRS = lib src filters
SUBDIRS = lib src
+48 -27
View File
@@ -125,7 +125,16 @@ AC_ARG_ENABLE(static-build,
static_build=yes
fi ],[ static_build=no ])
dnl ==============================================================
dnl Check if we are going to link DINAMICALLY ffmpeg and theora
dnl ==============================================================
AC_ARG_ENABLE(dynamic-build,
[ --enable-dynamic-build link dynamically ffmpeg and theora (default: no)],
[ if test ! x$enableval = xyes; then
dynamic_build=no
else
dynamic_build=yes
fi ],[ dynamic_build=no ])
dnl ==============================================================
@@ -144,11 +153,19 @@ dnl AC_HEADER_DIRENT
dnl ==============================================================
dnl Check OGG/THEORA MMX STUFF
dnl ==============================================================
XIPH_INCLUDE=""
XIPH_LIBS=""
THEORA_LIBS=""
THEORA_MMX_DIR=""
THEORA_MMX_INCLUDE=""
PKG_CHECK_MODULES(XIPH, ogg >= 1.1 vorbis vorbisenc,
have_oggtheora=yes, have_oggtheora=no)
if test x$dynamic_build = xyes ; then
PKG_CHECK_MODULES(XIPH, ogg >= 1.1 vorbis vorbisenc theora,
have_oggtheora=yes, have_oggtheora=no)
XIPH_LIBS="$XIPH_LIBS -ltheora"
else
PKG_CHECK_MODULES(XIPH, ogg >= 1.1 vorbis vorbisenc,
have_oggtheora=yes, have_oggtheora=no)
fi
if test x$have_oggtheora = xyes ; then
AC_DEFINE(WITH_OGGTHEORA,1,[define if compiling with Ogg/Theora encoding])
AC_DEFINE(THEORA_SUPPORT_ENCODE,1,[define if compiling Theora encoder])
@@ -160,9 +177,6 @@ if test x$have_oggtheora = xyes ; then
AC_DEFINE(CONFIG_OGGVORBIS_DECODER,1,[define if compiling FFMpeg with oggvorbis decoder])
AC_DEFINE(CONFIG_OGGTHEORA_ENCODER,1,[define if compiling FFMpeg with oggtheora encoder])
AC_DEFINE(CONFIG_OGGTHEORA_DECODER,1,[define if compiling FFMpeg with oggtheora decoder])
THEORA_LIBS="\$(top_srcdir)/lib/theora-mmx/libtheora.a \$(top_srcdir)/lib/shout/libshout.a"
THEORA_MMX_DIR="theora-mmx shout"
THEORA_MMX_INCLUDE="-I\$(top_srcdir)/lib/theora-mmx"
if test x$static_build = xyes ; then
XIPH_LIBS="`pkg-config --variable=prefix ogg`/lib/libogg.a"
XIPH_LIBS="$XIPH_LIBS `pkg-config --variable=prefix vorbis`/lib/libvorbis.a"
@@ -173,6 +187,7 @@ AC_SUBST(THEORA_LIBS)
AC_SUBST(THEORA_MMX_DIR)
AC_SUBST(THEORA_MMX_INCLUDE)
AC_SUBST(XIPH_LIBS)
AC_SUBST(XIPH_INCLUDE)
@@ -188,22 +203,28 @@ dnl ==============================================================
dnl AVCODEC AVFORMAT STUFF
dnl ==============================================================
dnl put a disable flag
FFMPEG_SUBDIRS=""
FFMPEG_LIBS=""
AVCODEC_ARCH=""
if test x$dynamic_build = xyes ; then
PKG_CHECK_MODULES(FFMPEG, libavcodec libavformat libavutil,
have_ffmpeg=yes, have_ffmpeg=no)
else
FFMPEG_LIBS="\$(top_srcdir)/lib/avcodec/libavcodec.a \$(top_srcdir)/lib/avformat/libavformat.a"
FFMPEG_SUBDIRS="avcodec avformat"
if test x$have_mmx = xyes ; then
AVCODEC_ARCH="i386"
FFMPEG_LIBS="$FFMPEG_LIBS \$(top_srcdir)/lib/avcodec/i386/libavcodecmmx.a"
fi
if test x$have_ppc = xyes ; then
AVCODEC_ARCH="ppc"
FFMPEG_LIBS="$FFMPEG_LIBS \$(top_srcdir)/lib/avcodec/ppc/libavcodecppc.a"
fi
fi
AC_DEFINE(WITH_AVCODEC,1,[define if compiling movie layer linking to ffmpeg libavcodec])
AVCODEC_SUBDIRS=""
AVCODEC_LIBS="\$(top_srcdir)/lib/avcodec/libavcodec.a"
AVFORMAT_LIBS="\$(top_srcdir)/lib/avformat/libavformat.a"
if test x$have_mmx = xyes ; then
AVCODEC_SUBDIRS="i386"
AVCODEC_LIBS="$AVCODEC_LIBS \$(top_srcdir)/lib/avcodec/i386/libavcodecmmx.a"
fi
if test x$have_ppc = xyes ; then
AVCODEC_SUBDIRS="ppc"
AVCODEC_LIBS="$AVCODEC_LIBS \$(top_srcdir)/lib/avcodec/ppc/libavcodecppc.a"
fi
AC_SUBST(AVFORMAT_LIBS)
AC_SUBST(AVCODEC_SUBDIRS)
AC_SUBST(AVCODEC_LIBS)
AC_SUBST(FFMPEG_LIBS)
AC_SUBST(FFMPEG_SUBDIRS)
AC_SUBST(AVCODEC_ARCH)
@@ -605,6 +626,7 @@ lib/flash/Makefile
lib/goom/Makefile
lib/slw/Makefile
lib/tbt/Makefile
lib/xmlrpc++/Makefile
lib/frei0r/Makefile
lib/frei0r/filter/Makefile
lib/frei0r/filter/3dflippo/Makefile
@@ -663,13 +685,12 @@ if test x$have_xhacks = xyes; then
echo "= Xscreensaver hacks"
echo " this layer is experimental and broken"
fi
if test x$use_ffmpeg = xyes; then
echo "= ffmpeg (libavcodec)"
echo " libs : $AVCODEC_LIBS"
echo " cflags: $AVCODEC_CFLAGS"
fi
echo "= ffmpeg (avcodec)"
echo " libs : $FFMPEG_LIBS"
if test x$have_oggtheora = xyes; then
echo "= ogg-theora (libogg libtheora)"
echo "= ogg-theora (xiph.org)"
echo " libs : $XIPH_LIBS"
echo " cflags: $XIPH_CFLAGS"
else
+3 -2
View File
@@ -1,7 +1,6 @@
SUBDIRS = ccvt \
@THEORA_MMX_DIR@ \
avformat \
avcodec \
@FFMPEG_SUBDIRS@ \
portaudio \
sdl_image \
sdl_gfx \
@@ -12,4 +11,6 @@ SUBDIRS = ccvt \
goom \
tbt \
slw \
xmlrpc++ \
frei0r
+1 -1
View File
@@ -1,4 +1,4 @@
SUBDIRS = @AVCODEC_SUBDIRS@
SUBDIRS = @AVCODEC_ARCH@
INCLUDES = -I$(top_srcdir)/lib/theora-mmx
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
XmlRpc++ Library
This is version 0.7 of XmlRpc++, an implementation of the [1]XmlRpc
protocol written in C++, based upon Shilad Sen's excellent
[2]py-xmlrpc library. XmlRpc++ is designed to make it easy to
incorporate XmlRpc client and server support into C++ applications. Or
use both client and server objects in your app for easy peer-to-peer
support.
Features
* Easy This library is easy to incorporate into C++ applications.
No other libraries are required, other than your system's socket
libraries. Simple XML parsing and HTTP support are built in.
* Fast All IO is non-blocking, so a slow client or network will
not slow down the server.
* Portable Written in standard C++ to the POSIX and Windows sockets
APIs. You do need a fairly recent compiler (g++ 3.1 or MSVC++ .Net
or MSVC++ 6 with the [3]STL patches.)
* Free This library is released under the [4]GNU [5]LGPL.
Changes
* Better handling of fault responses: server methods can throw an
XmlRpcException to return a fault and XmlRpcClient has a new
method to test whether the last response was a fault.
* Support for system.listMethods and system.methodHelp from the
introspection API.
* Support for system.multicall to process multiple requests in a
single transaction.
* Fixed a problem in the XmlRpcServer destructor (it should not have
been deleting the methods).
* The server ensures a valid result value is returned even if the
method does not set the result. The default result is an empty
string.
* Doxygen comments in header files and a doc target in the makefile.
Installation
There are VC++ 6 and VC++ .Net project files building on Windows. If
you are using VC++ 6, you should apply SP3 and the fixes at
[6]http://www.dinkumware.com/vc_fixes.html. Be sure to set the
appropriate code generation switches. In particular, ensure that the
runtime library (single/multi-threaded, static library/DLL) used is
the same for the XmlRpc++ code and whatever application it will be
linked to.
For Linux, Solaris, and other Unix-like platforms there is a GNU
Makefile which can be edited to suit your system. Specify your C++
compiler, compiler flags, and your system's socket libraries.
In the test directory there are various test programs that are built
by default. To verify that the library built correctly, you can start
the HelloServer example:
HelloServer 8000
and the HelloClient example in another terminal window:
HelloClient localhost 8000
You should see two Hello messages and a sum displayed (amongst a bunch
of debug output). You can also try the XML server validator program
(eg, "Validator 80") and then attempt to connect to it from
[7]http://validator.xmlrpc.com (if you have access to the internet and
are not behind a firewall etc).
Author
[8]Chris Morley
Although no code was re-used, the design and structure of the library
is based upon the py-xmlrpc library implementation.
The base64 decoder/encoder is by [9]Konstantin Pilipchuk.
License
A full copy of the LGPL license is included in the file COPYING. The
source code is Copyright (c) 2002-2003 by Chris Morley. 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
References
1. http://www.xmlrpc.org/
2. http://py-xmlrpc.sourceforge.net/
3. http://www.dinkumware.com/vc_fixes.html
4. http://www.gnu.org/
5. http://www.gnu.org/copyleft/lesser.html
6. http://www.dinkumware.com/vc_fixes.html
7. http://validator.xmlrpc.com/
8. mailto:cmorley@users.sourceforge.net
9. mailto:lostd@ukr.net
+97
View File
@@ -0,0 +1,97 @@
#ifndef _XMLRPC_H_
#define _XMLRPC_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
// 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
//
// The XmlRpc++ home page is http://xmlrpcpp.sourceforge.net/
// My home page is http://www.vermontel.net/~cmorley/
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
#include "XmlRpcClient.h"
#include "XmlRpcException.h"
#include "XmlRpcServer.h"
#include "XmlRpcServerMethod.h"
#include "XmlRpcValue.h"
#include "XmlRpcUtil.h"
namespace XmlRpc {
//! An interface allowing custom handling of error message reporting.
class XmlRpcErrorHandler {
public:
//! Returns a pointer to the currently installed error handling object.
static XmlRpcErrorHandler* getErrorHandler()
{ return _errorHandler; }
//! Specifies the error handler.
static void setErrorHandler(XmlRpcErrorHandler* eh)
{ _errorHandler = eh; }
//! Report an error. Custom error handlers should define this method.
virtual void error(const char* msg) = 0;
protected:
static XmlRpcErrorHandler* _errorHandler;
};
//! An interface allowing custom handling of informational message reporting.
class XmlRpcLogHandler {
public:
//! Returns a pointer to the currently installed message reporting object.
static XmlRpcLogHandler* getLogHandler()
{ return _logHandler; }
//! Specifies the message handler.
static void setLogHandler(XmlRpcLogHandler* lh)
{ _logHandler = lh; }
//! Returns the level of verbosity of informational messages. 0 is no output, 5 is very verbose.
static int getVerbosity()
{ return _verbosity; }
//! Specify the level of verbosity of informational messages. 0 is no output, 5 is very verbose.
static void setVerbosity(int v)
{ _verbosity = v; }
//! Output a message. Custom error handlers should define this method.
virtual void log(int level, const char* msg) = 0;
protected:
static XmlRpcLogHandler* _logHandler;
static int _verbosity;
};
//! Returns log message verbosity. This is short for XmlRpcLogHandler::getVerbosity()
int getVerbosity();
//! Sets log message verbosity. This is short for XmlRpcLogHandler::setVerbosity(level)
void setVerbosity(int level);
//! Version identifier
extern const char XMLRPC_VERSION[];
} // namespace XmlRpc
#endif // _XMLRPC_H_
+547
View File
@@ -0,0 +1,547 @@
#include "XmlRpcClient.h"
#include "XmlRpcSocket.h"
#include "XmlRpc.h"
#include "base64.h" // For HTTP authentication encoding
#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace XmlRpc;
using namespace std;
// Static data
const char XmlRpcClient::REQUEST_BEGIN[] =
"<?xml version=\"1.0\"?>\r\n"
"<methodCall><methodName>";
const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
const char XmlRpcClient::PARAMS_TAG[] = "<params>";
const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
const char XmlRpcClient::PARAM_TAG[] = "<param>";
const char XmlRpcClient::PARAM_ETAG[] = "</param>";
const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
const char XmlRpcClient::FAULT_TAG[] = "<fault>";
XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
{
XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
_host = host;
_port = port;
if (uri && *uri)
_uri = uri;
else
_uri = "/RPC2";
_connectionState = NO_CONNECTION;
_executing = false;
_eof = false;
_ssl = false; _ssl_ssl = (SSL *) NULL;
// Default to keeping the connection open until an explicit close is done
setKeepOpen();
}
XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri, bool ssl)
{
XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
_host = host;
_port = port;
if (uri && *uri)
_uri = uri;
else
_uri = "/RPC2";
_connectionState = NO_CONNECTION;
_executing = false;
_eof = false;
_ssl = ssl;
if (!_ssl) { _ssl_ssl = (SSL *) NULL; }
// Default to keeping the connection open until an explicit close is done
setKeepOpen();
}
XmlRpcClient::XmlRpcClient(const char* host, int port,
const char* login, const char* password, const char* uri/*=0*/)
{
XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d, login %s.", host, port, login);
_host = host;
_port = port;
if (uri)
_uri = uri;
else
_uri = "/RPC2";
_login = login;
_password = password;
_connectionState = NO_CONNECTION;
_executing = false;
_eof = false;
// Default to keeping the connection open until an explicit close is done
setKeepOpen();
}
XmlRpcClient::XmlRpcClient(const char* host, int port,
const char* login, const char* password,
const char* uri/*=0*/, bool ssl)
{
XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d, login %s.", host, port, login);
_host = host;
_port = port;
if (uri)
_uri = uri;
else
_uri = "/RPC2";
_login = login;
_password = password;
_connectionState = NO_CONNECTION;
_executing = false;
_eof = false;
_ssl = ssl;
if (!_ssl) { _ssl_ssl = (SSL *) NULL; }
// Default to keeping the connection open until an explicit close is done
setKeepOpen();
}
XmlRpcClient::~XmlRpcClient()
{
XmlRpcUtil::log(1, "XmlRpcClient dtor client: host %s, port %d.", _host.c_str(), _port);
if (_connectionState != NO_CONNECTION) close();
}
// Close the owned fd
void
XmlRpcClient::close()
{
XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
_connectionState = NO_CONNECTION;
_disp.exit();
_disp.removeSource(this);
if (_ssl) {
// Pre-socket shutdown
XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_shutdown");
SSL_shutdown(_ssl_ssl);
XmlRpcUtil::log(4, "XmlRpcClient::close: after SSL_shutdown");
}
XmlRpcSource::close();
if (_ssl) {
// Post-socket shutdown
XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_free(_ssl_ssl)");
SSL_free(_ssl_ssl);
XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_CTX_free(_ssl_ctx)");
SSL_CTX_free(_ssl_ctx);
XmlRpcUtil::log(4, "XmlRpcClient::close: SSL shutdown successful!");
}
}
// Clear the referenced flag even if exceptions or errors occur.
struct ClearFlagOnExit {
ClearFlagOnExit(bool& flag) : _flag(flag) {}
~ClearFlagOnExit() { _flag = false; }
bool& _flag;
};
// Execute the named procedure on the remote server.
// Params should be an array of the arguments for the method.
// Returns true if the request was sent and a result received (although the result
// might be a fault).
bool
XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
{
XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
// This is not a thread-safe operation, if you want to do multithreading, use separate
// clients for each thread. If you want to protect yourself from multiple threads
// accessing the same client, replace this code with a real mutex.
if (_executing)
return false;
_executing = true;
ClearFlagOnExit cf(_executing);
_sendAttempts = 0;
_isFault = false;
if ( ! setupConnection())
return false;
if ( ! generateRequest(method, params))
return false;
result.clear();
double msTime = -1.0; // Process until exit is called
_disp.work(msTime);
if (_connectionState != IDLE || ! parseResponse(result))
return false;
XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
_response = "";
return true;
}
// XmlRpcSource interface implementation
// Handle server responses. Called by the event dispatcher during execute.
unsigned
XmlRpcClient::handleEvent(unsigned eventType)
{
if (eventType == XmlRpcDispatch::Exception)
{
//if (XmlRpcSocket::nonFatalError())
// return (_connectionState == WRITE_REQUEST)
// ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
XmlRpcSocket::getErrorMsg().c_str());
else
XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
_connectionState, XmlRpcSocket::getErrorMsg().c_str());
return 0;
}
if (_connectionState == WRITE_REQUEST)
if ( ! writeRequest()) return 0;
if (_connectionState == READ_HEADER)
if ( ! readHeader()) return 0;
if (_connectionState == READ_RESPONSE)
if ( ! readResponse()) return 0;
// This should probably always ask for Exception events too
return (_connectionState == WRITE_REQUEST)
? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
}
// Create the socket connection to the server if necessary
bool
XmlRpcClient::setupConnection()
{
// If an error occurred last time through, or if the server closed the connection, close our end
if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
close();
_eof = false;
if (_connectionState == NO_CONNECTION)
if (! doConnect())
return false;
// Prepare to write the request
_connectionState = WRITE_REQUEST;
_bytesWritten = 0;
// Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
_disp.removeSource(this); // Make sure nothing is left over
_disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
return true;
}
// Connect to the xmlrpc server
bool
XmlRpcClient::doConnect()
{
int fd = XmlRpcSocket::socket();
if (fd < 0)
{
XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
this->setfd(fd);
// Don't block on connect/reads/writes
if ( ! XmlRpcSocket::setNonBlocking(fd))
{
this->close();
XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
if ( ! XmlRpcSocket::connect(fd, _host, _port))
{
this->close();
XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
// Perform SSL if needed
if (_ssl) {
SSLeay_add_ssl_algorithms();
_ssl_meth = SSLv23_client_method();
SSL_load_error_strings();
_ssl_ctx = SSL_CTX_new (_ssl_meth);
_ssl_ssl = SSL_new (_ssl_ctx);
SSL_set_fd (_ssl_ssl, fd);
int err = SSL_connect (_ssl_ssl);
}
return true;
}
// Encode the request to call the specified method with the specified parameters into xml
bool
XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
{
std::string body = REQUEST_BEGIN;
body += methodName;
body += REQUEST_END_METHODNAME;
// If params is an array, each element is a separate parameter
if (params.valid()) {
body += PARAMS_TAG;
if (params.getType() == XmlRpcValue::TypeArray)
{
for (int i=0; i<params.size(); ++i) {
body += PARAM_TAG;
body += params[i].toXml();
body += PARAM_ETAG;
}
}
else
{
body += PARAM_TAG;
body += params.toXml();
body += PARAM_ETAG;
}
body += PARAMS_ETAG;
}
body += REQUEST_END;
std::string header = generateHeader(body);
XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
header.length(), body.length());
_request = header + body;
return true;
}
// Prepend http headers
std::string
XmlRpcClient::generateHeader(std::string const& body)
{
std::string header =
"POST " + _uri + " HTTP/1.1\r\n"
"User-Agent: ";
header += XMLRPC_VERSION;
header += "\r\nHost: ";
header += _host;
char buff[40];
sprintf(buff,":%d\r\n", _port);
header += buff;
if (_login.length() != 0)
{
// convert to base64
std::vector<char> base64data;
int iostatus = 0;
base64<char> encoder;
std::back_insert_iterator<std::vector<char> > ins =
std::back_inserter(base64data);
std::string authBuf = _login + ":" + _password;
encoder.put(authBuf.begin(), authBuf.end(), ins, iostatus,
base64<>::crlf());
header += "Authorization: Basic ";
std::string authEnc(base64data.begin(), base64data.end());
// handle pesky linefeed characters
string::size_type lf;
while ( (lf = authEnc.find("\r")) != string::npos ) {
authEnc.erase(lf, 1);
}
while ( (lf = authEnc.find("\n")) != string::npos ) {
authEnc.erase(lf, 1);
}
header += authEnc;
header += "\r\n";
}
header += "Content-Type: text/xml\r\nContent-length: ";
sprintf(buff,"%d\r\n\r\n", body.size());
return header + buff;
}
bool
XmlRpcClient::writeRequest()
{
if (_bytesWritten == 0)
XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
// Try to write the request
if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten, _ssl_ssl)) {
XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
return false;
}
XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
// Wait for the result
if (_bytesWritten == int(_request.length())) {
_header = "";
_response = "";
_connectionState = READ_HEADER;
}
return true;
}
// Read the header from the response
bool
XmlRpcClient::readHeader()
{
// Read available data
if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof, _ssl_ssl) ||
(_eof && _header.length() == 0)) {
// If we haven't read any data yet and this is a keep-alive connection, the server may
// have timed out, so we try one more time.
if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
XmlRpcSource::close();
_connectionState = NO_CONNECTION;
_eof = false;
return setupConnection();
}
XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
XmlRpcSocket::getErrorMsg().c_str(), getfd());
return false;
}
XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
char *hp = (char*)_header.c_str(); // Start of header
char *ep = hp + _header.length(); // End of string
char *bp = 0; // Start of body
char *lp = 0; // Start of content-length value
for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
lp = cp + 16;
else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
bp = cp + 4;
else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
bp = cp + 2;
}
// If we haven't gotten the entire header yet, return (keep reading)
if (bp == 0) {
if (_eof) // EOF in the middle of a response is an error
{
XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
return false; // Close the connection
}
return true; // Keep reading
}
// Decode content length
if (lp == 0) {
XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
return false; // We could try to figure it out by parsing as we read, but for now...
}
_contentLength = atoi(lp);
if (_contentLength <= 0) {
XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
return false;
}
XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
// Otherwise copy non-header data to response buffer and set state to read response.
_response = bp;
_header = ""; // should parse out any interesting bits from the header (connection, etc)...
_connectionState = READ_RESPONSE;
return true; // Continue monitoring this source
}
bool
XmlRpcClient::readResponse()
{
// If we dont have the entire response yet, read available data
if (int(_response.length()) < _contentLength) {
if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof, _ssl_ssl)) {
XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
return false;
}
// If we haven't gotten the entire _response yet, return (keep reading)
if (int(_response.length()) < _contentLength) {
if (_eof) {
XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
return false;
}
return true;
}
}
// Otherwise, parse and return the result
XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
_connectionState = IDLE;
return false; // Stop monitoring this source (causes return from work)
}
// Convert the response xml into a result value
bool
XmlRpcClient::parseResponse(XmlRpcValue& result)
{
// Parse response xml into result
int offset = 0;
if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
return false;
}
// Expect either <params><param>... or <fault>...
if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true))
{
if ( ! result.fromXml(_response, &offset)) {
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
_response = "";
return false;
}
} else {
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
_response = "";
return false;
}
_response = "";
return result.valid();
}
+148
View File
@@ -0,0 +1,148 @@
#ifndef _XMLRPCCLIENT_H_
#define _XMLRPCCLIENT_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
#include "XmlRpcDispatch.h"
#include "XmlRpcSource.h"
namespace XmlRpc {
// Arguments and results are represented by XmlRpcValues
class XmlRpcValue;
//! A class to send XML RPC requests to a server and return the results.
class XmlRpcClient : public XmlRpcSource {
public:
// Static data
static const char REQUEST_BEGIN[];
static const char REQUEST_END_METHODNAME[];
static const char PARAMS_TAG[];
static const char PARAMS_ETAG[];
static const char PARAM_TAG[];
static const char PARAM_ETAG[];
static const char REQUEST_END[];
// Result tags
static const char METHODRESPONSE_TAG[];
static const char FAULT_TAG[];
//! Construct a client to connect to the server at the specified host:port address
//! @param host The name of the remote machine hosting the server, eg "myserver.mycompany.com"
//! @param port The port on the remote machine where the server is listening
//! @param uri An optional string to be sent as the URI in the HTTP GET header
//! Note that the host is not a URL, do not prepend "http://" or other protocol specifiers.
XmlRpcClient(const char* host, int port, const char* uri=0);
XmlRpcClient(const char* host, int port, const char* uri=0, bool ssl=false);
//! Construct a client to connect to the server at the specified host:port address including HTTP authentication
//! @param host The name of the remote machine hosting the server
//! @param port The port on the remote machine where the server is listening
//! @param login The username passed to the server
//! @param pass The password passed to the server
//! @param uri An optional string to be sent as the URI in the HTTP GET header
XmlRpcClient(const char* host, int port, const char* login, const char* password, const char* uri=0);
XmlRpcClient(const char* host, int port, const char* login, const char* password, const char* uri=0, bool ssl=false);
//! Destructor
virtual ~XmlRpcClient();
//! Execute the named procedure on the remote server.
//! @param method The name of the remote procedure to execute
//! @param params An array of the arguments for the method
//! @param result The result value to be returned to the client
//! @return true if the request was sent and a result received
//! (although the result might be a fault).
//!
//! Currently this is a synchronous (blocking) implementation (execute
//! does not return until it receives a response or an error). Use isFault()
//! to determine whether the result is a fault response.
bool execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result);
//! Returns true if the result of the last execute() was a fault response.
bool isFault() const { return _isFault; }
//! Return the host name of the server
const char* const host() const { return _host.c_str(); }
//! Return the port
int port() const { return _port; }
//! Return the URI
const char* const uri() const { return _uri.c_str(); }
// XmlRpcSource interface implementation
//! Close the connection
virtual void close();
//! Handle server responses. Called by the event dispatcher during execute.
//! @param eventType The type of event that occurred.
//! @see XmlRpcDispatch::EventType
virtual unsigned handleEvent(unsigned eventType);
protected:
// Execution processing helpers
virtual bool doConnect();
virtual bool setupConnection();
virtual bool generateRequest(const char* method, XmlRpcValue const& params);
virtual std::string generateHeader(std::string const& body);
virtual bool writeRequest();
virtual bool readHeader();
virtual bool readResponse();
virtual bool parseResponse(XmlRpcValue& result);
// Possible IO states for the connection
enum ClientConnectionState { NO_CONNECTION, CONNECTING, WRITE_REQUEST, READ_HEADER, READ_RESPONSE, IDLE };
ClientConnectionState _connectionState;
// Server location
std::string _host;
std::string _uri;
int _port;
// Login information for HTTP authentication
std::string _login;
std::string _password;
// The xml-encoded request, http header of response, and response xml
std::string _request;
std::string _header;
std::string _response;
// Number of times the client has attempted to send the request
int _sendAttempts;
// Number of bytes of the request that have been written to the socket so far
int _bytesWritten;
// True if we are currently executing a request. If you want to multithread,
// each thread should have its own client.
bool _executing;
// True if the server closed the connection
bool _eof;
// True if a fault response was returned by the server
bool _isFault;
// Number of bytes expected in the response body (parsed from response header)
int _contentLength;
// Event dispatcher
XmlRpcDispatch _disp;
}; // class XmlRpcClient
} // namespace XmlRpc
#endif // _XMLRPCCLIENT_H_
+268
View File
@@ -0,0 +1,268 @@
#include "XmlRpcDispatch.h"
#include "XmlRpcSource.h"
#include "XmlRpcUtil.h"
#include <errno.h>
#include <math.h>
#include <sys/timeb.h>
#if defined(_WINDOWS)
# include <winsock2.h>
# define USE_FTIME
# if defined(_MSC_VER)
# define timeb _timeb
# define ftime _ftime
# endif
#else
# include <sys/time.h>
#endif // _WINDOWS
using namespace XmlRpc;
XmlRpcDispatch::XmlRpcDispatch()
{
_endTime = -1.0;
_doClear = false;
_inWork = false;
}
XmlRpcDispatch::~XmlRpcDispatch()
{
}
// Monitor this source for the specified events and call its event handler
// when the event occurs
void
XmlRpcDispatch::addSource(XmlRpcSource* source, unsigned mask)
{
_sources.push_back(MonitoredSource(source, mask));
}
// Stop monitoring this source. Does not close the source.
void
XmlRpcDispatch::removeSource(XmlRpcSource* source)
{
for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
if (it->getSource() == source)
{
_sources.erase(it);
break;
}
}
// Modify the types of events to watch for on this source
void
XmlRpcDispatch::setSourceEvents(XmlRpcSource* source, unsigned eventMask)
{
for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
if (it->getSource() == source)
{
it->getMask() = eventMask;
break;
}
}
// Watch current set of sources and process events
void
XmlRpcDispatch::work(double timeout)
{
// Compute end time
double timeNow = getTime();
_endTime = (timeout < 0.0) ? -1.0 : (timeNow + timeout);
_doClear = false;
_inWork = true;
// Only work while there is something to monitor
while (_sources.size() > 0) {
// Wait for and dispatch events
if ( ! waitForAndProcessEvents(timeout))
{
_inWork = false;
return;
}
// Check whether to clear all sources
if (_doClear)
{
SourceList closeList = _sources;
_sources.clear();
for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) {
XmlRpcSource *src = it->getSource();
src->close();
}
_doClear = false;
}
// Check whether end time has passed or exit has been called
if (_endTime == 0.0) // Exit
break;
else if (_endTime > 0.0) // Check for timeout
{
double t = getTime();
if (t > _endTime)
break;
// Decrement timeout by elapsed time
timeout -= (t - timeNow);
if (timeout < 0.0)
timeout = 0.0; // Shouldn't happen but its fp math...
timeNow = t;
}
}
_inWork = false;
}
// Exit from work routine. Presumably this will be called from
// one of the source event handlers.
void
XmlRpcDispatch::exit()
{
_endTime = 0.0; // Return from work asap
}
// Clear all sources from the monitored sources list
void
XmlRpcDispatch::clear()
{
if (_inWork)
_doClear = true; // Finish reporting current events before clearing
else
{
SourceList closeList = _sources;
_sources.clear();
for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it)
it->getSource()->close();
}
}
// Time utility
double
XmlRpcDispatch::getTime()
{
#ifdef USE_FTIME
struct timeb tbuff;
ftime(&tbuff);
return ((double) tbuff.time + ((double)tbuff.millitm / 1000.0) +
((double) tbuff.timezone * 60));
#else
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return (tv.tv_sec + tv.tv_usec / 1000000.0);
#endif /* USE_FTIME */
}
// Wait for I/O on any source, timeout, or interrupt signal.
bool
XmlRpcDispatch::waitForAndProcessEvents(double timeout)
{
#if defined(_WINDOWS) && 0
int nHandles = 0;
SourceList::iterator it;
for (it=_sources.begin(); it!=_sources.end(); ++it) {
int fd = it->getSource()->getfd();
int mask = 0;
if (it->getMask() & ReadableEvent) mask = (FD_READ | FD_CLOSE | FD_ACCEPT);
if (it->getMask() & WritableEvent) mask |= (FD_WRITE | FD_CLOSE);
#else // Posix
// Construct the sets of descriptors we are interested in
fd_set inFd, outFd, excFd;
FD_ZERO(&inFd);
FD_ZERO(&outFd);
FD_ZERO(&excFd);
int maxFd = -1;
SourceList::iterator it;
for (it=_sources.begin(); it!=_sources.end(); ++it) {
int fd = it->getSource()->getfd();
if (it->getMask() & ReadableEvent) FD_SET(fd, &inFd);
if (it->getMask() & WritableEvent) FD_SET(fd, &outFd);
if (it->getMask() & Exception) FD_SET(fd, &excFd);
if (it->getMask() && fd > maxFd) maxFd = fd;
}
// Check for events
int nEvents;
if (_endTime < 0.0)
nEvents = select(maxFd+1, &inFd, &outFd, &excFd, NULL);
else
{
struct timeval tv;
tv.tv_sec = (int)floor(timeout);
tv.tv_usec = ((int)floor(1000000.0 * (timeout-floor(timeout)))) % 1000000;
nEvents = select(maxFd+1, &inFd, &outFd, &excFd, &tv);
}
if (nEvents < 0 && errno != EINTR)
{
XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents);
return false;
}
// Process events
for (it=_sources.begin(); it != _sources.end(); )
{
SourceList::iterator thisIt = it++;
XmlRpcSource* src = thisIt->getSource();
int fd = src->getfd();
if (fd <= maxFd) {
// handleEvent is called once per event type signalled
unsigned newMask = 0;
int nset = 0;
if (FD_ISSET(fd, &inFd))
{
newMask |= src->handleEvent(ReadableEvent);
++nset;
}
if (FD_ISSET(fd, &outFd))
{
newMask |= src->handleEvent(WritableEvent);
++nset;
}
if (FD_ISSET(fd, &excFd))
{
newMask |= src->handleEvent(Exception);
++nset;
}
// Some event occurred
if (nset)
{
if (newMask)
thisIt->getMask() = newMask;
else // Stop monitoring this one
{
_sources.erase(thisIt);
if ( ! src->getKeepOpen())
src->close();
}
}
}
}
#endif
return true;
}
+93
View File
@@ -0,0 +1,93 @@
#ifndef _XMLRPCDISPATCH_H_
#define _XMLRPCDISPATCH_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <list>
#endif
namespace XmlRpc {
// An RPC source represents a file descriptor to monitor
class XmlRpcSource;
//! An object which monitors file descriptors for events and performs
//! callbacks when interesting events happen.
class XmlRpcDispatch {
public:
//! Constructor
XmlRpcDispatch();
~XmlRpcDispatch();
//! Values indicating the type of events a source is interested in
enum EventType {
ReadableEvent = 1, //!< data available to read
WritableEvent = 2, //!< connected/data can be written without blocking
Exception = 4 //!< uh oh
};
//! Monitor this source for the event types specified by the event mask
//! and call its event handler when any of the events occur.
//! @param source The source to monitor
//! @param eventMask Which event types to watch for. \see EventType
void addSource(XmlRpcSource* source, unsigned eventMask);
//! Stop monitoring this source.
//! @param source The source to stop monitoring
//! The source socket is not closed.
void removeSource(XmlRpcSource* source);
//! Modify the types of events to watch for on this source
void setSourceEvents(XmlRpcSource* source, unsigned eventMask);
//! Watch current set of sources and process events for the specified
//! duration (in ms, -1 implies wait forever, or until exit is called)
void work(double msTime);
//! Exit from work routine
void exit();
//! Clear all sources from the monitored sources list. Sources are closed.
void clear();
protected:
//! Wait for I/O on any source, timeout, or interrupt signal.
bool waitForAndProcessEvents(double timeout);
// helper
double getTime();
// A source to monitor and what to monitor it for
struct MonitoredSource {
MonitoredSource(XmlRpcSource* src, unsigned mask) : _src(src), _mask(mask) {}
XmlRpcSource* getSource() const { return _src; }
unsigned& getMask() { return _mask; }
XmlRpcSource* _src;
unsigned _mask;
};
// A list of sources to monitor
typedef std::list< MonitoredSource > SourceList;
// Sources being monitored
SourceList _sources;
// When work should stop (-1 implies wait forever, or until exit is called)
double _endTime;
bool _doClear;
bool _inWork;
};
} // namespace XmlRpc
#endif // _XMLRPCDISPATCH_H_
+42
View File
@@ -0,0 +1,42 @@
#ifndef _XMLRPCEXCEPTION_H_
#define _XMLRPCEXCEPTION_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
namespace XmlRpc {
//! A class representing an error.
//! If server methods throw this exception, a fault response is returned
//! to the client.
class XmlRpcException {
public:
//! Constructor
//! @param message A descriptive error message
//! @param code An integer error code
XmlRpcException(const std::string& message, int code=-1) :
_message(message), _code(code) {}
//! Return the error message.
const std::string& getMessage() const { return _message; }
//! Return the error code.
int getCode() const { return _code; }
private:
std::string _message;
int _code;
};
}
#endif // _XMLRPCEXCEPTION_H_
+60
View File
@@ -0,0 +1,60 @@
#if defined(XMLRPC_THREADS)
#include "XmlRpcMutex.h"
#if defined(_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else
# include <pthread.h>
#endif
using namespace XmlRpc;
//! Destructor.
XmlRpcMutex::~XmlRpcMutex()
{
if (_pMutex)
{
#if defined(_WINDOWS)
::CloseHandle((HANDLE)_pMutex);
#else
::pthread_mutex_destroy((pthread_mutex_t*)_pMutex);
delete _pMutex;
#endif
_pMutex = 0;
}
}
//! Wait for the mutex to be available and then acquire the lock.
void XmlRpcMutex::acquire()
{
#if defined(_WINDOWS)
if ( ! _pMutex)
_pMutex = ::CreateMutex(0, TRUE, 0);
else
::WaitForSingleObject(_pMutex, INFINITE);
#else
if ( ! _pMutex)
{
_pMutex = new pthread_mutex_t;
::pthread_mutex_init((pthread_mutex_t*)_pMutex, 0);
}
::pthread_mutex_lock((pthread_mutex_t*)_pMutex);
#endif
}
//! Release the mutex.
void XmlRpcMutex::release()
{
if (_pMutex)
#if defined(_WINDOWS)
::ReleaseMutex(_pMutex);
#else
::pthread_mutex_unlock((pthread_mutex_t*)_pMutex);
#endif
}
#endif // XMLRPC_THREADS
+46
View File
@@ -0,0 +1,46 @@
#ifndef _XMLRPCMUTEX_H_
#define _XMLRPCMUTEX_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
namespace XmlRpc {
//! A simple platform-independent mutex API implemented for posix and windows.
class XmlRpcMutex {
public:
//! Construct a Mutex object.
XmlRpcMutex() : _pMutex(0) {}
//! Destroy a Mutex object.
~XmlRpcMutex();
//! Wait for the mutex to be available and then acquire the lock.
void acquire();
//! Release the mutex.
void release();
//! Utility class to acquire a mutex at construction and release it when destroyed.
struct AutoLock {
//! Acquire the mutex at construction
AutoLock(XmlRpcMutex& m) : _m(m) { _m.acquire(); }
//! Release at destruction
~AutoLock() { _m.release(); }
//! The mutex being held
XmlRpcMutex& _m;
};
private:
//! Native Mutex object
void* _pMutex;
}; // class XmlRpcMutex
} // namespace XmlRpc
#endif // _XMLRPCMUTEX_H_
+494
View File
@@ -0,0 +1,494 @@
#include "XmlRpcServer.h"
#include "XmlRpcServerConnection.h"
#include "XmlRpcServerMethod.h"
#include "XmlRpcSocket.h"
#include "XmlRpcUtil.h"
#include "XmlRpcException.h"
#include "XmlRpc.h"
using namespace XmlRpc;
// Static data
const char XmlRpcServer::METHODNAME_TAG[] = "<methodName>";
const char XmlRpcServer::PARAMS_TAG[] = "<params>";
const char XmlRpcServer::PARAMS_ETAG[] = "</params>";
const char XmlRpcServer::PARAM_TAG[] = "<param>";
const char XmlRpcServer::PARAM_ETAG[] = "</param>";
const std::string XmlRpcServer::METHODNAME = "methodName";
const std::string XmlRpcServer::PARAMS = "params";
const std::string XmlRpcServer::FAULTCODE = "faultCode";
const std::string XmlRpcServer::FAULTSTRING = "faultString";
XmlRpcServer::XmlRpcServer()
{
_introspectionEnabled = false;
_listMethods = 0;
_methodHelp = 0;
}
XmlRpcServer::~XmlRpcServer()
{
this->shutdown();
_methods.clear();
delete _listMethods;
delete _methodHelp;
}
// Add a command to the RPC server
void
XmlRpcServer::addMethod(XmlRpcServerMethod* method)
{
_methods[method->name()] = method;
}
// Remove a command from the RPC server
void
XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
{
MethodMap::iterator i = _methods.find(method->name());
if (i != _methods.end())
_methods.erase(i);
}
// Remove a command from the RPC server by name
void
XmlRpcServer::removeMethod(const std::string& methodName)
{
MethodMap::iterator i = _methods.find(methodName);
if (i != _methods.end())
_methods.erase(i);
}
// Look up a method by name
XmlRpcServerMethod*
XmlRpcServer::findMethod(const std::string& name) const
{
MethodMap::const_iterator i = _methods.find(name);
if (i == _methods.end())
return 0;
return i->second;
}
// Create a socket, bind to the specified port, and
// set it in listen mode to make it available for clients.
bool
XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
{
int fd = XmlRpcSocket::socket();
if (fd < 0)
{
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
this->setfd(fd);
// Don't block on reads/writes
if ( ! XmlRpcSocket::setNonBlocking(fd))
{
this->close();
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
// Allow this port to be re-bound immediately so server re-starts are not delayed
if ( ! XmlRpcSocket::setReuseAddr(fd))
{
this->close();
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
// Bind to the specified port on the default interface
if ( ! XmlRpcSocket::bind(fd, port))
{
this->close();
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
// Set in listening mode
if ( ! XmlRpcSocket::listen(fd, backlog))
{
this->close();
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
return false;
}
XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd);
// Notify the dispatcher to listen on this source when we are in work()
_disp.addSource(this, XmlRpcDispatch::ReadableEvent);
return true;
}
// Get port number that this server is listening on
int
XmlRpcServer::getPort(void) const
{
return XmlRpcSocket::getPort(getfd());
}
// Process client requests for the specified time
void
XmlRpcServer::work(double msTime)
{
XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
_disp.work(msTime);
}
// Handle input on the server socket by accepting the connection
// and reading the rpc request.
unsigned
XmlRpcServer::handleEvent(unsigned mask)
{
acceptConnection();
return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd
}
// Accept a client connection request and create a connection to
// handle method calls from the client.
void
XmlRpcServer::acceptConnection()
{
int s = XmlRpcSocket::accept(this->getfd());
XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
if (s < 0)
{
//this->close();
XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
}
else if ( ! XmlRpcSocket::setNonBlocking(s))
{
XmlRpcSocket::close(s);
XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
}
else // Notify the dispatcher to listen for input on this source when we are in work()
{
XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
XmlRpcServerConnection* c = this->createConnection(s);
if (c) this->dispatchConnection(c);
}
}
// Create a new connection object for processing requests from a specific client.
XmlRpcServerConnection*
XmlRpcServer::createConnection(int s)
{
// Specify that the connection object be deleted when it is closed
return new XmlRpcServerConnection(s, this, true);
}
// Hand off a new connection to a dispatcher
void
XmlRpcServer::dispatchConnection(XmlRpcServerConnection* sc)
{
_disp.addSource(sc, XmlRpcDispatch::ReadableEvent);
}
// Remove a connection. Called by the connection when it closes down.
void
XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
{
_disp.removeSource(sc);
}
// Stop processing client requests
void
XmlRpcServer::exit()
{
_disp.exit();
}
// Close the server socket file descriptor and stop monitoring connections
void
XmlRpcServer::shutdown()
{
// This closes and destroys all connections as well as closing this socket
_disp.clear();
}
// Introspection support
static const std::string LIST_METHODS("system.listMethods");
static const std::string METHOD_HELP("system.methodHelp");
static const std::string MULTICALL("system.multicall");
// List all methods available on a server
class ListMethods : public XmlRpcServerMethod
{
public:
ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
void execute(XmlRpcValue& params, XmlRpcValue& result)
{
_server->listMethods(result);
}
std::string help() { return std::string("List all methods available on a server as an array of strings"); }
};
// Retrieve the help string for a named method
class MethodHelp : public XmlRpcServerMethod
{
public:
MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
void execute(XmlRpcValue& params, XmlRpcValue& result)
{
if (params[0].getType() != XmlRpcValue::TypeString)
throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
XmlRpcServerMethod* m = _server->findMethod(params[0]);
if ( ! m)
throw XmlRpcException(METHOD_HELP + ": Unknown method name");
result = m->help();
}
std::string help() { return std::string("Retrieve the help string for a named method"); }
};
// Specify whether introspection is enabled or not. Default is enabled.
void
XmlRpcServer::enableIntrospection(bool enabled)
{
if (_introspectionEnabled == enabled)
return;
_introspectionEnabled = enabled;
if (enabled)
{
if ( ! _listMethods)
{
_listMethods = new ListMethods(this);
_methodHelp = new MethodHelp(this);
} else {
addMethod(_listMethods);
addMethod(_methodHelp);
}
}
else
{
removeMethod(LIST_METHODS);
removeMethod(METHOD_HELP);
}
}
void
XmlRpcServer::listMethods(XmlRpcValue& result)
{
int i = 0;
result.setSize(_methods.size()+1);
for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
result[i++] = it->first;
// Multicall support is built into XmlRpcServer::executeRequest
result[i] = MULTICALL;
}
// Parse the request, run the method, generate a response string.
std::string
XmlRpcServer::executeRequest(std::string const& request)
{
XmlRpcValue params, resultValue;
std::string methodName = parseRequest(request, params);
XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: server calling method '%s'",
methodName.c_str());
std::string response;
try {
if ( ! executeMethod(methodName, params, resultValue) &&
! executeMulticall(methodName, params, resultValue))
response = generateFaultResponse(methodName + ": unknown method name");
else
response = generateResponse(resultValue.toXml());
} catch (const XmlRpcException& fault) {
XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: fault %s.",
fault.getMessage().c_str());
response = generateFaultResponse(fault.getMessage(), fault.getCode());
}
return response;
}
// Parse the method name and the argument values from the request.
std::string
XmlRpcServer::parseRequest(std::string const& request, XmlRpcValue& params)
{
int offset = 0; // Number of chars parsed from the request
std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, request, &offset);
if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, request, &offset))
{
int nArgs = 0;
while (XmlRpcUtil::nextTagIs(PARAM_TAG, request, &offset)) {
params[nArgs++] = XmlRpcValue(request, &offset);
(void) XmlRpcUtil::nextTagIs(PARAM_ETAG, request, &offset);
}
(void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, request, &offset);
}
return methodName;
}
// Execute a named method with the specified params.
bool
XmlRpcServer::executeMethod(const std::string& methodName,
XmlRpcValue& params,
XmlRpcValue& result)
{
XmlRpcServerMethod* method = findMethod(methodName);
if ( ! method) return false;
method->execute(params, result);
// Ensure a valid result value
if ( ! result.valid())
result = std::string();
return true;
}
// Execute multiple calls and return the results in an array.
bool
XmlRpcServer::executeMulticall(const std::string& methodName,
XmlRpcValue& params,
XmlRpcValue& result)
{
if (methodName != MULTICALL) return false;
// There ought to be 1 parameter, an array of structs
if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
throw XmlRpcException(MULTICALL + ": Invalid argument (expected an array)");
int nc = params[0].size();
result.setSize(nc);
for (int i=0; i<nc; ++i) {
if ( ! params[0][i].hasMember(METHODNAME) ||
! params[0][i].hasMember(PARAMS)) {
result[i][FAULTCODE] = -1;
result[i][FAULTSTRING] = MULTICALL +
": Invalid argument (expected a struct with members methodName and params)";
continue;
}
const std::string& methodName = params[0][i][METHODNAME];
XmlRpcValue& methodParams = params[0][i][PARAMS];
XmlRpcValue resultValue;
resultValue.setSize(1);
try {
if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
! executeMulticall(methodName, params, resultValue[0]))
{
result[i][FAULTCODE] = -1;
result[i][FAULTSTRING] = methodName + ": unknown method name";
}
else
result[i] = resultValue;
} catch (const XmlRpcException& fault) {
result[i][FAULTCODE] = fault.getCode();
result[i][FAULTSTRING] = fault.getMessage();
}
}
return true;
}
// Create a response from results xml
std::string
XmlRpcServer::generateResponse(std::string const& resultXml)
{
const char RESPONSE_1[] =
"<?xml version=\"1.0\"?>\r\n"
"<methodResponse><params><param>\r\n\t";
const char RESPONSE_2[] =
"\r\n</param></params></methodResponse>\r\n";
std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
std::string header = generateHeader(body);
std::string response = header + body;
XmlRpcUtil::log(5, "XmlRpcServer::generateResponse:\n%s\n", response.c_str());
return response;
}
// Prepend http headers
std::string
XmlRpcServer::generateHeader(std::string const& body)
{
std::string header =
"HTTP/1.1 200 OK\r\n"
"Server: ";
header += XMLRPC_VERSION;
header += "\r\n"
"Content-Type: text/xml\r\n"
"Content-length: ";
char buffLen[40];
sprintf(buffLen,"%d\r\n\r\n", body.size());
return header + buffLen;
}
std::string
XmlRpcServer::generateFaultResponse(std::string const& errorMsg, int errorCode)
{
const char RESPONSE_1[] =
"<?xml version=\"1.0\"?>\r\n"
"<methodResponse><fault>\r\n\t";
const char RESPONSE_2[] =
"\r\n</fault></methodResponse>\r\n";
XmlRpcValue faultStruct;
faultStruct[FAULTCODE] = errorCode;
faultStruct[FAULTSTRING] = errorMsg;
std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
std::string header = generateHeader(body);
return header + body;
}
+160
View File
@@ -0,0 +1,160 @@
#ifndef _XMLRPCSERVER_H_
#define _XMLRPCSERVER_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <map>
# include <string>
#endif
#include "XmlRpcDispatch.h"
#include "XmlRpcSource.h"
namespace XmlRpc {
// An abstract class supporting XML RPC methods
class XmlRpcServerMethod;
// Class representing connections to specific clients
class XmlRpcServerConnection;
// Class representing argument and result values
class XmlRpcValue;
//! A class to handle XML RPC requests
class XmlRpcServer : public XmlRpcSource {
public:
//! Create a server object.
XmlRpcServer();
//! Destructor.
virtual ~XmlRpcServer();
//! Specify whether introspection is enabled or not. Default is not enabled.
void enableIntrospection(bool enabled=true);
//! Add a command to the RPC server
void addMethod(XmlRpcServerMethod* method);
//! Remove a command from the RPC server
void removeMethod(XmlRpcServerMethod* method);
//! Remove a command from the RPC server by name
void removeMethod(const std::string& methodName);
//! Look up a method by name
XmlRpcServerMethod* findMethod(const std::string& name) const;
//! Create a socket, bind to the specified port, and
//! set it in listen mode to make it available for clients.
//! @param port The port to bind and listen on (zero to choose an arbitrary port)
bool bindAndListen(int port, int backlog = 5);
//! Get the port number this server is listening on.
int getPort(void) const;
//! Process client requests for the specified time
void work(double msTime);
//! Temporarily stop processing client requests and exit the work() method.
void exit();
//! Close all connections with clients and the socket file descriptor
void shutdown();
//! Introspection support
void listMethods(XmlRpcValue& result);
//! Parses the request xml, runs the method, generates the response (header+xml).
//! Returns a fault response if an error occurs during method execution.
virtual std::string executeRequest(std::string const& request);
// XmlRpcSource interface implementation
//! Handle client connection requests
virtual unsigned handleEvent(unsigned eventType);
//! Remove a connection from the dispatcher
virtual void removeConnection(XmlRpcServerConnection*);
protected:
// Static data
static const char METHODNAME_TAG[];
static const char PARAMS_TAG[];
static const char PARAMS_ETAG[];
static const char PARAM_TAG[];
static const char PARAM_ETAG[];
static const std::string SYSTEM_MULTICALL;
static const std::string METHODNAME;
static const std::string PARAMS;
static const std::string FAULTCODE;
static const std::string FAULTSTRING;
//! Accept a client connection request
virtual void acceptConnection();
//! Create a new connection object for processing requests from a specific client.
//! If the client is not authorized to connect, close the socket and return 0.
virtual XmlRpcServerConnection* createConnection(int socket);
//! Hand off a new connection object to a dispatcher.
virtual void dispatchConnection(XmlRpcServerConnection* sc);
//! Parse the methodName and parameters from the request.
//! @returns the methodName
std::string parseRequest(std::string const& request, XmlRpcValue& params);
//! Execute a named method with the specified params.
bool executeMethod(const std::string& methodName, XmlRpcValue& params, XmlRpcValue& result);
//! Execute multiple calls and return the results in an array.
//! System.multicall implementation
bool executeMulticall(const std::string& methodName, XmlRpcValue& params, XmlRpcValue& result);
//! Construct a response from the result XML.
std::string generateResponse(std::string const& resultXml);
//! Construct a fault response.
std::string generateFaultResponse(std::string const& msg, int errorCode = -1);
//! Return the appropriate headers for the response.
std::string generateHeader(std::string const& body);
//! Whether the introspection API is supported by this server
bool _introspectionEnabled;
//! Event dispatcher
XmlRpcDispatch _disp;
//! Collection of methods. This could be a set keyed on method name if we wanted...
typedef std::map< std::string, XmlRpcServerMethod* > MethodMap;
//! Registered RPC methods.
MethodMap _methods;
//! List all registered RPC methods (only available if introspection is enabled)
XmlRpcServerMethod* _listMethods;
//! Return help string for a specified method (only available if introspection is enabled)
XmlRpcServerMethod* _methodHelp;
};
} // namespace XmlRpc
#endif //_XMLRPCSERVER_H_
+205
View File
@@ -0,0 +1,205 @@
#include "XmlRpcServerConnection.h"
#include "XmlRpcSocket.h"
#ifndef MAKEDEPEND
# include <stdio.h>
# include <stdlib.h>
#endif
#include "XmlRpcDispatch.h"
#include "XmlRpcServer.h"
#include "XmlRpcUtil.h"
using namespace XmlRpc;
// The server delegates handling client requests to a serverConnection object.
XmlRpcServerConnection::XmlRpcServerConnection(int fd,
XmlRpcServer* server,
bool deleteOnClose /*= false*/) :
XmlRpcSource(fd, deleteOnClose)
{
XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
_server = server;
_connectionState = READ_HEADER;
_keepAlive = true;
}
XmlRpcServerConnection::~XmlRpcServerConnection()
{
XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
_server->removeConnection(this);
}
// Handle input on the server socket by accepting the connection
// and reading the rpc request. Return true to continue to monitor
// the socket for events, false to remove it from the dispatcher.
unsigned
XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
{
if (_connectionState == READ_HEADER)
if ( ! readHeader()) return 0;
if (_connectionState == READ_REQUEST)
if ( ! readRequest()) return 0;
if (_connectionState == WRITE_RESPONSE)
if ( ! writeResponse()) return 0;
return (_connectionState == WRITE_RESPONSE)
? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
}
bool
XmlRpcServerConnection::readHeader()
{
// Read available data
bool eof;
if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof, NULL)) { // was _ssl_ssl
// Its only an error if we already have read some data
if (_header.length() > 0)
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
return false;
}
XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
char *hp = (char*)_header.c_str(); // Start of header
char *ep = hp + _header.length(); // End of string
char *bp = 0; // Start of body
char *lp = 0; // Start of content-length value
char *kp = 0; // Start of connection value
for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
lp = cp + 16;
else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
kp = cp + 12;
else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
bp = cp + 4;
else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
bp = cp + 2;
}
// If we haven't gotten the entire header yet, return (keep reading)
if (bp == 0) {
// EOF in the middle of a request is an error, otherwise its ok
if (eof) {
XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
if (_header.length() > 0)
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
return false; // Either way we close the connection
}
return true; // Keep reading
}
// Decode content length
if (lp == 0) {
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
return false; // We could try to figure it out by parsing as we read, but for now...
}
_contentLength = atoi(lp);
if (_contentLength <= 0) {
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
return false;
}
XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
// Otherwise copy non-header data to request buffer and set state to read request.
_request = bp;
// Parse out any interesting bits from the header (HTTP version, connection)
_keepAlive = true;
if (_header.find("HTTP/1.0") != std::string::npos) {
if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
_keepAlive = false; // Default for HTTP 1.0 is to close the connection
} else {
if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
_keepAlive = false;
}
XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
_header = "";
_connectionState = READ_REQUEST;
return true; // Continue monitoring this source
}
bool
XmlRpcServerConnection::readRequest()
{
// If we dont have the entire request yet, read available data
if (int(_request.length()) < _contentLength) {
bool eof;
if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof, NULL)) { // was _ssl_ssl
XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
return false;
}
// If we haven't gotten the entire request yet, return (keep reading)
if (int(_request.length()) < _contentLength) {
if (eof) {
XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
return false; // Either way we close the connection
}
return true;
}
}
// Otherwise, parse and dispatch the request
XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
//XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
_connectionState = WRITE_RESPONSE;
return true; // Continue monitoring this source
}
bool
XmlRpcServerConnection::writeResponse()
{
if (_response.length() == 0) {
executeRequest();
_bytesWritten = 0;
if (_response.length() == 0) {
XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
return false;
}
}
// Try to write the response
if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten, NULL)) { // was _ssl_ssl
XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
return false;
}
XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
// Prepare to read the next request
if (_bytesWritten == int(_response.length())) {
_header = "";
_request = "";
_response = "";
_connectionState = READ_HEADER;
}
return _keepAlive; // Continue monitoring this source if true
}
//! Helper method to execute the client request
void XmlRpcServerConnection::executeRequest()
{
_response = _server->executeRequest(_request);
}
+82
View File
@@ -0,0 +1,82 @@
#ifndef _XMLRPCSERVERCONNECTION_H_
#define _XMLRPCSERVERCONNECTION_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
#include "XmlRpcValue.h"
#include "XmlRpcSource.h"
namespace XmlRpc {
// The server waits for client connections and provides support for executing methods
class XmlRpcServer;
class XmlRpcServerMethod;
//! A class to handle XML RPC requests from a particular client
class XmlRpcServerConnection : public XmlRpcSource {
public:
//! Constructor
XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose = false);
//! Destructor
virtual ~XmlRpcServerConnection();
// XmlRpcSource interface implementation
//! Handle IO on the client connection socket.
//! @param eventType Type of IO event that occurred. @see XmlRpcDispatch::EventType.
virtual unsigned handleEvent(unsigned eventType);
protected:
//! Reads the http header
bool readHeader();
//! Reads the request (based on the content-length header value)
bool readRequest();
//! Executes the request and writes the resulting response
bool writeResponse();
//! Helper method to execute the client request
virtual void executeRequest();
//! The XmlRpc server that accepted this connection
XmlRpcServer* _server;
//! Possible IO states for the connection
enum ServerConnectionState { READ_HEADER, READ_REQUEST, WRITE_RESPONSE };
//! Current IO state for the connection
ServerConnectionState _connectionState;
//! Request headers
std::string _header;
//! Number of bytes expected in the request body (parsed from header)
int _contentLength;
//! Request body
std::string _request;
//! Response
std::string _response;
//! Number of bytes of the response written so far
int _bytesWritten;
//! Whether to keep the current client connection open for further requests
bool _keepAlive;
};
} // namespace XmlRpc
#endif // _XMLRPCSERVERCONNECTION_H_
+21
View File
@@ -0,0 +1,21 @@
#include "XmlRpcServerMethod.h"
#include "XmlRpcServer.h"
namespace XmlRpc {
XmlRpcServerMethod::XmlRpcServerMethod(std::string const& name, XmlRpcServer* server)
{
_name = name;
_server = server;
if (_server) _server->addMethod(this);
}
XmlRpcServerMethod::~XmlRpcServerMethod()
{
if (_server) _server->removeMethod(this);
}
} // namespace XmlRpc
+47
View File
@@ -0,0 +1,47 @@
#ifndef _XMLRPCSERVERMETHOD_H_
#define _XMLRPCSERVERMETHOD_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
namespace XmlRpc {
// Representation of a parameter or result value
class XmlRpcValue;
// The XmlRpcServer processes client requests to call RPCs
class XmlRpcServer;
//! Abstract class representing a single RPC method
class XmlRpcServerMethod {
public:
//! Constructor
XmlRpcServerMethod(std::string const& name, XmlRpcServer* server = 0);
//! Destructor
virtual ~XmlRpcServerMethod();
//! Returns the name of the method
std::string& name() { return _name; }
//! Execute the method. Subclasses must provide a definition for this method.
virtual void execute(XmlRpcValue& params, XmlRpcValue& result) = 0;
//! Returns a help string for the method.
//! Subclasses should define this method if introspection is being used.
virtual std::string help() { return std::string(); }
protected:
std::string _name;
XmlRpcServer* _server;
};
} // namespace XmlRpc
#endif // _XMLRPCSERVERMETHOD_H_
+285
View File
@@ -0,0 +1,285 @@
#include "XmlRpcSocket.h"
#include "XmlRpcUtil.h"
#ifndef MAKEDEPEND
#if defined(_WINDOWS)
# include <stdio.h>
# include <winsock2.h>
//# pragma lib(WS2_32.lib)
# define EINPROGRESS WSAEINPROGRESS
# define EWOULDBLOCK WSAEWOULDBLOCK
# define ETIMEDOUT WSAETIMEDOUT
typedef int socklen_t;
#else
extern "C" {
# include <unistd.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <errno.h>
# include <fcntl.h>
}
#endif // _WINDOWS
#endif // MAKEDEPEND
using namespace XmlRpc;
#if defined(_WINDOWS)
static void initWinSock()
{
static bool wsInit = false;
if (! wsInit)
{
WORD wVersionRequested = MAKEWORD( 2, 0 );
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
wsInit = true;
}
}
#else
#define initWinSock()
#endif // _WINDOWS
// These errors are not considered fatal for an IO operation; the operation will be re-tried.
bool
XmlRpcSocket::nonFatalError()
{
int err = XmlRpcSocket::getError();
return (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
}
int
XmlRpcSocket::socket()
{
initWinSock();
return (int) ::socket(AF_INET, SOCK_STREAM, 0);
}
void
XmlRpcSocket::close(int fd)
{
XmlRpcUtil::log(4, "XmlRpcSocket::close: fd %d.", fd);
#if defined(_WINDOWS)
closesocket(fd);
#else
::close(fd);
#endif // _WINDOWS
}
bool
XmlRpcSocket::setNonBlocking(int fd)
{
#if defined(_WINDOWS)
unsigned long flag = 1;
return (ioctlsocket((SOCKET)fd, FIONBIO, &flag) == 0);
#else
return (fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
#endif // _WINDOWS
}
bool
XmlRpcSocket::setReuseAddr(int fd)
{
// Allow this port to be re-bound immediately so server re-starts are not delayed
int sflag = 1;
return (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&sflag, sizeof(sflag)) == 0);
}
// Bind to a specified port
bool
XmlRpcSocket::bind(int fd, int port)
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons((u_short) port);
return (::bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) == 0);
}
// Set socket in listen mode
bool
XmlRpcSocket::listen(int fd, int backlog)
{
return (::listen(fd, backlog) == 0);
}
int
XmlRpcSocket::accept(int fd)
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
return (int) ::accept(fd, (struct sockaddr*)&addr, &addrlen);
}
// Connect a socket to a server (from a client)
bool
XmlRpcSocket::connect(int fd, std::string& host, int port)
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
struct hostent *hp = gethostbyname(host.c_str());
if (hp == 0) return false;
saddr.sin_family = hp->h_addrtype;
memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
saddr.sin_port = htons((u_short) port);
// For asynch operation, this will return EWOULDBLOCK (windows) or
// EINPROGRESS (linux) and we just need to wait for the socket to be writable...
int result = ::connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
return result == 0 || nonFatalError();
}
// Read available text from the specified socket. Returns false on error.
bool
XmlRpcSocket::nbRead(int fd, std::string& s, bool *eof, SSL* ssl)
{
const int READ_SIZE = 4096; // Number of bytes to attempt to read at a time
char readBuf[READ_SIZE];
bool wouldBlock = false;
*eof = false;
while ( ! wouldBlock && ! *eof) {
#if defined(_WINDOWS)
int n = recv(fd, readBuf, READ_SIZE-1, 0);
#else
int n;
if (ssl != (SSL *) NULL) {
n = SSL_read(ssl, readBuf, READ_SIZE-1);
} else {
n = read(fd, readBuf, READ_SIZE-1);
}
#endif
XmlRpcUtil::log(5, "XmlRpcSocket::nbRead: read/recv returned %d.", n);
if (n > 0) {
readBuf[n] = 0;
s.append(readBuf, n);
} else if (n == 0) {
*eof = true;
} else if (nonFatalError()) {
wouldBlock = true;
} else {
return false; // Error
}
}
return true;
}
// Write text to the specified socket. Returns false on error.
bool
XmlRpcSocket::nbWrite(int fd, std::string& s, int *bytesSoFar, SSL* ssl)
{
int nToWrite = int(s.length()) - *bytesSoFar;
char *sp = const_cast<char*>(s.c_str()) + *bytesSoFar;
bool wouldBlock = false;
while ( nToWrite > 0 && ! wouldBlock ) {
#if defined(_WINDOWS)
int n = send(fd, sp, nToWrite, 0);
#else
int n;
if (ssl != (SSL *) NULL) {
n = SSL_write(ssl, sp, nToWrite);
} else {
n = write(fd, sp, nToWrite);
}
#endif
XmlRpcUtil::log(5, "XmlRpcSocket::nbWrite: send/write returned %d.", n);
if (n > 0) {
sp += n;
*bytesSoFar += n;
nToWrite -= n;
} else if (nonFatalError()) {
wouldBlock = true;
} else {
return false; // Error
}
}
return true;
}
// Get the port of a bound socket
int
XmlRpcSocket::getPort(int socket)
{
struct sockaddr_in saddr;
socklen_t saddr_len = sizeof(saddr);
int port;
int result = ::getsockname(socket, (sockaddr*) &saddr, &saddr_len);
if (result != 0) {
port = -1;
} else {
port = ntohs(saddr.sin_port);
}
return port;
}
// Returns last errno
int
XmlRpcSocket::getError()
{
#if defined(_WINDOWS)
return WSAGetLastError();
#else
return errno;
#endif
}
// Returns message corresponding to last errno
std::string
XmlRpcSocket::getErrorMsg()
{
return getErrorMsg(getError());
}
// Returns message corresponding to errno... well, it should anyway
std::string
XmlRpcSocket::getErrorMsg(int error)
{
char err[60];
snprintf(err,sizeof(err),"error %d", error);
return std::string(err);
}
+76
View File
@@ -0,0 +1,76 @@
#ifndef _XMLRPCSOCKET_H_
#define _XMLRPCSOCKET_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
#include <openssl/ssl.h>
namespace XmlRpc {
//! A platform-independent socket API.
class XmlRpcSocket {
public:
// TODO - typedef the socket type, casting to int won't work for 64 bit windows
//! Creates a stream (TCP) socket. Returns -1 on failure.
static int socket();
//! Closes a socket.
static void close(int socket);
//! Sets a stream (TCP) socket to perform non-blocking IO. Returns false on failure.
static bool setNonBlocking(int socket);
//! Read text from the specified socket. Returns false on error.
static bool nbRead(int socket, std::string& s, bool *eof, SSL *ssl);
//! Write text to the specified socket. Returns false on error.
static bool nbWrite(int socket, std::string& s, int *bytesSoFar, SSL* ssl);
// The next four methods are appropriate for servers.
//! Allow the port the specified socket is bound to to be re-bound immediately so
//! server re-starts are not delayed. Returns false on failure.
static bool setReuseAddr(int socket);
//! Bind to a specified port
static bool bind(int socket, int port);
//! Set socket in listen mode
static bool listen(int socket, int backlog);
//! Accept a client connection request
static int accept(int socket);
//! Connect a socket to a server (from a client)
static bool connect(int socket, std::string& host, int port);
//! Get the port of a bound socket
static int getPort(int socket);
//! Returns true if the last error was not a fatal one (eg, EWOULDBLOCK)
static bool nonFatalError();
//! Returns last errno
static int getError();
//! Returns message corresponding to last error
static std::string getErrorMsg();
//! Returns message corresponding to error
static std::string getErrorMsg(int error);
};
} // namespace XmlRpc
#endif
+40
View File
@@ -0,0 +1,40 @@
#include "XmlRpcSource.h"
#include "XmlRpcSocket.h"
#include "XmlRpcUtil.h"
namespace XmlRpc {
XmlRpcSource::XmlRpcSource(int fd /*= -1*/, bool deleteOnClose /*= false*/)
: _fd(fd), _deleteOnClose(deleteOnClose), _keepOpen(false)
{
}
XmlRpcSource::~XmlRpcSource()
{
}
void
XmlRpcSource::close()
{
if (_fd != -1) {
XmlRpcUtil::log(2,"XmlRpcSource::close: closing socket %d.", _fd);
XmlRpcSocket::close(_fd);
XmlRpcUtil::log(2,"XmlRpcSource::close: done closing socket %d.", _fd);
_fd = -1;
}
if (_deleteOnClose) {
XmlRpcUtil::log(2,"XmlRpcSource::close: deleting this");
_deleteOnClose = false;
delete this;
}
if (_ssl_ssl != (SSL *) NULL) {
SSL_shutdown (_ssl_ssl);
SSL_free (_ssl_ssl);
SSL_CTX_free (_ssl_ctx);
}
}
} // namespace XmlRpc
+67
View File
@@ -0,0 +1,67 @@
#ifndef _XMLRPCSOURCE_H_
#define _XMLRPCSOURCE_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
// Deal with SSL dependencies
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
namespace XmlRpc {
//! An RPC source represents a file descriptor to monitor
class XmlRpcSource {
public:
//! Constructor
//! @param fd The socket file descriptor to monitor.
//! @param deleteOnClose If true, the object deletes itself when close is called.
XmlRpcSource(int fd = -1, bool deleteOnClose = false);
//! Destructor
virtual ~XmlRpcSource();
//! Return the file descriptor being monitored.
int getfd() const { return _fd; }
//! Specify the file descriptor to monitor.
void setfd(int fd) { _fd = fd; }
//! Return whether the file descriptor should be kept open if it is no longer monitored.
bool getKeepOpen() const { return _keepOpen; }
//! Specify whether the file descriptor should be kept open if it is no longer monitored.
void setKeepOpen(bool b=true) { _keepOpen = b; }
//! Close the owned fd. If deleteOnClose was specified at construction, the object is deleted.
virtual void close();
//! Return true to continue monitoring this source
virtual unsigned handleEvent(unsigned eventType) = 0;
// Keep track of SSL status and other such things
bool _ssl;
SSL_CTX* _ssl_ctx;
SSL* _ssl_ssl;
SSL_METHOD* _ssl_meth;
private:
// Socket. This should really be a SOCKET (an alias for unsigned int*) on windows...
int _fd;
// In the server, a new source (XmlRpcServerConnection) is created
// for each connected client. When each connection is closed, the
// corresponding source object is deleted.
bool _deleteOnClose;
// In the client, keep connections open if you intend to make multiple calls.
bool _keepOpen;
};
} // namespace XmlRpc
#endif //_XMLRPCSOURCE_H_
+74
View File
@@ -0,0 +1,74 @@
#if defined(XMLRPC_THREADS)
#include "XmlRpcThread.h"
#if defined(_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <process.h>
#else
# include <pthread.h>
#endif
using namespace XmlRpc;
//! Destructor. Does not perform a join() (ie, the thread may continue to run).
XmlRpcThread::~XmlRpcThread()
{
if (_pThread)
{
#if defined(_WINDOWS)
::CloseHandle((HANDLE)_pThread);
#else
::pthread_detach((pthread_t)_pThread);
#endif
_pThread = 0;
}
}
//! Execute the run method of the runnable object in a separate thread.
//! Returns immediately in the calling thread.
void
XmlRpcThread::start()
{
if ( ! _pThread)
{
#if defined(_WINDOWS)
unsigned threadID;
_pThread = (HANDLE)_beginthreadex(NULL, 0, &runInThread, this, 0, &threadID);
#else
::pthread_create((pthread_t*) &_pThread, NULL, &runInThread, this);
#endif
}
}
//! Waits until the thread exits.
void
XmlRpcThread::join()
{
if (_pThread)
{
#if defined(_WINDOWS)
::WaitForSingleObject(_pThread, INFINITE);
::CloseHandle(_pThread);
#else
::pthread_join((pthread_t)_pThread, 0);
#endif
_pThread = 0;
}
}
//! Start the runnable going in a thread
unsigned int
XmlRpcThread::runInThread(void* pThread)
{
XmlRpcThread* t = (XmlRpcThread*)pThread;
t->getRunnable()->run();
return 0;
}
#endif // XMLRPC_THREADS
+60
View File
@@ -0,0 +1,60 @@
#ifndef _XMLRPCTHREAD_H_
#define _XMLRPCTHREAD_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
namespace XmlRpc {
//! An abstract class providing an interface for objects that can run in a separate thread.
class XmlRpcRunnable {
public:
//! Code to be executed.
virtual void run() = 0;
}; // class XmlRpcRunnable
//! A simple platform-independent thread API implemented for posix and windows.
class XmlRpcThread {
public:
//! Construct a thread object. Not usable until setRunnable() has been called.
XmlRpcThread() : _runner(0), _pThread(0) {}
//! Construct a thread object.
XmlRpcThread(XmlRpcRunnable* runnable) : _runner(runnable), _pThread(0) {}
//! Destructor. Does not perform a join() (ie, the thread may continue to run).
~XmlRpcThread();
//! Execute the run method of the runnable object in a separate thread.
//! Returns immediately in the calling thread.
void start();
//! Waits until the thread exits.
void join();
//! Access the runnable
XmlRpcRunnable* getRunnable() const { return _runner; }
//! Set the runnable
void setRunnable(XmlRpcRunnable* r) { _runner = r; }
private:
//! Start the runnable going in a thread
static unsigned int __stdcall runInThread(void* pThread);
//! Code to be executed
XmlRpcRunnable* _runner;
//! Native thread object
void* _pThread;
}; // class XmlRpcThread
} // namespace XmlRpc
#endif // _XMLRPCTHREAD_H_
+27
View File
@@ -0,0 +1,27 @@
#if defined(XMLRPC_THREADS)
#include "XmlRpcThreadedServer.h"
//#include "XmlRpcServerConnection.h"
using namespace XmlRpc;
// executeRequestThreaded:
// remove the serverConnection from the dispatcher (but don't close the socket)
// push the request onto the request queue
// (acquire the mutex, push_back request, release mutex, incr semaphore)
//
// worker::run
// while ! stopped
// pop a request off the request queue (block on semaphore/decr, acquire mutex, get request, rel)
// executeRequest (parse, run, generate response)
// notify the serverConnection that the response is available
// (the serverConnection needs to add itself back to the dispatcher safely - mutex)
// How do I interrupt the dispatcher if it is waiting in a select call?
// i) Replace select with WaitForMultipleObjects, using WSAEventSelect to associate
// each socket with an event object, and adding an additional "signal" event.
//
#endif // XMLRPC_THREADS
+67
View File
@@ -0,0 +1,67 @@
#ifndef _XMLRPCTHREADEDSERVER_H_
#define _XMLRPCTHREADEDSERVER_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <map>
# include <vector>
#endif
#include "XmlRpcMutex.h"
#include "XmlRpcServer.h"
#include "XmlRpcThread.h"
namespace XmlRpc {
//! A class to handle multiple simultaneous XML RPC requests
class XmlRpcThreadedServer : public XmlRpcServer {
public:
//! Create a server object with a specified number of worker threads.
XmlRpcThreadedServer(int nWorkers = 6) : _workers(nWorkers) {}
//! Execute a request
protected:
//! Each client request is assigned to one worker to handle.
//! Workers are executed on separate threads, and one worker may be
//! responsible for dispatching events to multiple client connections.
class Worker : XmlRpcRunnable {
public:
//! Constructor. Executes the run method in a separate thread.
Worker() { _thread.setRunnable(this); _thread.start(); }
//! Implement the Runnable interface
void run();
protected:
//! The thread this worker is running in.
XmlRpcThread _thread;
};
//! The worker pool
std::vector<Worker> _workers;
//! Serialize dispatcher access
XmlRpcMutex _mutex;
}; // class XmlRpcThreadedServer
}
#endif // _XMLRPCTHREADEDSERVER_H_
+259
View File
@@ -0,0 +1,259 @@
#include "XmlRpcUtil.h"
#ifndef MAKEDEPEND
# include <ctype.h>
# include <iostream>
# include <stdarg.h>
# include <stdio.h>
# include <string.h>
#endif
#include "XmlRpc.h"
using namespace XmlRpc;
//#define USE_WINDOWS_DEBUG // To make the error and log messages go to VC++ debug output
#ifdef USE_WINDOWS_DEBUG
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
// Version id
const char XmlRpc::XMLRPC_VERSION[] = "XMLRPC++ 0.8";
// Default log verbosity: 0 for no messages through 5 (writes everything)
int XmlRpcLogHandler::_verbosity = 0;
// Default log handler
static class DefaultLogHandler : public XmlRpcLogHandler {
public:
void log(int level, const char* msg) {
#ifdef USE_WINDOWS_DEBUG
if (level <= _verbosity) { OutputDebugString(msg); OutputDebugString("\n"); }
#else
if (level <= _verbosity) std::cout << msg << std::endl;
#endif
}
} defaultLogHandler;
// Message log singleton
XmlRpcLogHandler* XmlRpcLogHandler::_logHandler = &defaultLogHandler;
// Default error handler
static class DefaultErrorHandler : public XmlRpcErrorHandler {
public:
void error(const char* msg) {
#ifdef USE_WINDOWS_DEBUG
OutputDebugString(msg); OutputDebugString("\n");
#else
std::cerr << msg << std::endl;
#endif
}
} defaultErrorHandler;
// Error handler singleton
XmlRpcErrorHandler* XmlRpcErrorHandler::_errorHandler = &defaultErrorHandler;
// Easy API for log verbosity
int XmlRpc::getVerbosity() { return XmlRpcLogHandler::getVerbosity(); }
void XmlRpc::setVerbosity(int level) { XmlRpcLogHandler::setVerbosity(level); }
void XmlRpcUtil::log(int level, const char* fmt, ...)
{
if (level <= XmlRpcLogHandler::getVerbosity())
{
va_list va;
char buf[1024];
va_start( va, fmt);
vsnprintf(buf,sizeof(buf)-1,fmt,va);
buf[sizeof(buf)-1] = 0;
XmlRpcLogHandler::getLogHandler()->log(level, buf);
}
}
void XmlRpcUtil::error(const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
char buf[1024];
vsnprintf(buf,sizeof(buf)-1,fmt,va);
buf[sizeof(buf)-1] = 0;
XmlRpcErrorHandler::getErrorHandler()->error(buf);
}
// Returns contents between <tag> and </tag>, updates offset to char after </tag>
std::string
XmlRpcUtil::parseTag(const char* tag, std::string const& xml, int* offset)
{
if (*offset >= int(xml.length())) return std::string();
size_t istart = xml.find(tag, *offset);
if (istart == std::string::npos) return std::string();
istart += strlen(tag);
std::string etag = "</";
etag += tag + 1;
size_t iend = xml.find(etag, istart);
if (iend == std::string::npos) return std::string();
*offset = int(iend + etag.length());
return xml.substr(istart, iend-istart);
}
// Returns true if the tag is found and updates offset to the char after the tag
bool
XmlRpcUtil::findTag(const char* tag, std::string const& xml, int* offset)
{
if (*offset >= int(xml.length())) return false;
size_t istart = xml.find(tag, *offset);
if (istart == std::string::npos)
return false;
*offset = int(istart + strlen(tag));
return true;
}
// Returns true if the tag is found at the specified offset (modulo any whitespace)
// and updates offset to the char after the tag
bool
XmlRpcUtil::nextTagIs(const char* tag, std::string const& xml, int* offset)
{
if (*offset >= int(xml.length())) return false;
const char* cp = xml.c_str() + *offset;
int nc = 0;
while (*cp && isspace(*cp)) {
++cp;
++nc;
}
int len = int(strlen(tag));
if (*cp && (strncmp(cp, tag, len) == 0)) {
*offset += nc + len;
return true;
}
return false;
}
// Returns the next tag and updates offset to the char after the tag, or empty string
// if the next non-whitespace character is not '<'. Ignores parameters and values within
// the tag entity.
std::string
XmlRpcUtil::getNextTag(std::string const& xml, int* offset)
{
if (*offset >= int(xml.length())) return std::string();
const char* cp = xml.c_str() + size_t(*offset);
const char* startcp = cp;
while (*cp && isspace(*cp))
++cp;
if (*cp != '<') return std::string();
// Tag includes the non-blank characters after <
const char* start = cp++;
while (*cp != '>' && *cp != 0 && ! isspace(*cp))
++cp;
std::string s(start, cp-start+1);
if (*cp != '>') // Skip parameters and values
{
while (*cp != '>' && *cp != 0)
++cp;
s[s.length()-1] = *cp;
}
*offset += int(cp - startcp + 1);
return s;
}
// xml encodings (xml-encoded entities are preceded with '&')
static const char AMP = '&';
static const char rawEntity[] = { '<', '>', '&', '\'', '\"', 0 };
static const char* xmlEntity[] = { "lt;", "gt;", "amp;", "apos;", "quot;", 0 };
static const int xmlEntLen[] = { 3, 3, 4, 5, 5 };
// Replace xml-encoded entities with the raw text equivalents.
std::string
XmlRpcUtil::xmlDecode(const std::string& encoded)
{
std::string::size_type iAmp = encoded.find(AMP);
if (iAmp == std::string::npos)
return encoded;
std::string decoded(encoded, 0, iAmp);
std::string::size_type iSize = encoded.size();
decoded.reserve(iSize);
const char* ens = encoded.c_str();
while (iAmp != iSize) {
if (encoded[iAmp] == AMP && iAmp+1 < iSize) {
int iEntity;
for (iEntity=0; xmlEntity[iEntity] != 0; ++iEntity)
//if (encoded.compare(iAmp+1, xmlEntLen[iEntity], xmlEntity[iEntity]) == 0)
if (strncmp(ens+iAmp+1, xmlEntity[iEntity], xmlEntLen[iEntity]) == 0)
{
decoded += rawEntity[iEntity];
iAmp += xmlEntLen[iEntity]+1;
break;
}
if (xmlEntity[iEntity] == 0) // unrecognized sequence
decoded += encoded[iAmp++];
} else {
decoded += encoded[iAmp++];
}
}
return decoded;
}
// Replace raw text with xml-encoded entities.
std::string
XmlRpcUtil::xmlEncode(const std::string& raw)
{
std::string::size_type iRep = raw.find_first_of(rawEntity);
if (iRep == std::string::npos)
return raw;
std::string encoded(raw, 0, iRep);
std::string::size_type iSize = raw.size();
while (iRep != iSize) {
int iEntity;
for (iEntity=0; rawEntity[iEntity] != 0; ++iEntity)
if (raw[iRep] == rawEntity[iEntity])
{
encoded += AMP;
encoded += xmlEntity[iEntity];
break;
}
if (rawEntity[iEntity] == 0)
encoded += raw[iRep];
++iRep;
}
return encoded;
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef _XMLRPCUTIL_H_
#define _XMLRPCUTIL_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <string>
#endif
#if defined(_MSC_VER)
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
#elif defined(__BORLANDC__)
# define strcasecmp stricmp
# define strncasecmp strnicmp
#endif
namespace XmlRpc {
//! Utilities for XML parsing, encoding, and decoding and message handlers.
class XmlRpcUtil {
public:
// hokey xml parsing
//! Returns contents between <tag> and </tag>, updates offset to char after </tag>
static std::string parseTag(const char* tag, std::string const& xml, int* offset);
//! Returns true if the tag is found and updates offset to the char after the tag
static bool findTag(const char* tag, std::string const& xml, int* offset);
//! Returns the next tag and updates offset to the char after the tag, or empty string
//! if the next non-whitespace character is not '<'
static std::string getNextTag(std::string const& xml, int* offset);
//! Returns true if the tag is found at the specified offset (modulo any whitespace)
//! and updates offset to the char after the tag
static bool nextTagIs(const char* tag, std::string const& xml, int* offset);
//! Convert raw text to encoded xml.
static std::string xmlEncode(const std::string& raw);
//! Convert encoded xml to raw text
static std::string xmlDecode(const std::string& encoded);
//! Dump messages somewhere
static void log(int level, const char* fmt, ...);
//! Dump error messages somewhere
static void error(const char* fmt, ...);
};
} // namespace XmlRpc
#endif // _XMLRPCUTIL_H_
+611
View File
@@ -0,0 +1,611 @@
#include "XmlRpcValue.h"
#include "XmlRpcException.h"
#include "XmlRpcUtil.h"
#include "base64.h"
#ifndef MAKEDEPEND
# include <iostream>
# include <ostream>
# include <stdlib.h>
# include <stdio.h>
#endif
namespace XmlRpc {
static const char VALUE_TAG[] = "<value>";
static const char VALUE_ETAG[] = "</value>";
static const char BOOLEAN_TAG[] = "<boolean>";
static const char BOOLEAN_ETAG[] = "</boolean>";
static const char DOUBLE_TAG[] = "<double>";
static const char DOUBLE_ETAG[] = "</double>";
static const char INT_TAG[] = "<int>";
static const char I4_TAG[] = "<i4>";
static const char I4_ETAG[] = "</i4>";
static const char STRING_TAG[] = "<string>";
static const char DATETIME_TAG[] = "<dateTime.iso8601>";
static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
static const char BASE64_TAG[] = "<base64>";
static const char BASE64_ETAG[] = "</base64>";
static const char ARRAY_TAG[] = "<array>";
static const char DATA_TAG[] = "<data>";
static const char DATA_ETAG[] = "</data>";
static const char ARRAY_ETAG[] = "</array>";
static const char STRUCT_TAG[] = "<struct>";
static const char MEMBER_TAG[] = "<member>";
static const char NAME_TAG[] = "<name>";
static const char NAME_ETAG[] = "</name>";
static const char MEMBER_ETAG[] = "</member>";
static const char STRUCT_ETAG[] = "</struct>";
// Format strings
std::string XmlRpcValue::_doubleFormat("%f");
// Clean up
void XmlRpcValue::invalidate()
{
switch (_type) {
case TypeString: delete _value.asString; break;
case TypeDateTime: delete _value.asTime; break;
case TypeBase64: delete _value.asBinary; break;
case TypeArray: delete _value.asArray; break;
case TypeStruct: delete _value.asStruct; break;
default: break;
}
_type = TypeInvalid;
_value.asBinary = 0;
}
// Type checking
void XmlRpcValue::assertTypeOrInvalid(Type t)
{
if (_type == TypeInvalid)
{
_type = t;
switch (_type) { // Ensure there is a valid value for the type
case TypeString: _value.asString = new std::string(); break;
case TypeDateTime: _value.asTime = new struct tm(); break;
case TypeBase64: _value.asBinary = new BinaryData(); break;
case TypeArray: _value.asArray = new ValueArray(); break;
case TypeStruct: _value.asStruct = new ValueStruct(); break;
default: _value.asBinary = 0; break;
}
}
else if (_type != t)
throw XmlRpcException("type error");
}
void XmlRpcValue::assertArray(int size) const
{
if (_type != TypeArray)
throw XmlRpcException("type error: expected an array");
else if (int(_value.asArray->size()) < size)
throw XmlRpcException("range error: array index too large");
}
void XmlRpcValue::assertArray(int size)
{
if (_type == TypeInvalid) {
_type = TypeArray;
_value.asArray = new ValueArray(size);
} else if (_type == TypeArray) {
if (int(_value.asArray->size()) < size)
_value.asArray->resize(size);
} else
throw XmlRpcException("type error: expected an array");
}
void XmlRpcValue::assertStruct()
{
if (_type == TypeInvalid) {
_type = TypeStruct;
_value.asStruct = new ValueStruct();
} else if (_type != TypeStruct)
throw XmlRpcException("type error: expected a struct");
}
// Operators
XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
{
if (this != &rhs)
{
invalidate();
_type = rhs._type;
switch (_type) {
case TypeBoolean: _value.asBool = rhs._value.asBool; break;
case TypeInt: _value.asInt = rhs._value.asInt; break;
case TypeDouble: _value.asDouble = rhs._value.asDouble; break;
case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break;
case TypeString: _value.asString = new std::string(*rhs._value.asString); break;
case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break;
case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break;
case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break;
default: _value.asBinary = 0; break;
}
}
return *this;
}
// Predicate for tm equality
static bool tmEq(struct tm const& t1, struct tm const& t2) {
return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday &&
t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
}
bool XmlRpcValue::operator==(XmlRpcValue const& other) const
{
if (_type != other._type)
return false;
switch (_type) {
case TypeBoolean: return ( !_value.asBool && !other._value.asBool) ||
( _value.asBool && other._value.asBool);
case TypeInt: return _value.asInt == other._value.asInt;
case TypeDouble: return _value.asDouble == other._value.asDouble;
case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime);
case TypeString: return *_value.asString == *other._value.asString;
case TypeBase64: return *_value.asBinary == *other._value.asBinary;
case TypeArray: return *_value.asArray == *other._value.asArray;
// The map<>::operator== requires the definition of value< for kcc
case TypeStruct: //return *_value.asStruct == *other._value.asStruct;
{
if (_value.asStruct->size() != other._value.asStruct->size())
return false;
ValueStruct::const_iterator it1=_value.asStruct->begin();
ValueStruct::const_iterator it2=other._value.asStruct->begin();
while (it1 != _value.asStruct->end()) {
const XmlRpcValue& v1 = it1->second;
const XmlRpcValue& v2 = it2->second;
if ( ! (v1 == v2))
return false;
it1++;
it2++;
}
return true;
}
default: break;
}
return true; // Both invalid values ...
}
bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
{
return !(*this == other);
}
// Works for strings, binary data, arrays, and structs.
int XmlRpcValue::size() const
{
switch (_type) {
case TypeString: return int(_value.asString->size());
case TypeBase64: return int(_value.asBinary->size());
case TypeArray: return int(_value.asArray->size());
case TypeStruct: return int(_value.asStruct->size());
default: break;
}
throw XmlRpcException("type error");
}
// Checks for existence of struct member
bool XmlRpcValue::hasMember(const std::string& name) const
{
return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end();
}
// Set the value from xml. The chars at *offset into valueXml
// should be the start of a <value> tag. Destroys any existing value.
bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset)
{
int savedOffset = *offset;
invalidate();
if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset))
return false; // Not a value, offset not updated
int afterValueOffset = *offset;
std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset);
bool result = false;
if (typeTag == BOOLEAN_TAG)
result = boolFromXml(valueXml, offset);
else if (typeTag == I4_TAG || typeTag == INT_TAG)
result = intFromXml(valueXml, offset);
else if (typeTag == DOUBLE_TAG)
result = doubleFromXml(valueXml, offset);
else if (typeTag.empty() || typeTag == STRING_TAG)
result = stringFromXml(valueXml, offset);
else if (typeTag == DATETIME_TAG)
result = timeFromXml(valueXml, offset);
else if (typeTag == BASE64_TAG)
result = binaryFromXml(valueXml, offset);
else if (typeTag == ARRAY_TAG)
result = arrayFromXml(valueXml, offset);
else if (typeTag == STRUCT_TAG)
result = structFromXml(valueXml, offset);
// Watch for empty/blank strings with no <string>tag
else if (typeTag == VALUE_ETAG)
{
*offset = afterValueOffset; // back up & try again
result = stringFromXml(valueXml, offset);
}
if (result) // Skip over the </value> tag
XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset);
else // Unrecognized tag after <value>
*offset = savedOffset;
return result;
}
// Encode the Value in xml
std::string XmlRpcValue::toXml() const
{
switch (_type) {
case TypeBoolean: return boolToXml();
case TypeInt: return intToXml();
case TypeDouble: return doubleToXml();
case TypeString: return stringToXml();
case TypeDateTime: return timeToXml();
case TypeBase64: return binaryToXml();
case TypeArray: return arrayToXml();
case TypeStruct: return structToXml();
default: break;
}
return std::string(); // Invalid value
}
// Boolean
bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset)
{
const char* valueStart = valueXml.c_str() + *offset;
char* valueEnd;
long ivalue = strtol(valueStart, &valueEnd, 10);
if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1))
return false;
_type = TypeBoolean;
_value.asBool = (ivalue == 1);
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::boolToXml() const
{
std::string xml = VALUE_TAG;
xml += BOOLEAN_TAG;
xml += (_value.asBool ? "1" : "0");
xml += BOOLEAN_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Int
bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset)
{
const char* valueStart = valueXml.c_str() + *offset;
char* valueEnd;
long ivalue = strtol(valueStart, &valueEnd, 10);
if (valueEnd == valueStart)
return false;
_type = TypeInt;
_value.asInt = int(ivalue);
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::intToXml() const
{
char buf[256];
snprintf(buf, sizeof(buf)-1, "%d", _value.asInt);
buf[sizeof(buf)-1] = 0;
std::string xml = VALUE_TAG;
xml += I4_TAG;
xml += buf;
xml += I4_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Double
bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset)
{
const char* valueStart = valueXml.c_str() + *offset;
char* valueEnd;
double dvalue = strtod(valueStart, &valueEnd);
if (valueEnd == valueStart)
return false;
_type = TypeDouble;
_value.asDouble = dvalue;
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::doubleToXml() const
{
char buf[256];
snprintf(buf, sizeof(buf)-1, getDoubleFormat().c_str(), _value.asDouble);
buf[sizeof(buf)-1] = 0;
std::string xml = VALUE_TAG;
xml += DOUBLE_TAG;
xml += buf;
xml += DOUBLE_ETAG;
xml += VALUE_ETAG;
return xml;
}
// String
bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset)
{
size_t valueEnd = valueXml.find('<', *offset);
if (valueEnd == std::string::npos)
return false; // No end tag;
_type = TypeString;
_value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset)));
*offset += int(_value.asString->length());
return true;
}
std::string XmlRpcValue::stringToXml() const
{
std::string xml = VALUE_TAG;
//xml += STRING_TAG; optional
xml += XmlRpcUtil::xmlEncode(*_value.asString);
//xml += STRING_ETAG;
xml += VALUE_ETAG;
return xml;
}
// DateTime (stored as a struct tm)
bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset)
{
size_t valueEnd = valueXml.find('<', *offset);
if (valueEnd == std::string::npos)
return false; // No end tag;
std::string stime = valueXml.substr(*offset, valueEnd-*offset);
struct tm t;
if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6)
return false;
t.tm_year -= 1900;
t.tm_isdst = -1;
_type = TypeDateTime;
_value.asTime = new struct tm(t);
*offset += int(stime.length());
return true;
}
std::string XmlRpcValue::timeToXml() const
{
struct tm* t = _value.asTime;
char buf[20];
snprintf(buf, sizeof(buf)-1, "%04d%02d%02dT%02d:%02d:%02d",
1900+t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
buf[sizeof(buf)-1] = 0;
std::string xml = VALUE_TAG;
xml += DATETIME_TAG;
xml += buf;
xml += DATETIME_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Base64
bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset)
{
size_t valueEnd = valueXml.find('<', *offset);
if (valueEnd == std::string::npos)
return false; // No end tag;
_type = TypeBase64;
std::string asString = valueXml.substr(*offset, valueEnd-*offset);
_value.asBinary = new BinaryData();
// check whether base64 encodings can contain chars xml encodes...
// convert from base64 to binary
int iostatus = 0;
base64<char> decoder;
std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary));
decoder.get(asString.begin(), asString.end(), ins, iostatus);
*offset += int(asString.length());
return true;
}
std::string XmlRpcValue::binaryToXml() const
{
// convert to base64
std::vector<char> base64data;
int iostatus = 0;
base64<char> encoder;
std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf());
// Wrap with xml
std::string xml = VALUE_TAG;
xml += BASE64_TAG;
xml.append(base64data.begin(), base64data.end());
xml += BASE64_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Array
bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset)
{
if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset))
return false;
_type = TypeArray;
_value.asArray = new ValueArray;
XmlRpcValue v;
while (v.fromXml(valueXml, offset))
_value.asArray->push_back(v); // copy...
// Skip the trailing </data>
(void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset);
return true;
}
// In general, its preferable to generate the xml of each element of the
// array as it is needed rather than glomming up one big string.
std::string XmlRpcValue::arrayToXml() const
{
std::string xml = VALUE_TAG;
xml += ARRAY_TAG;
xml += DATA_TAG;
int s = int(_value.asArray->size());
for (int i=0; i<s; ++i)
xml += _value.asArray->at(i).toXml();
xml += DATA_ETAG;
xml += ARRAY_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Struct
bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset)
{
_type = TypeStruct;
_value.asStruct = new ValueStruct;
while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) {
// name
const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset);
// value
XmlRpcValue val(valueXml, offset);
if ( ! val.valid()) {
invalidate();
return false;
}
const std::pair<const std::string, XmlRpcValue> p(name, val);
_value.asStruct->insert(p);
(void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset);
}
return true;
}
// In general, its preferable to generate the xml of each element
// as it is needed rather than glomming up one big string.
std::string XmlRpcValue::structToXml() const
{
std::string xml = VALUE_TAG;
xml += STRUCT_TAG;
ValueStruct::const_iterator it;
for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) {
xml += MEMBER_TAG;
xml += NAME_TAG;
xml += XmlRpcUtil::xmlEncode(it->first);
xml += NAME_ETAG;
xml += it->second.toXml();
xml += MEMBER_ETAG;
}
xml += STRUCT_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Write the value without xml encoding it
std::ostream& XmlRpcValue::write(std::ostream& os) const {
switch (_type) {
default: break;
case TypeBoolean: os << _value.asBool; break;
case TypeInt: os << _value.asInt; break;
case TypeDouble: os << _value.asDouble; break;
case TypeString: os << *_value.asString; break;
case TypeDateTime:
{
struct tm* t = _value.asTime;
char buf[20];
snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
buf[sizeof(buf)-1] = 0;
os << buf;
break;
}
case TypeBase64:
{
int iostatus = 0;
std::ostreambuf_iterator<char> out(os);
base64<char> encoder;
encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf());
break;
}
case TypeArray:
{
int s = int(_value.asArray->size());
os << '{';
for (int i=0; i<s; ++i)
{
if (i > 0) os << ',';
_value.asArray->at(i).write(os);
}
os << '}';
break;
}
case TypeStruct:
{
os << '[';
ValueStruct::const_iterator it;
for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it)
{
if (it!=_value.asStruct->begin()) os << ',';
os << it->first << ':';
it->second.write(os);
}
os << ']';
break;
}
}
return os;
}
} // namespace XmlRpc
// ostream
std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v)
{
// If you want to output in xml format:
//return os << v.toXml();
return v.write(os);
}
+259
View File
@@ -0,0 +1,259 @@
#ifndef _XMLRPCVALUE_H_
#define _XMLRPCVALUE_H_
//
// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
//
#if defined(_MSC_VER)
# pragma warning(disable:4786) // identifier was truncated in debug info
#endif
#ifndef MAKEDEPEND
# include <map>
# include <string>
# include <vector>
# include <time.h>
#endif
namespace XmlRpc {
//! A class to represent RPC arguments and results.
//! Each XmlRpcValue object contains a typed value,
//! where the type is determined by the initial value
//! assigned to the object.
// should probably refcount them...
class XmlRpcValue {
public:
//! XmlRpcValue types
enum Type {
TypeInvalid,
TypeBoolean,
TypeInt,
TypeDouble,
TypeString,
TypeDateTime,
TypeBase64,
TypeArray,
TypeStruct
};
// Non-primitive types
typedef std::vector<char> BinaryData;
typedef std::vector<XmlRpcValue> ValueArray;
typedef std::map<std::string, XmlRpcValue> ValueStruct;
// Constructors
//! Construct an empty XmlRpcValue
XmlRpcValue() : _type(TypeInvalid) { _value.asBinary = 0; }
//! Construct an XmlRpcValue with a bool value
XmlRpcValue(bool value) : _type(TypeBoolean) { _value.asBool = value; }
//! Construct an XmlRpcValue with an int value
XmlRpcValue(int value) : _type(TypeInt) { _value.asInt = value; }
//! Construct an XmlRpcValue with a double value
XmlRpcValue(double value) : _type(TypeDouble) { _value.asDouble = value; }
//! Construct an XmlRpcValue with a string value
XmlRpcValue(std::string const& value) : _type(TypeString)
{ _value.asString = new std::string(value); }
//! Construct an XmlRpcValue with a string value.
//! @param value A null-terminated (C) string.
XmlRpcValue(const char* value) : _type(TypeString)
{ _value.asString = new std::string(value); }
//! Construct an XmlRpcValue with a date/time value.
//! @param value A pointer to a struct tm (see localtime)
XmlRpcValue(struct tm* value) : _type(TypeDateTime)
{ _value.asTime = new struct tm(*value); }
//! Construct an XmlRpcValue with a binary data value
//! @param value A pointer to data
//! @param nBytes The length of the data pointed to, in bytes
XmlRpcValue(void* value, int nBytes) : _type(TypeBase64)
{
_value.asBinary = new BinaryData((char*)value, ((char*)value)+nBytes);
}
//! Construct from xml, beginning at *offset chars into the string, updates offset
XmlRpcValue(std::string const& xml, int* offset) : _type(TypeInvalid)
{ if ( ! fromXml(xml,offset)) _type = TypeInvalid; }
//! Copy constructor
XmlRpcValue(XmlRpcValue const& rhs) : _type(TypeInvalid) { *this = rhs; }
//! Destructor (make virtual if you want to subclass)
/*virtual*/ ~XmlRpcValue() { invalidate(); }
//! Erase the current value
void clear() { invalidate(); }
// Operators
//! Assignment from one XmlRpcValue to this one.
//! @param rhs The value in rhs is copied to this value.
XmlRpcValue& operator=(XmlRpcValue const& rhs);
//! Assign an int to this XmlRpcValue.
XmlRpcValue& operator=(int const& rhs) { return operator=(XmlRpcValue(rhs)); }
//! Assign a double to this XmlRpcValue.
XmlRpcValue& operator=(double const& rhs) { return operator=(XmlRpcValue(rhs)); }
//! Assign a string to this XmlRpcValue.
XmlRpcValue& operator=(const char* rhs) { return operator=(XmlRpcValue(std::string(rhs))); }
//! Tests two XmlRpcValues for equality
bool operator==(XmlRpcValue const& other) const;
//! Tests two XmlRpcValues for inequality
bool operator!=(XmlRpcValue const& other) const;
//! Treat an XmlRpcValue as a bool.
//! Throws XmlRpcException if the value is initialized to
//! a type that is not TypeBoolean.
operator bool&() { assertTypeOrInvalid(TypeBoolean); return _value.asBool; }
//! Treat an XmlRpcValue as an int.
//! Throws XmlRpcException if the value is initialized to
//! a type that is not TypeInt.
operator int&() { assertTypeOrInvalid(TypeInt); return _value.asInt; }
//! Treat an XmlRpcValue as a double.
//! Throws XmlRpcException if the value is initialized to
//! a type that is not TypeDouble.
operator double&() { assertTypeOrInvalid(TypeDouble); return _value.asDouble; }
//! Treat an XmlRpcValue as a string.
//! Throws XmlRpcException if the value is initialized to
//! a type that is not TypeString.
operator std::string&() { assertTypeOrInvalid(TypeString); return *_value.asString; }
//! Access the BinaryData value.
//! Throws XmlRpcException if the value is initialized to
//! a type that is not TypeBase64.
operator BinaryData&() { assertTypeOrInvalid(TypeBase64); return *_value.asBinary; }
//! Access the DateTime value.
//! Throws XmlRpcException if the value is initialized to
//! a type that is not TypeDateTime.
operator struct tm&() { assertTypeOrInvalid(TypeDateTime); return *_value.asTime; }
//! Const array value accessor.
//! Access the ith value of the array.
//! Throws XmlRpcException if the value is not an array or if the index i is
//! not a valid index for the array.
XmlRpcValue const& operator[](int i) const { assertArray(i+1); return _value.asArray->at(i); }
//! Array value accessor.
//! Access the ith value of the array, growing the array if necessary.
//! Throws XmlRpcException if the value is not an array.
XmlRpcValue& operator[](int i) { assertArray(i+1); return _value.asArray->at(i); }
//! Struct entry accessor.
//! Returns the value associated with the given entry, creating one if necessary.
XmlRpcValue& operator[](std::string const& k) { assertStruct(); return (*_value.asStruct)[k]; }
//! Struct entry accessor.
//! Returns the value associated with the given entry, creating one if necessary.
XmlRpcValue& operator[](const char* k) { assertStruct(); std::string s(k); return (*_value.asStruct)[s]; }
//! Access the struct value map.
//! Can be used to iterate over the entries in the map to find all defined entries.
operator ValueStruct const&() { assertStruct(); return *_value.asStruct; }
// Accessors
//! Return true if the value has been set to something.
bool valid() const { return _type != TypeInvalid; }
//! Return the type of the value stored. \see Type.
Type const &getType() const { return _type; }
//! Return the size for string, base64, array, and struct values.
int size() const;
//! Specify the size for array values. Array values will grow beyond this size if needed.
void setSize(int size) { assertArray(size); }
//! Check for the existence of a struct member by name.
bool hasMember(const std::string& name) const;
//! Decode xml. Destroys any existing value.
bool fromXml(std::string const& valueXml, int* offset);
//! Encode the Value in xml
std::string toXml() const;
//! Write the value (no xml encoding)
std::ostream& write(std::ostream& os) const;
// Formatting
//! Return the format used to write double values.
static std::string const& getDoubleFormat() { return _doubleFormat; }
//! Specify the format used to write double values.
static void setDoubleFormat(const char* f) { _doubleFormat = f; }
protected:
// Clean up
void invalidate();
// Type checking
void assertTypeOrInvalid(Type t);
void assertArray(int size) const;
void assertArray(int size);
void assertStruct();
// XML decoding
bool boolFromXml(std::string const& valueXml, int* offset);
bool intFromXml(std::string const& valueXml, int* offset);
bool doubleFromXml(std::string const& valueXml, int* offset);
bool stringFromXml(std::string const& valueXml, int* offset);
bool timeFromXml(std::string const& valueXml, int* offset);
bool binaryFromXml(std::string const& valueXml, int* offset);
bool arrayFromXml(std::string const& valueXml, int* offset);
bool structFromXml(std::string const& valueXml, int* offset);
// XML encoding
std::string boolToXml() const;
std::string intToXml() const;
std::string doubleToXml() const;
std::string stringToXml() const;
std::string timeToXml() const;
std::string binaryToXml() const;
std::string arrayToXml() const;
std::string structToXml() const;
// Format strings
static std::string _doubleFormat;
// Type tag and values
Type _type;
// At some point I will split off Arrays and Structs into
// separate ref-counted objects for more efficient copying.
union {
bool asBool;
int asInt;
double asDouble;
struct tm* asTime;
std::string* asString;
BinaryData* asBinary;
ValueArray* asArray;
ValueStruct* asStruct;
} _value;
};
} // namespace XmlRpc
std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v);
#endif // _XMLRPCVALUE_H_
+379
View File
@@ -0,0 +1,379 @@
// base64.hpp
// Autor Konstantin Pilipchuk
// mailto:lostd@ukr.net
//
//
#if !defined(__BASE64_H_INCLUDED__)
#define __BASE64_H_INCLUDED__ 1
#ifndef MAKEDEPEND
# include <iterator>
#endif
static
int _base64Chars[]= {'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',
'+','/' };
#define _0000_0011 0x03
#define _1111_1100 0xFC
#define _1111_0000 0xF0
#define _0011_0000 0x30
#define _0011_1100 0x3C
#define _0000_1111 0x0F
#define _1100_0000 0xC0
#define _0011_1111 0x3F
#define _EQUAL_CHAR (-1)
#define _UNKNOWN_CHAR (-2)
#define _IOS_FAILBIT std::ios_base::failbit
#define _IOS_EOFBIT std::ios_base::eofbit
#define _IOS_BADBIT std::ios_base::badbit
#define _IOS_GOODBIT std::ios_base::goodbit
// TEMPLATE CLASS base64_put
template<class _E = char, class _Tr = std::char_traits<_E> >
class base64
{
public:
typedef unsigned char byte_t;
typedef _E char_type;
typedef _Tr traits_type;
// base64 requires max line length <= 72 characters
// you can fill end of line
// it may be crlf, crlfsp, noline or other class like it
struct crlf
{
template<class _OI>
_OI operator()(_OI _To) const{
*_To = _Tr::to_char_type('\r'); ++_To;
*_To = _Tr::to_char_type('\n'); ++_To;
return (_To);
}
};
struct crlfsp
{
template<class _OI>
_OI operator()(_OI _To) const{
*_To = _Tr::to_char_type('\r'); ++_To;
*_To = _Tr::to_char_type('\n'); ++_To;
*_To = _Tr::to_char_type(' '); ++_To;
return (_To);
}
};
struct noline
{
template<class _OI>
_OI operator()(_OI _To) const{
return (_To);
}
};
struct three2four
{
void zero()
{
_data[0] = 0;
_data[1] = 0;
_data[2] = 0;
}
byte_t get_0() const
{
return _data[0];
}
byte_t get_1() const
{
return _data[1];
}
byte_t get_2() const
{
return _data[2];
}
void set_0(byte_t _ch)
{
_data[0] = _ch;
}
void set_1(byte_t _ch)
{
_data[1] = _ch;
}
void set_2(byte_t _ch)
{
_data[2] = _ch;
}
// 0000 0000 1111 1111 2222 2222
// xxxx xxxx xxxx xxxx xxxx xxxx
// 0000 0011 1111 2222 2233 3333
int b64_0() const {return (_data[0] & _1111_1100) >> 2;}
int b64_1() const {return ((_data[0] & _0000_0011) << 4) + ((_data[1] & _1111_0000)>>4);}
int b64_2() const {return ((_data[1] & _0000_1111) << 2) + ((_data[2] & _1100_0000)>>6);}
int b64_3() const {return (_data[2] & _0011_1111);}
void b64_0(int _ch) {_data[0] = ((_ch & _0011_1111) << 2) | (_0000_0011 & _data[0]);}
void b64_1(int _ch) {
_data[0] = ((_ch & _0011_0000) >> 4) | (_1111_1100 & _data[0]);
_data[1] = ((_ch & _0000_1111) << 4) | (_0000_1111 & _data[1]); }
void b64_2(int _ch) {
_data[1] = ((_ch & _0011_1100) >> 2) | (_1111_0000 & _data[1]);
_data[2] = ((_ch & _0000_0011) << 6) | (_0011_1111 & _data[2]); }
void b64_3(int _ch){
_data[2] = (_ch & _0011_1111) | (_1100_0000 & _data[2]);}
private:
byte_t _data[3];
};
template<class _II, class _OI, class _State, class _Endline>
_II put(_II _First, _II _Last, _OI _To, _State& _St, _Endline _Endl) const
{
three2four _3to4;
int line_octets = 0;
while(_First != _Last)
{
_3to4.zero();
// áåð¸ì ïî 3 ñèìâîëà
_3to4.set_0(*_First);
_First++;
if(_First == _Last)
{
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
*_To = _Tr::to_char_type('='); ++_To;
*_To = _Tr::to_char_type('='); ++_To;
goto __end;
}
_3to4.set_1(*_First);
_First++;
if(_First == _Last)
{
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To;
*_To = _Tr::to_char_type('='); ++_To;
goto __end;
}
_3to4.set_2(*_First);
_First++;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To;
*_To = _Tr::to_char_type(_base64Chars[_3to4.b64_3()]); ++_To;
if(line_octets == 17) // base64 ïîçâîëÿåò äëèíó ñòðîêè íå áîëåå 72 ñèìâîëîâ
{
//_To = _Endl(_To);
*_To = '\n'; ++_To;
line_octets = 0;
}
else
++line_octets;
}
__end: ;
return (_First);
}
template<class _II, class _OI, class _State>
_II get(_II _First, _II _Last, _OI _To, _State& _St) const
{
three2four _3to4;
int _Char;
while(_First != _Last)
{
// Take octet
_3to4.zero();
// -- 0 --
// Search next valid char...
while((_Char = _getCharType(*_First)) < 0 && _Char == _UNKNOWN_CHAR)
{
if(++_First == _Last)
{
_St |= _IOS_FAILBIT|_IOS_EOFBIT; return _First; // unexpected EOF
}
}
if(_Char == _EQUAL_CHAR){
// Error! First character in octet can't be '='
_St |= _IOS_FAILBIT;
return _First;
}
else
_3to4.b64_0(_Char);
// -- 1 --
// Search next valid char...
while(++_First != _Last)
if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
break;
if(_First == _Last) {
_St |= _IOS_FAILBIT|_IOS_EOFBIT; // unexpected EOF
return _First;
}
if(_Char == _EQUAL_CHAR){
// Error! Second character in octet can't be '='
_St |= _IOS_FAILBIT;
return _First;
}
else
_3to4.b64_1(_Char);
// -- 2 --
// Search next valid char...
while(++_First != _Last)
if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
break;
if(_First == _Last) {
// Error! Unexpected EOF. Must be '=' or base64 character
_St |= _IOS_FAILBIT|_IOS_EOFBIT;
return _First;
}
if(_Char == _EQUAL_CHAR){
// OK!
_3to4.b64_2(0);
_3to4.b64_3(0);
// chek for EOF
if(++_First == _Last)
{
// Error! Unexpected EOF. Must be '='. Ignore it.
//_St |= _IOS_BADBIT|_IOS_EOFBIT;
_St |= _IOS_EOFBIT;
}
else
if(_getCharType(*_First) != _EQUAL_CHAR)
{
// Error! Must be '='. Ignore it.
//_St |= _IOS_BADBIT;
}
else
++_First; // Skip '='
// write 1 byte to output
*_To = (byte_t) _3to4.get_0();
return _First;
}
else
_3to4.b64_2(_Char);
// -- 3 --
// Search next valid char...
while(++_First != _Last)
if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
break;
if(_First == _Last) {
// Unexpected EOF. It's error. But ignore it.
//_St |= _IOS_FAILBIT|_IOS_EOFBIT;
_St |= _IOS_EOFBIT;
return _First;
}
if(_Char == _EQUAL_CHAR)
{
// OK!
_3to4.b64_3(0);
// write to output 2 bytes
*_To = (byte_t) _3to4.get_0();
*_To = (byte_t) _3to4.get_1();
++_First; // set position to next character
return _First;
}
else
_3to4.b64_3(_Char);
// write to output 3 bytes
*_To = (byte_t) _3to4.get_0();
*_To = (byte_t) _3to4.get_1();
*_To = (byte_t) _3to4.get_2();
++_First;
} // while(_First != _Last)
return (_First);
}
protected:
int _getCharType(int _Ch) const
{
if(_base64Chars[62] == _Ch)
return 62;
if(_base64Chars[63] == _Ch)
return 63;
if((_base64Chars[0] <= _Ch) && (_base64Chars[25] >= _Ch))
return _Ch - _base64Chars[0];
if((_base64Chars[26] <= _Ch) && (_base64Chars[51] >= _Ch))
return _Ch - _base64Chars[26] + 26;
if((_base64Chars[52] <= _Ch) && (_base64Chars[61] >= _Ch))
return _Ch - _base64Chars[52] + 52;
if(_Ch == _Tr::to_int_type('='))
return _EQUAL_CHAR;
return _UNKNOWN_CHAR;
}
};
#endif
+9 -5
View File
@@ -13,8 +13,11 @@ INCLUDES = -I$(top_srcdir)/src/include \
-I$(top_srcdir)/lib/goom \
-I$(top_srcdir)/lib/tbt \
-I$(top_srcdir)/lib/slw \
-I$(top_srcdir)/lib/xmlrpc++ \
-I/usr/include/slang \
@THEORA_MMX_INCLUDE@
@THEORA_MMX_INCLUDE@ \
@XIPH_INCLUDE@
# -I$(top_srcdir)/lib/portaudio-v19/pa_common
@@ -48,6 +51,7 @@ freej_SOURCES = \
jutils.cpp \
fastmemcpy.cpp \
ringbuffer.cpp \
xmlrpc_ctrl.cpp \
\
v4l_layer.cpp tvfreq.c \
v4l_layer_js.cpp \
@@ -93,8 +97,7 @@ freej_SOURCES = \
freej_LDADD = $(top_srcdir)/lib/ccvt/libccvt.a \
@THEORA_LIBS@ \
@XIPH_LIBS@ \
@AVFORMAT_LIBS@ \
@AVCODEC_LIBS@ \
@FFMPEG_LIBS@ \
$(top_srcdir)/lib/portaudio/libportaudio.a \
@PORTAUDIO_LIBS@ \
$(top_srcdir)/lib/sdl_image/libsdl_image.a \
@@ -105,8 +108,9 @@ freej_LDADD = $(top_srcdir)/lib/ccvt/libccvt.a \
$(top_srcdir)/lib/goom/libgoom2.a \
$(top_srcdir)/lib/tbt/libtbt.a \
$(top_srcdir)/lib/slw/libslw.a \
$(top_srcdir)/lib/shout/libshout.a \
-ldl -lpthread -lm -lslang -lpng -ljpeg \
$(top_srcdir)/lib/shout/libshout.a \
$(top_srcdir)/lib/xmlrpc++/libxmlrpc.a \
-ldl -lpthread -lm -lslang -lpng -ljpeg -lssl \
@STATIC_LIBS@ \
@OPENGL_LIB@ \
@SDL_LIBS@ \
+1 -2
View File
@@ -78,7 +78,6 @@ static const char *short_options = "-hvD:gs:nj:cgf:F";
/* this is the global FreeJ context */
Context freej;
int debug_level = 0;
char layer_files[MAX_CLI_CHARS];
int cli_chars = 0;
@@ -95,6 +94,7 @@ bool opengl = false;
bool noconsole = false;
bool fullscreen = false;
void cmdline(int argc, char **argv) {
int res, optlen;
@@ -346,7 +346,6 @@ int main (int argc, char **argv) {
or the pasta for the pizza. it's an intense
action for your arms, processing materia */
freej.cafudda(1.0);
/* also layers have the cafudda() function
which is called by the Context class (freej instance here)
so it's a tree of cafudda calls originating from here
+8
View File
@@ -55,6 +55,9 @@ class JsParser;
class AudioInput;
class VideoEncoder;
class FreejDaemon;
namespace XmlRpc { class XmlRpcServer; }
class Context {
private:
/* ---- fps ---- */
@@ -153,6 +156,11 @@ class Context {
JsParser *js; ///< javascript parser object
XmlRpc::XmlRpcServer *xmlrpc; ///< xmlrpc interface
FreejDaemon *daemon; ///< xmlrpc daemon
/* Set the interval (in frames) after
the fps counter is updated */
+3
View File
@@ -50,6 +50,9 @@ class Controller: public Entry {
bool initialized;
bool active;
JSContext *jsenv;
JSObject *jsobj;
};
#endif
-2
View File
@@ -36,8 +36,6 @@ class JoyCtrl : Controller {
private:
JSContext *jsenv;
JSObject *jsobj;
SDL_Joystick *joy[4];
int num;
+5
View File
@@ -76,6 +76,7 @@ JS(js_joy_ctrl_constructor);
JS(js_midi_ctrl_constructor);
#endif
JS(js_trigger_ctrl_constructor);
JS(js_xmlrpc_ctrl_constructor);
// encoder constructor
#ifdef WITH_OGGTHEORA
@@ -115,6 +116,10 @@ extern JSFunctionSpec js_midi_ctrl_methods[];
extern JSClass js_trigger_ctrl_class;
extern JSFunctionSpec js_trigger_ctrl_methods[];
// XmlRpc Controller
extern JSClass js_xmlrpc_ctrl_class;
extern JSFunctionSpec js_xmlrpc_ctrl_methods[];
// ParticleLayer
extern JSClass particle_layer_class;
extern JSFunctionSpec particle_layer_methods[];
-2
View File
@@ -36,8 +36,6 @@ class KbdCtrl: public Controller {
private:
SDL_keysym *keysym;
JSContext *jsenv;
JSObject *jsobj;
};
-2
View File
@@ -46,8 +46,6 @@ class MidiControl: public Controller {
//bool quit;
private:
JSContext *jsenv; // cx
JSObject *jsobj; // obj
//int midi_action();
-2
View File
@@ -36,8 +36,6 @@ class TriggerCtrl : Controller {
private:
JSContext *jsenv;
JSObject *jsobj;
int _poll(Context *env);
+78
View File
@@ -0,0 +1,78 @@
/* XmlRpc daemon code - originally from IvySync - using xmlrpc++
*
* (c) Copyright 2004 - 2007 Denis Rojo <jaromil@dyne.org>
* Nederlands Instituut voor Mediakunst
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This source code 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.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __FREEJ_XMLRPC_H__
#define __FREEJ_XMLRPC_H__
#include <controller.h>
#include <XmlRpc.h>
#include <iostream>
#include <stdlib.h>
using namespace XmlRpc;
class Context;
// XMLRPC METHODS
class ExecMethod;
class FreejDaemon: public Controller {
public:
FreejDaemon();
~FreejDaemon();
bool init(JSContext *env, JSObject *obj);
int peep(Context *env);
int poll(Context *env);
Context *freej;
private:
XmlRpc::XmlRpcServer *xmlrpc;
ExecMethod *exec;
};
class ExecMethod: public XmlRpc::XmlRpcServerMethod {
public:
ExecMethod(XmlRpc::XmlRpcServer *srv, FreejDaemon *freej);
~ExecMethod();
void execute(XmlRpcValue &params, XmlRpcValue &result);
std::string help() {
return std::string("Execute a script in FreeJ"); }
FreejDaemon *daemon;
};
#endif
+12 -6
View File
@@ -196,6 +196,18 @@ void JsParser::init() {
js_trigger_ctrl_constructor,
js_trigger_ctrl_methods);
#ifdef WITH_MIDI
REGISTER_CLASS("MidiController",
js_midi_ctrl_class,
js_midi_ctrl_constructor,
js_midi_ctrl_methods);
#endif
REGISTER_CLASS("XmlRpcController",
js_xmlrpc_ctrl_class,
js_xmlrpc_ctrl_constructor,
js_xmlrpc_ctrl_methods);
#ifdef WITH_OGGTHEORA
// encoder class
REGISTER_CLASS("VideoEncoder",
@@ -204,12 +216,6 @@ void JsParser::init() {
js_vid_enc_methods);
#endif
#ifdef WITH_MIDI
REGISTER_CLASS("MidiController",
js_midi_ctrl_class,
js_midi_ctrl_constructor,
js_midi_ctrl_methods);
#endif
// JS_DefineProperties(js_context, layer_object, layer_properties);
+135
View File
@@ -0,0 +1,135 @@
/* XmlRpc daemon code - originally from IvySync - using xmlrpc++
*
* (c) Copyright 2004 - 2007 Denis Rojo <jaromil@dyne.org>
* Nederlands Instituut voor Mediakunst
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This source code 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.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <xmlrpc_ctrl.h>
#include <context.h>
#include <jutils.h>
#include <callbacks_js.h> // javascript
/////// Javascript XmlRpcController
JS(js_xmlrpc_ctrl_constructor);
DECLARE_CLASS("XmlRpcController", js_xmlrpc_ctrl_class, js_xmlrpc_ctrl_constructor);
JSFunctionSpec js_xmlrpc_ctrl_methods[] = { {0} }; // TODO
FreejDaemon::FreejDaemon()
:Controller() {
xmlrpc = new XmlRpc::XmlRpcServer();
xmlrpc->_ssl = false;
xmlrpc->_ssl_ssl = NULL;
exec = NULL;
set_name("XmlRpc");
}
FreejDaemon::~FreejDaemon() {
if(exec) free(exec);
}
bool FreejDaemon::init(JSContext *env, JSObject *obj) {
if(!exec)
exec = new ExecMethod(xmlrpc, this);
if( ! xmlrpc->bindAndListen(2640) ) {
error("XmlRpc cannot bind and listen");
return false;
}
jsenv = env;
jsobj = obj;
initialized = true;
// to be introspective we can list our own methods
// xmlrpc->enableIntrospection(true);
return true;
}
int FreejDaemon::peep(Context *env) {
func("XmlRpcDaemon::peep");
// run for amount of milliseconds (-1.0 for infinite)
xmlrpc->work( 1.0 );
return 1;
}
int FreejDaemon::poll(Context *env) {
return 1;
}
ExecMethod::ExecMethod(XmlRpcServer *srv, FreejDaemon *freej)
: XmlRpcServerMethod("Exec", srv)
{
daemon = freej;
}
ExecMethod::~ExecMethod()
{ }
void ExecMethod::execute(XmlRpcValue &params, XmlRpcValue &result) {
func("XMLRPC: Exec method called");
char *script;
if( params.size() < 1) {
error("XMLRPC: Exec called without any script", params.size());
result = 0.0;
return;
}
// int t = params.getType();
// if(t!=TypeString) error("parameter is not a string");
script = (char*)string(params[0]).c_str();
act("XMLRPC: received script of size %u", params[0].size());
daemon->freej->open_script( script );
act("XMLRPC: script executed");
}
JS(js_xmlrpc_ctrl_constructor) {
func("%u:%s:%s",__LINE__,__FILE__,__FUNCTION__);
FreejDaemon *daemon = new FreejDaemon();
// assign instance into javascript object
if( ! JS_SetPrivate(cx, obj, (void*)daemon) ) {
error("failed assigning xmlrpc daemon controller to javascript");
delete daemon; return JS_FALSE;
}
// initialize with javascript context
if(! daemon->init(cx, obj) ) {
error("failed initializing xmlrpc daemon controller");
delete daemon; return JS_FALSE;
}
*rval = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}