summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjaromil <jaromil@949728d9-16ea-0310-a75c-cbdf8430a4b8>2005-11-18 16:22:32 (GMT)
committer jaromil <jaromil@949728d9-16ea-0310-a75c-cbdf8430a4b8>2005-11-18 16:22:32 (GMT)
commit86d7f172e1766da6f8ded42203b3b964c5b3652d (patch)
tree279743cf9313934887d149994afe6c74703702a9
parent24a2282f30627cbb882f1c2613721948a63b7ffc (diff)
new XMLRPC interface for remotely controlled syncstarting,
(xmlrpc++ library by Chris Morley was included), Get and Set position of decoder XMLRPC methods were exposed git-svn-id: svn://dyne.org/montevideo/ivysync@47 949728d9-16ea-0310-a75c-cbdf8430a4b8
-rw-r--r--Makefile16
-rw-r--r--cmdline.cpp65
-rw-r--r--decoder.cpp65
-rw-r--r--decoder.h9
-rw-r--r--xmlrpc++/Doxyfile1041
-rw-r--r--xmlrpc++/Makefile34
-rw-r--r--xmlrpc++/README102
-rw-r--r--xmlrpc++/XmlRpc.h97
-rw-r--r--xmlrpc++/XmlRpcClient.cpp547
-rw-r--r--xmlrpc++/XmlRpcClient.h148
-rw-r--r--xmlrpc++/XmlRpcDispatch.cpp268
-rw-r--r--xmlrpc++/XmlRpcDispatch.h93
-rw-r--r--xmlrpc++/XmlRpcException.h42
-rw-r--r--xmlrpc++/XmlRpcMutex.cpp60
-rw-r--r--xmlrpc++/XmlRpcMutex.h46
-rw-r--r--xmlrpc++/XmlRpcServer.cpp494
-rw-r--r--xmlrpc++/XmlRpcServer.h160
-rw-r--r--xmlrpc++/XmlRpcServerConnection.cpp205
-rw-r--r--xmlrpc++/XmlRpcServerConnection.h82
-rw-r--r--xmlrpc++/XmlRpcServerMethod.cpp21
-rw-r--r--xmlrpc++/XmlRpcServerMethod.h47
-rw-r--r--xmlrpc++/XmlRpcSocket.cpp285
-rw-r--r--xmlrpc++/XmlRpcSocket.h76
-rw-r--r--xmlrpc++/XmlRpcSource.cpp40
-rw-r--r--xmlrpc++/XmlRpcSource.h67
-rw-r--r--xmlrpc++/XmlRpcThread.cpp74
-rw-r--r--xmlrpc++/XmlRpcThread.h60
-rw-r--r--xmlrpc++/XmlRpcThreadedServer.cpp27
-rw-r--r--xmlrpc++/XmlRpcThreadedServer.h67
-rw-r--r--xmlrpc++/XmlRpcUtil.cpp259
-rw-r--r--xmlrpc++/XmlRpcUtil.h61
-rw-r--r--xmlrpc++/XmlRpcValue.cpp611
-rw-r--r--xmlrpc++/XmlRpcValue.h259
-rw-r--r--xmlrpc++/base64.h379
34 files changed, 5872 insertions, 35 deletions
diff --git a/Makefile b/Makefile
index fbc16e9..14c5aa9 100644
--- a/Makefile
+++ b/Makefile
@@ -10,24 +10,28 @@ CPP = g++
LINKER = ld
# debugging flags:
-#CPPFLAGS = -I. -Wall -ggdb -pg `pkg-config --cflags gtk+-2.0`
+CPPFLAGS = -I. -Ixmlrpc++ -Wall -ggdb -pg `pkg-config --cflags gtk+-2.0`
# optimized flags:
-CPPFLAGS = -I. -Wall -O2 -fomit-frame-pointer -ffast-math -march=pentium3 \
+#CPPFLAGS = -I. -Ixmlrpc++ -Wall -O2 -fomit-frame-pointer -ffast-math -march=pentium3 \
`pkg-config --cflags gtk+-2.0`
-LIBS = -lpthread `pkg-config --libs gtk+-2.0`
+LIBS = xmlrpc++/libxmlrpc++.a -lpthread -lssl `pkg-config --libs gtk+-2.0`
-OBJ = decoder.o thread.o utils.o cmdline.o gui.o
+OBJ = decoder.o thread.o utils.o cmdline.o gui.o xmlrpc.o
-all: ivysync
+all: xmlrpc ivysync
+
+xmlrpc:
+ cd xmlrpc++ && $(MAKE)
ivysync: $(OBJ)
$(CPP) $(CPPFLAGS) -o ivysync $(OBJ) $(LIBS)
+
#make clean
clean:
- rm -rf *.o *~ ivysync
+ rm -rf *.o *~ ivysync xmlrpc++/*.o xmlrpc++/*.a xmlrpc++/*~
install: ivysync
install ivysync /usr/local/bin
diff --git a/cmdline.cpp b/cmdline.cpp
index d2bca03..fdc9f98 100644
--- a/cmdline.cpp
+++ b/cmdline.cpp
@@ -1,8 +1,8 @@
/* IvySync - Video SyncStarter
*
- * (c) Copyright 2004 Denis Roio aka jaromil <jaromil@dyne.org>
- * Nederlands Instituut voor Mediakunst
+ * (c) Copyright 2004-2005 Denis Roio aka jaromil <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
@@ -32,13 +32,17 @@
#include <signal.h>
#include <decoder.h>
-#include <utils.h>
+
+#include <xmlrpc.h>
#include <gui.h>
+#include <utils.h>
+
+
bool syncstart = false;
-bool daemonize = false;
bool graphical = false;
bool dummytest = false;
+bool rpcdaemon = false;
// our global vector holding all instantiated decoders
vector<Decoder*> decoders;
@@ -46,22 +50,28 @@ vector<Decoder*> decoders;
// graphical interface
Gui *gui;
+// xmlrpc interface
+XmlRpcServer *xmlrpc;
+
+// Threaded daemon
+IvySyncDaemon *daemonthread;
+
char *help =
"Usage: ivysync [-hsDgt] [ -d /dev/video16 [ -p playmode files ] ]\n"
" -h --help show this help\n"
" -d --device activate a device (i.e. /dev/video16)\n"
" -s --scan scan for available devices\n"
-" -D --daemon run in daemon mode\n"
+" -x --xmlrpc run XmlRpc daemon\n"
" -p --playmode playlist mode (play|cont|loop|rand)\n"
" -g --gui start the graphical user interface\n"
" -t --test dummy testrun: don't open devices\n";
-char *short_options = "-hd:sDp:gt";
+char *short_options = "-hd:sxp:gt";
const struct option long_options[] = {
{ "help", no_argument, NULL, 'h'},
{ "device", required_argument, NULL, 'd'},
{ "scan", no_argument, NULL, 's'},
- { "daemon", no_argument, NULL, 'D'},
+ { "xmlrpc", no_argument, NULL, 'x'},
{ "playmode", required_argument, NULL, 'p'},
{ "gui", no_argument, NULL, 'g'},
{ "test", no_argument, NULL, 't'},
@@ -176,8 +186,8 @@ int cmdline(int argc, char **argv) {
break;
- case 'D':
- daemonize = true;
+ case 'x':
+ rpcdaemon = true;
break;
case 'g':
@@ -236,7 +246,8 @@ int main(int argc, char **argv) {
exit(0);
}
- // check for graphical interface
+ /////////////////////////////////
+ // setup the graphical interface
if(graphical)
if(!getenv("DISPLAY")) {
graphical = false;
@@ -254,7 +265,29 @@ int main(int argc, char **argv) {
exit(1);
}
+ ////////////////////////////////
+
+
+ ////////////////////////////////
+ /// setup the XMLRPC interface
+ if(rpcdaemon) {
+ xmlrpc = new XmlRpcServer();
+ // instantiate all classes
+ new GetPos(xmlrpc, &decoders);
+ new SetPos(xmlrpc, &decoders);
+ // instantiate and launch the threaded daemon
+ daemonthread = new IvySyncDaemon(xmlrpc);
+ daemonthread->launch();
+
+ }
+
+ ////////////////////////////////
+
+
+
+ ////////////////////////////////
+ /// Syncstart!
for( dec_iter = decoders.begin();
dec_iter != decoders.end();
++dec_iter) {
@@ -271,15 +304,15 @@ int main(int argc, char **argv) {
jsleep(0,500);
A("Start!");
syncstart = 1;
- // jsleep(0,2);
- // syncstart = 0;
- // jsleep(0,500);
- // syncstart = 1;
+ ////////////////////////////////
+
int still_running = decoders.size();
- if(daemonize) while(true); // loop infinitely
- else while(still_running) {
+ // if(daemonize) while(true); // loop infinitely
+ // else
+
+ while(still_running) {
still_running = 0;
for( dec_iter = decoders.begin();
dec_iter != decoders.end();
diff --git a/decoder.cpp b/decoder.cpp
index 3a138eb..eca9ea4 100644
--- a/decoder.cpp
+++ b/decoder.cpp
@@ -40,6 +40,10 @@ Decoder::Decoder()
dummy = false;
gui = false;
+ filesize = 0L;
+ filepos = 0L;
+ newfilepos = 0L;
+
// memset(buffo,0,CHUNKSIZE+1024);
buffo = (uint8_t*) calloc(CHUNKSIZE,1);
if(!buffo)
@@ -173,7 +177,7 @@ void Decoder::run() {
if(!playlist_fd) {
E("can't open %s: %s (%i)", movie.c_str(), strerror(errno), errno);
- if(errno==27) { // EOVERFLOW - file too large
+ if(errno==27) { // EOVERFLOW - file too large on Linux/Glibc
int tmpfd;
tmpfd = open( movie.c_str(), O_RDONLY|O_LARGEFILE);
@@ -191,6 +195,14 @@ void Decoder::run() {
}
N("now playing %s",movie.c_str());
+
+ // read the total length of movie file
+ fseek(playlist_fd, 0L, SEEK_END);
+ filesize = ftell(playlist_fd);
+ fseek(playlist_fd, 0L, SEEK_SET);
+ filepos = 0L; // set position at the beginning
+
+ A("movie length: %lu (bytes)",filesize);
do { // inner reading loop
@@ -234,10 +246,13 @@ void Decoder::run() {
if(written<0) // error on write
continue;
+ else
+ filepos += written;
writing -= written;
flush();
+
}
} while(in>0 && !quit); // read/write inner loop
@@ -267,6 +282,13 @@ void Decoder::flush() {
if(quit) return;
}
}
+
+ // if there is a seek to do, do it now
+ if(newfilepos > 0L) {
+ D("seeking to new position %lu", newfilepos);
+ fseek(playlist_fd, newfilepos, SEEK_SET);
+ newfilepos = 0;
+ }
}
bool Decoder::play() {
@@ -283,6 +305,27 @@ bool Decoder::restart() {
return true;
}
+int Decoder::getpos() {
+ // filesize : 100 = filepos : x
+ // filesize : filepos = 100 : x
+ int percent;
+
+ percent = (filepos * 100) / filesize;
+ // D("movie %s at position %u %% (%lu byte)",
+ // movie.c_str(), percent, filepos);
+ return percent;
+}
+
+void Decoder::setpos(int pos) {
+ // filesize : 100 = x : pos
+
+ newfilepos = (filesize * pos) / 100;
+
+ D("Decoder::setpos(%u) : newfilepos = %lu",
+ pos, newfilepos);
+}
+
+
bool Decoder::prepend(char *file) {
playlist.insert( playlist.begin(), file );
return true;
@@ -428,16 +471,16 @@ int Decoder::load() {
} else if(now.tm_hour == pltime.tm_hour) {
- // same hour, let's check the minutes
- if(now.tm_min >= pltime.tm_min) {
-
- D("this playlist is scheduled right now");
- snprintf(ThePlaylist,511,"%s",filelist[found]->d_name);
- memcpy(&plseltime,&pltime,sizeof(struct tm));
-
- }
- } else D("this playlist will be activated later");
- }
+ // same hour, let's check the minutes
+ if(now.tm_min >= pltime.tm_min) {
+
+ D("this playlist is scheduled right now");
+ snprintf(ThePlaylist,511,"%s",filelist[found]->d_name);
+ memcpy(&plseltime,&pltime,sizeof(struct tm));
+
+ }
+ } else D("this playlist will be activated later");
+ }
}
snprintf(path,511,"%s/.ivysync/%s",home,ThePlaylist);
diff --git a/decoder.h b/decoder.h
index 30e4ea8..2b6e60f 100644
--- a/decoder.h
+++ b/decoder.h
@@ -73,6 +73,10 @@ class Decoder : public Thread {
bool play();
bool stop();
bool restart();
+
+ int getpos();
+ void setpos(int pos);
+
bool playing;
bool *syncstart;
@@ -92,8 +96,9 @@ class Decoder : public Thread {
void flush();
-
-
+ long long filesize; // current file playing, size in bytes
+ long long filepos; // current file playing, position in bytes
+ long long newfilepos; // new position to skip in file
int fd;
FILE *playlist_fd;
diff --git a/xmlrpc++/Doxyfile b/xmlrpc++/Doxyfile
new file mode 100644
index 0000000..2d8a346
--- /dev/null
+++ b/xmlrpc++/Doxyfile
@@ -0,0 +1,1041 @@
+# Doxyfile 1.3-rc3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = XmlRpc++
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.7
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ../doc
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = base64.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output dir.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse the
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/xmlrpc++/Makefile b/xmlrpc++/Makefile
new file mode 100644
index 0000000..bc8080a
--- /dev/null
+++ b/xmlrpc++/Makefile
@@ -0,0 +1,34 @@
+# makefile written for gnu make
+CXX = g++
+CXXFLAGS = -Wall -ggdb
+VERSION = 0.8
+LIB = ./libxmlrpc++.a
+
+# Add your system-dependent network libs here. These are
+# only used to build the tests (your application will need them too).
+# Linux: none
+# Solaris: -lsocket -lnsl
+#SYSTEMLIBS = -lsocket -lnsl
+SYSTEMLIBS =
+LDLIBS = $(LIB) $(SYSTEMLIBS)
+
+OBJ = XmlRpcClient.o XmlRpcDispatch.o \
+ XmlRpcServer.o XmlRpcServerConnection.o \
+ XmlRpcServerMethod.o XmlRpcSocket.o XmlRpcSource.o \
+ XmlRpcUtil.o XmlRpcValue.o
+
+all: $(LIB)
+
+$(LIB): $(OBJ)
+ $(AR) $(ARFLAGS) $(LIB) $(OBJ)
+
+doc doxygen:
+ doxygen Doxyfile
+
+distclean: clean
+
+clean:
+ rm -f *.o
+ rm -f *~
+ rm -f $(LIB)
+
diff --git a/xmlrpc++/README b/xmlrpc++/README
new file mode 100644
index 0000000..c5cb7d1
--- /dev/null
+++ b/xmlrpc++/README
@@ -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
diff --git a/xmlrpc++/XmlRpc.h b/xmlrpc++/XmlRpc.h
new file mode 100644
index 0000000..aa761f9
--- /dev/null
+++ b/xmlrpc++/XmlRpc.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcClient.cpp b/xmlrpc++/XmlRpcClient.cpp
new file mode 100644
index 0000000..af70715
--- /dev/null
+++ b/xmlrpc++/XmlRpcClient.cpp
@@ -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();
+}
+
diff --git a/xmlrpc++/XmlRpcClient.h b/xmlrpc++/XmlRpcClient.h
new file mode 100644
index 0000000..433e012
--- /dev/null
+++ b/xmlrpc++/XmlRpcClient.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcDispatch.cpp b/xmlrpc++/XmlRpcDispatch.cpp
new file mode 100644
index 0000000..9d675dc
--- /dev/null
+++ b/xmlrpc++/XmlRpcDispatch.cpp
@@ -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;
+}
diff --git a/xmlrpc++/XmlRpcDispatch.h b/xmlrpc++/XmlRpcDispatch.h
new file mode 100644
index 0000000..88fd657
--- /dev/null
+++ b/xmlrpc++/XmlRpcDispatch.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcException.h b/xmlrpc++/XmlRpcException.h
new file mode 100644
index 0000000..6090450
--- /dev/null
+++ b/xmlrpc++/XmlRpcException.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcMutex.cpp b/xmlrpc++/XmlRpcMutex.cpp
new file mode 100644
index 0000000..b29455d
--- /dev/null
+++ b/xmlrpc++/XmlRpcMutex.cpp
@@ -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
+
diff --git a/xmlrpc++/XmlRpcMutex.h b/xmlrpc++/XmlRpcMutex.h
new file mode 100644
index 0000000..f46373d
--- /dev/null
+++ b/xmlrpc++/XmlRpcMutex.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcServer.cpp b/xmlrpc++/XmlRpcServer.cpp
new file mode 100644
index 0000000..a32604f
--- /dev/null
+++ b/xmlrpc++/XmlRpcServer.cpp
@@ -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;
+}
+
diff --git a/xmlrpc++/XmlRpcServer.h b/xmlrpc++/XmlRpcServer.h
new file mode 100644
index 0000000..28b34a5
--- /dev/null
+++ b/xmlrpc++/XmlRpcServer.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcServerConnection.cpp b/xmlrpc++/XmlRpcServerConnection.cpp
new file mode 100644
index 0000000..9ff3834
--- /dev/null
+++ b/xmlrpc++/XmlRpcServerConnection.cpp
@@ -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, _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, _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, _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);
+}
+
diff --git a/xmlrpc++/XmlRpcServerConnection.h b/xmlrpc++/XmlRpcServerConnection.h
new file mode 100644
index 0000000..31c31d0
--- /dev/null
+++ b/xmlrpc++/XmlRpcServerConnection.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcServerMethod.cpp b/xmlrpc++/XmlRpcServerMethod.cpp
new file mode 100644
index 0000000..1616ff4
--- /dev/null
+++ b/xmlrpc++/XmlRpcServerMethod.cpp
@@ -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
diff --git a/xmlrpc++/XmlRpcServerMethod.h b/xmlrpc++/XmlRpcServerMethod.h
new file mode 100644
index 0000000..738a9c8
--- /dev/null
+++ b/xmlrpc++/XmlRpcServerMethod.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcSocket.cpp b/xmlrpc++/XmlRpcSocket.cpp
new file mode 100644
index 0000000..ed29b1a
--- /dev/null
+++ b/xmlrpc++/XmlRpcSocket.cpp
@@ -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);
+}
+
+
diff --git a/xmlrpc++/XmlRpcSocket.h b/xmlrpc++/XmlRpcSocket.h
new file mode 100644
index 0000000..115d3e0
--- /dev/null
+++ b/xmlrpc++/XmlRpcSocket.h
@@ -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
diff --git a/xmlrpc++/XmlRpcSource.cpp b/xmlrpc++/XmlRpcSource.cpp
new file mode 100644
index 0000000..31ab771
--- /dev/null
+++ b/xmlrpc++/XmlRpcSource.cpp
@@ -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
diff --git a/xmlrpc++/XmlRpcSource.h b/xmlrpc++/XmlRpcSource.h
new file mode 100644
index 0000000..2843241
--- /dev/null
+++ b/xmlrpc++/XmlRpcSource.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcThread.cpp b/xmlrpc++/XmlRpcThread.cpp
new file mode 100644
index 0000000..8a91d87
--- /dev/null
+++ b/xmlrpc++/XmlRpcThread.cpp
@@ -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
+
+
diff --git a/xmlrpc++/XmlRpcThread.h b/xmlrpc++/XmlRpcThread.h
new file mode 100644
index 0000000..6f4ec78
--- /dev/null
+++ b/xmlrpc++/XmlRpcThread.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcThreadedServer.cpp b/xmlrpc++/XmlRpcThreadedServer.cpp
new file mode 100644
index 0000000..3f40e49
--- /dev/null
+++ b/xmlrpc++/XmlRpcThreadedServer.cpp
@@ -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
diff --git a/xmlrpc++/XmlRpcThreadedServer.h b/xmlrpc++/XmlRpcThreadedServer.h
new file mode 100644
index 0000000..bedbeec
--- /dev/null
+++ b/xmlrpc++/XmlRpcThreadedServer.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcUtil.cpp b/xmlrpc++/XmlRpcUtil.cpp
new file mode 100644
index 0000000..a084814
--- /dev/null
+++ b/xmlrpc++/XmlRpcUtil.cpp
@@ -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;
+}
+
+
+
diff --git a/xmlrpc++/XmlRpcUtil.h b/xmlrpc++/XmlRpcUtil.h
new file mode 100644
index 0000000..8128f72
--- /dev/null
+++ b/xmlrpc++/XmlRpcUtil.h
@@ -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_
diff --git a/xmlrpc++/XmlRpcValue.cpp b/xmlrpc++/XmlRpcValue.cpp
new file mode 100644
index 0000000..69627a4
--- /dev/null
+++ b/xmlrpc++/XmlRpcValue.cpp
@@ -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);
+}
+
diff --git a/xmlrpc++/XmlRpcValue.h b/xmlrpc++/XmlRpcValue.h
new file mode 100644
index 0000000..b490ae4
--- /dev/null
+++ b/xmlrpc++/XmlRpcValue.h
@@ -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_
diff --git a/xmlrpc++/base64.h b/xmlrpc++/base64.h
new file mode 100644
index 0000000..519ee0f
--- /dev/null
+++ b/xmlrpc++/base64.h
@@ -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