This commit is contained in:
Ivan
2022-04-05 11:42:28 +03:00
commit 6dc0eb0fcf
5565 changed files with 1200500 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,472 @@
#include "xmlrpcpp/XmlRpcClient.h"
#include "xmlrpcpp/XmlRpcSocket.h"
#include "xmlrpcpp/XmlRpc.h"
#include <stdio.h>
#include <stdlib.h>
#ifndef _WINDOWS
# include <strings.h>
#endif
#include <string.h>
using namespace XmlRpc;
// 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;
else
_uri = "/RPC2";
_connectionState = NO_CONNECTION;
_executing = false;
_eof = false;
// Default to keeping the connection open until an explicit close is done
setKeepOpen();
}
XmlRpcClient::~XmlRpcClient()
{
this->close();
}
// Close the owned fd
void
XmlRpcClient::close()
{
XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
_connectionState = NO_CONNECTION;
_disp.exit();
_disp.removeSource(this);
XmlRpcSource::close();
}
// 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;
}
// Execute the named procedure on the remote server, non-blocking.
// 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::executeNonBlock(const char* method, XmlRpcValue const& params)
{
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;
return true;
}
bool
XmlRpcClient::executeCheckDone(XmlRpcValue& result)
{
result.clear();
// Are we done yet?
if (_connectionState != IDLE)
return false;
if (! parseResponse(result))
{
// Hopefully the caller can determine that parsing failed.
}
//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 (_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;
}
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];
#ifdef _MSC_VER
sprintf_s(buff,40,":%d\r\n", _port);
#else
sprintf(buff,":%d\r\n", _port);
#endif
header += buff;
header += "Content-Type: text/xml\r\nContent-length: ";
#ifdef _MSC_VER
sprintf_s(buff,40,"%d\r\n\r\n", (int)body.size());
#else
sprintf(buff,"%d\r\n\r\n", (int)body.size());
#endif
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)) {
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) ||
(_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)) {
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();
}

View File

@@ -0,0 +1,231 @@
#include "xmlrpcpp/XmlRpcDispatch.h"
#include "xmlrpcpp/XmlRpcSource.h"
#include "xmlrpcpp/XmlRpcUtil.h"
#include "ros/time.h"
#include <math.h>
#include <errno.h>
#include <sys/timeb.h>
#include <sys/poll.h>
#if defined(_WINDOWS)
# include <winsock2.h>
static inline int poll( struct pollfd *pfd, int nfds, int timeout)
{
return WSAPoll(pfd, nfds, timeout);
}
# define USE_FTIME
# if defined(_MSC_VER)
# define timeb _timeb
# define ftime _ftime_s
# 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)
{
// Loosely based on `man select` > Correspondence between select() and poll() notifications
// and cloudius-systems/osv#35, cloudius-systems/osv@b53d39a using poll to emulate select
const unsigned POLLIN_REQ = POLLIN; // Request read
const unsigned POLLIN_CHK = (POLLIN | POLLHUP | POLLERR); // Readable or connection lost
const unsigned POLLOUT_REQ = POLLOUT; // Request write
const unsigned POLLOUT_CHK = (POLLOUT | POLLERR); // Writable or connection lost
const unsigned POLLEX_CHK = (POLLPRI | POLLNVAL); // Exception or invalid fd
// Compute end time
_endTime = (timeout < 0.0) ? -1.0 : (getTime() + timeout);
_doClear = false;
_inWork = true;
int timeout_ms = static_cast<int>(floor(timeout * 1000.));
// Only work while there is something to monitor
while (_sources.size() > 0) {
// Construct the sets of descriptors we are interested in
const unsigned source_cnt = _sources.size();
pollfd fds[source_cnt];
XmlRpcSource * sources[source_cnt];
SourceList::iterator it;
std::size_t i = 0;
for (it=_sources.begin(); it!=_sources.end(); ++it, ++i) {
sources[i] = it->getSource();
fds[i].fd = sources[i]->getfd();
fds[i].revents = 0; // some platforms may not clear this in poll()
fds[i].events = 0;
if (it->getMask() & ReadableEvent) fds[i].events |= POLLIN_REQ;
if (it->getMask() & WritableEvent) fds[i].events |= POLLOUT_REQ;
}
// Check for events
int nEvents = poll(fds, source_cnt, (timeout_ms < 0) ? -1 : timeout_ms);
if (nEvents < 0)
{
if(errno != EINTR)
XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in poll (%d).", nEvents);
_inWork = false;
return;
}
// Process events
for (i=0; i < source_cnt; ++i)
{
XmlRpcSource* src = sources[i];
pollfd & pfd = fds[i];
unsigned newMask = (unsigned) -1;
// Only handle requested events to avoid being prematurely removed from dispatch
bool readable = (pfd.events & POLLIN_REQ) == POLLIN_REQ;
bool writable = (pfd.events & POLLOUT_REQ) == POLLOUT_REQ;
if (readable && (pfd.revents & POLLIN_CHK))
newMask &= src->handleEvent(ReadableEvent);
if (writable && (pfd.revents & POLLOUT_CHK))
newMask &= src->handleEvent(WritableEvent);
if (pfd.revents & POLLEX_CHK)
newMask &= src->handleEvent(Exception);
// Find the source iterator. It may have moved as a result of the way
// that sources are removed and added in the call stack starting
// from the handleEvent() calls above.
SourceList::iterator thisIt;
for (thisIt = _sources.begin(); thisIt != _sources.end(); thisIt++)
{
if(thisIt->getSource() == src)
break;
}
if(thisIt == _sources.end())
{
XmlRpcUtil::error("Error in XmlRpcDispatch::work: couldn't find source iterator");
continue;
}
if ( ! newMask) {
_sources.erase(thisIt); // Stop monitoring this one
if ( ! src->getKeepOpen())
src->close();
} else if (newMask != (unsigned) -1) {
thisIt->getMask() = newMask;
}
}
// 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
if (0 <= _endTime && getTime() > _endTime)
break;
}
_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();
}
}
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
uint32_t sec, nsec;
ros::ros_steadytime(sec, nsec);
return ((double)sec + (double)nsec / 1e9);
#endif /* USE_FTIME */
}

View File

@@ -0,0 +1,290 @@
// this file modified by Morgan Quigley on 22 Apr 2008.
// added features: server can be opened on port 0 and you can read back
// what port the OS gave you
#include "xmlrpcpp/XmlRpcServer.h"
#include "xmlrpcpp/XmlRpcServerConnection.h"
#include "xmlrpcpp/XmlRpcServerMethod.h"
#include "xmlrpcpp/XmlRpcSocket.h"
#include "xmlrpcpp/XmlRpcUtil.h"
#include "xmlrpcpp/XmlRpcException.h"
using namespace XmlRpc;
XmlRpcServer::XmlRpcServer()
{
_introspectionEnabled = false;
_listMethods = 0;
_methodHelp = 0;
_port = 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;
}
_port = XmlRpcSocket::get_port(fd);
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;
}
// 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)
{
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");
_disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent);
}
}
// 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);
}
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&, 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 XmlRpcServerConnection
result[i] = MULTICALL;
}

View File

@@ -0,0 +1,381 @@
#include "xmlrpcpp/XmlRpcServerConnection.h"
#include "xmlrpcpp/XmlRpcSocket.h"
#include "xmlrpcpp/XmlRpc.h"
#ifndef MAKEDEPEND
# include <stdio.h>
# include <stdlib.h>
#ifndef _WINDOWS
# include <strings.h>
#endif
# include <string.h>
#endif
using namespace XmlRpc;
// Static data
const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
const std::string XmlRpcServerConnection::METHODNAME = "methodName";
const std::string XmlRpcServerConnection::PARAMS = "params";
const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
// 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;
_contentLength = 0;
_bytesWritten = 0;
_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)) {
// 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)) {
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)) {
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
}
// Run the method, generate _response string
void
XmlRpcServerConnection::executeRequest()
{
XmlRpcValue params, resultValue;
std::string methodName = parseRequest(params);
XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'",
methodName.c_str());
try {
if ( ! executeMethod(methodName, params, resultValue) &&
! executeMulticall(methodName, params, resultValue))
generateFaultResponse(methodName + ": unknown method name");
else
generateResponse(resultValue.toXml());
} catch (const XmlRpcException& fault) {
XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
fault.getMessage().c_str());
generateFaultResponse(fault.getMessage(), fault.getCode());
}
}
// Parse the method name and the argument values from the request.
std::string
XmlRpcServerConnection::parseRequest(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
XmlRpcServerConnection::executeMethod(const std::string& methodName,
XmlRpcValue& params, XmlRpcValue& result)
{
XmlRpcServerMethod* method = _server->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
XmlRpcServerConnection::executeMulticall(const std::string& methodName,
XmlRpcValue& params, XmlRpcValue& result)
{
if (methodName != SYSTEM_MULTICALL) return false;
// There ought to be 1 parameter, an array of structs
if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
throw XmlRpcException(SYSTEM_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] = SYSTEM_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
void
XmlRpcServerConnection::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);
_response = header + body;
XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str());
}
// Prepend http headers
std::string
XmlRpcServerConnection::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];
#ifdef _MSC_VER
sprintf_s(buffLen,40,"%d\r\n\r\n", (int)body.size());
#else
sprintf(buffLen,"%d\r\n\r\n", (int)body.size());
#endif
return header + buffLen;
}
void
XmlRpcServerConnection::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);
_response = header + body;
}

View File

@@ -0,0 +1,21 @@
#include "xmlrpcpp/XmlRpcServerMethod.h"
#include "xmlrpcpp/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

View File

@@ -0,0 +1,389 @@
// this file modified by Morgan Quigley on 22 Apr 2008.
// added features: server can be opened on port 0 and you can read back
// what port the OS gave you
#include "xmlrpcpp/XmlRpcSocket.h"
#include "xmlrpcpp/XmlRpcUtil.h"
#ifndef MAKEDEPEND
#if defined(_WINDOWS)
# include <stdio.h>
# include <winsock2.h>
# include <ws2tcpip.h>
//# pragma lib(WS2_32.lib)
// MS VS10 actually has these definitions (as opposed to earlier versions),
// so if present, temporarily disable them and reset to WSA codes for this file only.
#ifdef EAGAIN
#undef EAGAIN
#endif
#ifdef EINTR
#undef EINTR
#endif
#ifdef EINPROGRESS
#undef EINPROGRESS
#endif
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif
#ifdef ETIMEDOUT
#undef ETIMEDOUT
#endif
# define EAGAIN WSATRY_AGAIN
# define EINTR WSAEINTR
# define EINPROGRESS WSAEINPROGRESS
# define EWOULDBLOCK WSAEWOULDBLOCK
# define ETIMEDOUT WSAETIMEDOUT
#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>
# include <string.h>
# include <stdlib.h>
# include <arpa/inet.h>
}
#endif // _WINDOWS
#endif // MAKEDEPEND
// MSG_NOSIGNAL does not exists on OS X
#if defined(__APPLE__) || defined(__MACH__)
# ifndef MSG_NOSIGNAL
# define MSG_NOSIGNAL SO_NOSIGPIPE
# endif
#endif
using namespace XmlRpc;
bool XmlRpcSocket::s_use_ipv6_ = false;
#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.
static inline bool
nonFatalError()
{
int err = XmlRpcSocket::getError();
return (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
}
int
XmlRpcSocket::socket()
{
initWinSock();
return (int) ::socket(s_use_ipv6_ ? AF_INET6 : 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)
{
sockaddr_storage ss;
socklen_t ss_len;
memset(&ss, 0, sizeof(ss));
if (s_use_ipv6_)
{
sockaddr_in6 *address = (sockaddr_in6 *)&ss;
ss_len = sizeof(sockaddr_in6);
address->sin6_family = AF_INET6;
address->sin6_addr = in6addr_any;
address->sin6_port = htons((u_short) port);
}
else
{
sockaddr_in *address = (sockaddr_in *)&ss;
ss_len = sizeof(sockaddr_in);
address->sin_family = AF_INET;
address->sin_addr.s_addr = htonl(INADDR_ANY);
address->sin_port = htons((u_short) port);
}
return (::bind(fd, (sockaddr*)&ss, ss_len) == 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;
#if defined(_WINDOWS)
int
#else
socklen_t
#endif
addrlen = sizeof(addr);
// accept will truncate the address if the buffer is too small.
// As we are not using it, no special case for IPv6
// has to be made.
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)
{
sockaddr_storage ss;
socklen_t ss_len;
memset(&ss, 0, sizeof(ss));
struct addrinfo* addr;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
if (getaddrinfo(host.c_str(), NULL, &hints, &addr) != 0)
{
fprintf(stderr, "Couldn't find an %s address for [%s]\n", s_use_ipv6_ ? "AF_INET6" : "AF_INET", host.c_str());
return false;
}
bool found = false;
struct addrinfo* it = addr;
for (; it; it = it->ai_next)
{
if (!s_use_ipv6_ && it->ai_family == AF_INET)
{
sockaddr_in *address = (sockaddr_in *)&ss;
ss_len = sizeof(sockaddr_in);
memcpy(address, it->ai_addr, it->ai_addrlen);
address->sin_family = it->ai_family;
address->sin_port = htons((u_short) port);
XmlRpcUtil::log(5, "found host as %s\n", inet_ntoa(address->sin_addr));
found = true;
break;
}
if (s_use_ipv6_ && it->ai_family == AF_INET6)
{
sockaddr_in6 *address = (sockaddr_in6 *)&ss;
ss_len = sizeof(sockaddr_in6);
memcpy(address, it->ai_addr, it->ai_addrlen);
address->sin6_family = it->ai_family;
address->sin6_port = htons((u_short) port);
char buf[128];
// TODO IPV6: check if this also works under Windows
XmlRpcUtil::log(5, "found ipv6 host as %s\n", inet_ntop(AF_INET6, (void*)&(address->sin6_addr), buf, sizeof(buf)));
found = true;
break;
}
}
if (!found)
{
printf("Couldn't find an %s address for [%s]\n", s_use_ipv6_ ? "AF_INET6" : "AF_INET", host.c_str());
freeaddrinfo(addr);
return false;
}
// 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, (sockaddr*)&ss, ss_len);
if (result != 0 ) {
int error = getError();
if ( (error != EINPROGRESS) && error != EWOULDBLOCK) { // actually, should probably do a platform check here, EWOULDBLOCK on WIN32 and EINPROGRESS otherwise
printf("::connect error = %d\n", getError());
}
}
freeaddrinfo(addr);
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)
{
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 = 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)
{
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 = 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;
}
// 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];
#ifdef _MSC_VER
strerror_s(err,60,error);
#else
snprintf(err,sizeof(err),"%s",strerror(error));
#endif
return std::string(err);
}
int XmlRpcSocket::get_port(int socket)
{
sockaddr_storage ss;
socklen_t ss_len = sizeof(ss);
getsockname(socket, (sockaddr *)&ss, &ss_len);
sockaddr_in *sin = (sockaddr_in *)&ss;
sockaddr_in6 *sin6 = (sockaddr_in6 *)&ss;
switch (ss.ss_family)
{
case AF_INET:
return ntohs(sin->sin_port);
case AF_INET6:
return ntohs(sin6->sin6_port);
}
return 0;
}

View File

@@ -0,0 +1,35 @@
#include "xmlrpcpp/XmlRpcSource.h"
#include "xmlrpcpp/XmlRpcSocket.h"
#include "xmlrpcpp/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;
}
}
} // namespace XmlRpc

View File

@@ -0,0 +1,261 @@
#include "xmlrpcpp/XmlRpcUtil.h"
#ifndef MAKEDEPEND
# include <ctype.h>
# include <iostream>
# include <stdarg.h>
# include <stdio.h>
# include <string.h>
#endif
#include "xmlrpcpp/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.7";
// 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:
#ifdef USE_WINDOWS_DEBUG
void error(const char* msg) {
OutputDebugString(msg); OutputDebugString("\n");
#else
void error(const char*) {
#endif
// As far as I can tell, throwing an exception here is a bug, unless
// the intention is that the program should exit. Throughout the code,
// calls to error() are followed by cleanup code that does things like
// closing a failed socket. Thus it would seem that it should be
// possible to continue execution. But if the user just catches the
// exception that's thrown here, the library ends up in a bogus state.
// So I'm commenting out the throw. - BPG
//
//throw std::runtime_error(msg);
}
} 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);
va_end(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);
va_end(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 '<'
std::string
XmlRpcUtil::getNextTag(std::string const& xml, int* offset)
{
if (*offset >= int(xml.length())) return std::string();
size_t pos = *offset;
const char* cp = xml.c_str() + pos;
while (*cp && isspace(*cp)) {
++cp;
++pos;
}
if (*cp != '<') return std::string();
std::string s;
do {
s += *cp;
++pos;
} while (*cp++ != '>' && *cp != 0);
*offset = int(pos);
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;
}

View File

@@ -0,0 +1,636 @@
#include "xmlrpcpp/XmlRpcValue.h"
#include "xmlrpcpp/XmlRpcException.h"
#include "xmlrpcpp/XmlRpcUtil.h"
#include "xmlrpcpp/base64.h"
#ifndef MAKEDEPEND
# include <iostream>
# include <ostream>
# include <stdlib.h>
# include <stdio.h>
#endif
#include <sstream>
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("%.16g");
// 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 == t2.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;
// ticket #2438
// push/pop the locale here. Value 123.45 can get read by strtod
// as '123', if the locale expects a comma instead of dot.
// if there are locale problems, silently continue.
std::string tmplocale;
char* locale_cstr = setlocale(LC_NUMERIC, 0);
if (locale_cstr)
{
tmplocale = locale_cstr;
setlocale(LC_NUMERIC, "POSIX");
}
double dvalue = strtod(valueStart, &valueEnd);
if (tmplocale.size() > 0) {
setlocale(LC_NUMERIC, tmplocale.c_str());
}
if (valueEnd == valueStart)
return false;
_type = TypeDouble;
_value.asDouble = dvalue;
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::doubleToXml() const
{
// ticket #2438
std::stringstream ss;
ss.imbue(std::locale::classic()); // ensure we're using "C" locale for formatting floating-point (1.4 vs. 1,4, etc.)
ss.precision(17);
ss << _value.asDouble;
std::string xml = VALUE_TAG;
xml += DOUBLE_TAG;
xml += ss.str();
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;
#ifdef _MSC_VER
if (sscanf_s(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)
#else
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)
#endif
return false;
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, "%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;
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, const XmlRpc::XmlRpcValue& v)
{
// If you want to output in xml format:
//return os << v.toXml();
return v.write(os);
}