From 922cb5559b9f2f97279fa24cc9c5862c8b666495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 8 Mar 2005 10:23:43 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r2603, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/iem/iemxmlrpc/; revision=2604 --- LICENSE.txt | 504 +++++++++++++++ Makefile | 100 +++ README.txt | 29 + main.cpp | 805 ++++++++++++++++++++++++ xmlrpc++/COPYING | 504 +++++++++++++++ xmlrpc++/Makefile | 39 ++ xmlrpc++/Makefile.win | 189 ++++++ xmlrpc++/README.html | 109 ++++ xmlrpc++/src/Doxyfile | 1041 +++++++++++++++++++++++++++++++ xmlrpc++/src/XmlRpc.h | 94 +++ xmlrpc++/src/XmlRpcClient.cpp | 413 ++++++++++++ xmlrpc++/src/XmlRpcClient.h | 125 ++++ xmlrpc++/src/XmlRpcDispatch.cpp | 209 +++++++ xmlrpc++/src/XmlRpcDispatch.h | 88 +++ xmlrpc++/src/XmlRpcException.h | 42 ++ xmlrpc++/src/XmlRpcServer.cpp | 284 +++++++++ xmlrpc++/src/XmlRpcServer.h | 104 +++ xmlrpc++/src/XmlRpcServerConnection.cpp | 371 +++++++++++ xmlrpc++/src/XmlRpcServerConnection.h | 102 +++ xmlrpc++/src/XmlRpcServerMethod.cpp | 21 + xmlrpc++/src/XmlRpcServerMethod.h | 47 ++ xmlrpc++/src/XmlRpcSocket.cpp | 260 ++++++++ xmlrpc++/src/XmlRpcSocket.h | 69 ++ xmlrpc++/src/XmlRpcSource.cpp | 35 ++ xmlrpc++/src/XmlRpcSource.h | 55 ++ xmlrpc++/src/XmlRpcUtil.cpp | 250 ++++++++ xmlrpc++/src/XmlRpcUtil.h | 61 ++ xmlrpc++/src/XmlRpcValue.cpp | 611 ++++++++++++++++++ xmlrpc++/src/XmlRpcValue.h | 189 ++++++ xmlrpc++/src/base64.h | 379 +++++++++++ xmlrpc-init.pd | 12 + xmlrpc-test.pd | 31 + xmlrpc-test.py | 75 +++ xmlrpc.dsp | 96 +++ xmlrpc.dsw | 29 + xmlrpc.vcproj | 177 ++++++ 36 files changed, 7549 insertions(+) create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.txt create mode 100644 main.cpp create mode 100644 xmlrpc++/COPYING create mode 100644 xmlrpc++/Makefile create mode 100644 xmlrpc++/Makefile.win create mode 100644 xmlrpc++/README.html create mode 100644 xmlrpc++/src/Doxyfile create mode 100644 xmlrpc++/src/XmlRpc.h create mode 100644 xmlrpc++/src/XmlRpcClient.cpp create mode 100644 xmlrpc++/src/XmlRpcClient.h create mode 100644 xmlrpc++/src/XmlRpcDispatch.cpp create mode 100644 xmlrpc++/src/XmlRpcDispatch.h create mode 100644 xmlrpc++/src/XmlRpcException.h create mode 100644 xmlrpc++/src/XmlRpcServer.cpp create mode 100644 xmlrpc++/src/XmlRpcServer.h create mode 100644 xmlrpc++/src/XmlRpcServerConnection.cpp create mode 100644 xmlrpc++/src/XmlRpcServerConnection.h create mode 100644 xmlrpc++/src/XmlRpcServerMethod.cpp create mode 100644 xmlrpc++/src/XmlRpcServerMethod.h create mode 100644 xmlrpc++/src/XmlRpcSocket.cpp create mode 100644 xmlrpc++/src/XmlRpcSocket.h create mode 100644 xmlrpc++/src/XmlRpcSource.cpp create mode 100644 xmlrpc++/src/XmlRpcSource.h create mode 100644 xmlrpc++/src/XmlRpcUtil.cpp create mode 100644 xmlrpc++/src/XmlRpcUtil.h create mode 100644 xmlrpc++/src/XmlRpcValue.cpp create mode 100644 xmlrpc++/src/XmlRpcValue.h create mode 100644 xmlrpc++/src/base64.h create mode 100644 xmlrpc-init.pd create mode 100644 xmlrpc-test.pd create mode 100644 xmlrpc-test.py create mode 100644 xmlrpc.dsp create mode 100644 xmlrpc.dsw create mode 100644 xmlrpc.vcproj diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4143a6f --- /dev/null +++ b/Makefile @@ -0,0 +1,100 @@ +# xmlrpc - XMLRPC external for PD +# +# Makefile for gcc @ linux +# +# usage: +# to build run "make -f Makefile.linux" +# to install (as root), do "make -f Makefile.linux install" +# +#* Author: Thomas Grill t.grill [at] gmx.net +# +# License LGPL see LICENSE.txt +# IEM - Institute of Electronic Music and Acoustics, Graz +# Inffeldgasse 10/3, 8010 Graz, Austria +# http://iem.at +#************************************************************/ + + +# Configuration: ########################## + + +# where are the PD header files? +# leave it blank if it is a system directory (like /usr/local/include), +# Ѓsince gcc 3.2 complains about it +PDPATH= + +# where should flext libraries be built? +TARGDIR=./pd-linux + +# where are the XMLRPC++ header files? +XMLRPCPATH=xmlrpc++ +XMLRPCINC=$(XMLRPCPATH)/src/ +XMLRPCLIB=libXmlRpc.a + +# where should the external be installed? +# (leave blank to omit installation) +INSTPATH=/usr/local/lib/pd/extra + +# additional compiler flags +# (check if they fit for your system!) +# UFLAGS=-mcpu=pentiumpro # gcc 2.95 +UFLAGS=-mcpu=pentium3 -msse # gcc 3.2 + +########################################### + +# compiler+linker stuff +INCLUDES=$(PDPATH) $(XMLRPCINC) +LIBPATH= ${XMLRPCPATH} + +FLAGS=-DPD ${U_FLAGS} +CFLAGS=-O6 +#CFLAGS=-g +LIBS=m util XmlRpc + +# --------------------------------------------- +# the rest can stay untouched +# ---------------------------------------------- +NAME=iemxmlrpc + +# all the source files from the package +DIR=. +SRCS=main.cpp + +MAKEFILE=Makefile + +TARGET=$(TARGDIR)/$(NAME).pd_linux + +# default target +all: $(XMLRPCPATH)/$(XMLRPCLIB) $(TARGDIR) $(TARGET) + +$(XMLRPCPATH)/$(XMLRPCLIB): $(XMLRPCPATH) + make -C $(XMLRPCPATH) + +$(patsubst %,$(DIR)/%,$(SRCS)): $(patsubst %,$(DIR)/%,$(HDRS)) $(MAKEFILE) + touch $@ + +$(TARGDIR): + mkdir -p $(TARGDIR) + +$(TARGDIR)/%.o : $(DIR)/%.cpp + $(CXX) -c $(CFLAGS) $(FLAGS) $(patsubst %,-I%,$(INCLUDES)) $< -o $@ + +$(TARGET) : $(patsubst %.cpp,$(TARGDIR)/%.o,$(SRCS)) + $(CXX) -shared $^ $(patsubst %,-L%,$(LIBPATH)) $(patsubst %,-l%,$(LIBS)) -o $@ + chmod 755 $@ + +$(INSTPATH): + mkdir $(INSTPATH) + +install:: $(INSTPATH) + +install:: $(TARGET) + cp $^ $(INSTPATH) + chown root.root $(patsubst %,$(INSTPATH)/%,$(notdir $^)) + chmod 755 $(patsubst %,$(INSTPATH)/%,$(notdir $^)) + +.PHONY: clean +clean: + make -C $(XMLRPCPATH) clean + rm -f $(TARGDIR)/*.o $(TARGET) + -rm -f *~ diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..f01cfc9 --- /dev/null +++ b/README.txt @@ -0,0 +1,29 @@ +XMLRPC external for PD -- Version 0.1.3b + +Author: Thomas Grill t.grill [at] gmx.net +Maintainer: w.ritsch (for this release) + + +Docu: + + For Howto look at pd helppatches and pythonexample. + +Requirements: + + I included xmlprc++ as library to prevent additional dependencies + also this lib looks simple and clean not much to improve (w.ritsch) + + +Licence: + + LGPL see LICENSE.txt + IEM - Institute of Electronic Music and Acoustics, Graz + Inffeldgasse 10/3, 8010 Graz, Austria + http://iem.at + +CHANGES: + + V 0.1.3b, LICENSES added, post error removed. + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2cc0a26 --- /dev/null +++ b/main.cpp @@ -0,0 +1,805 @@ +/************************************************************* + * + * XMLRPC external for PD + * + * File: main.cpp + * + * Description: This is it all + * + * Author: Thomas Grill t.grill [at] gmx.net + * +# License LGPL see LICENSE.txt +# IEM - Institute of Electronic Music and Acoustics, Graz +# Inffeldgasse 10/3, 8010 Graz, Austria +# http://iem.at +# +# CHANGES: +# V 0.1.3b, LICENSES added, post error removed. +# +#************************************************************/ + +//! Version number of this external +#define __XMLRPC_VERSION "0.1.3b" + +// prevent MSVC "extern" warning +#ifdef _MSC_VER +#pragma warning( disable : 4091 ) +#endif + +// necessary for a Sleep command +#ifdef WIN32 +#include +#endif + +// headers contained in the PD distribution +#include +#include + +// we are using the xmlrpc++ project (see http://sf.net/projects/xmlrpcpp ) +#include + +// include STL map class +#include + + +#ifdef PD_DEVEL_VERSION + // here, the sys_lock and sys_unlock functions are defined + #undef USECLOCK +#else + #define USECLOCK +#endif + + +//! Convert XMLRPC values to PD atoms +static bool Val2Atom(XmlRpc::XmlRpcValue &val,t_atom &atom) +{ + switch(val.getType()) { + case XmlRpc::XmlRpcValue::TypeBoolean: SETFLOAT(&atom,(int)val?0:1); break; + case XmlRpc::XmlRpcValue::TypeInt: SETFLOAT(&atom,(int)val); break; + case XmlRpc::XmlRpcValue::TypeDouble: SETFLOAT(&atom,(double)val); break; + case XmlRpc::XmlRpcValue::TypeString: SETSYMBOL(&atom,gensym((char *)((std::string &)val).c_str())); break; + default: + return false; + } + return true; +} + +//! Convert PD atoms to XMLRPC values +static bool Atom2Val(const t_atom &atom,XmlRpc::XmlRpcValue &val) +{ + switch(atom.a_type) { + case A_FLOAT: val = (double)atom_getfloat((t_atom *)&atom); break; + case A_SYMBOL: val = (std::string)(atom_getsymbol((t_atom *)&atom)->s_name); + default: return false; + } + return true; +} + + + +//! proxy class holder +static t_class *px_class = NULL; + + +/*! \brief This is the proxy class receiving messages to a symbol + \note No virtual table here! +*/ +class bind_proxy { +public: + //! Obligatory PD object structure + t_object obj; + + //! this symbol + const t_symbol *sym; + + //! mutex for value changes + pthread_mutex_t *mutex; + + //! current value of bound sender + XmlRpc::XmlRpcValue value; + + //! Initalize structure + void Init(const t_symbol *s,pthread_mutex_t *mtx) + { + sym = s; mutex = mtx; + value.clear(); + } + + /*! \brief PD method called when a bound symbol changes its value + \note This is called from PD system thread + */ + static void px_method(bind_proxy *obj,const t_symbol *s,int argc,const t_atom *argv); +}; + + +void bind_proxy::px_method(bind_proxy *obj,const t_symbol *s,int argc,const t_atom *argv) +{ + pthread_mutex_lock(obj->mutex); + + obj->value.clear(); + + if(s == &s_float && argc == 1 && argv->a_type == A_FLOAT) + // float value + obj->value = (double)atom_getfloat((t_atom *)argv); + else if(argc == 0) + // symbol value + obj->value = (std::string)s->s_name; + else if(s == &s_symbol && argc == 1 && argv->a_type == A_SYMBOL) + // this is probably never called + obj->value = (std::string)atom_getsymbol((t_atom *)argv)->s_name; + else { + // anything or list + + // look if header is "list" and don't propagate it to the atom list + int hdr = s == &s_list?0:1; + + // set the parameters + obj->value.setSize(argc+hdr); + + if(hdr) obj->value[0] = s->s_name; + + for(int i = 0; i < argc; ++i) { + if(!Atom2Val(argv[i],obj->value[i+hdr])) +#ifdef _DEBUG + error("XMLRPC: Internal error - version %s, file %s, line %i",__XMLRPC_VERSION,__FILE__,__LINE__); +#else + {} +#endif + } + } + + pthread_mutex_unlock(obj->mutex); +} + + +//! Type to map a symbol name to the associated proxy class +typedef std::map ProxyMapper; + + +//! Our XMLRPC server +class RPCServer: + public XmlRpc::XmlRpcServer +{ +public: + RPCServer(int port); + ~RPCServer(); + + void Work() { work(-1.0); } + + class Message + { + public: + enum Msg { load,close,send,bind,unbind,query }; + + Message(Msg m,XmlRpc::XmlRpcValue &val,XmlRpc::XmlRpcValue &res,pthread_mutex_t &mtx): + msg(m),value(val),result(res),mutex(mtx) + { +#ifdef USECLOCK + pthread_cond_init(&cond,NULL); +#endif + } + + ~Message() + { +#ifdef USECLOCK + pthread_cond_destroy(&cond); +#endif + } + +#ifdef USECLOCK + void Signal() + { + pthread_cond_signal(&cond); + } + + void Wait() + { + pthread_mutex_lock(&mutex); + pthread_cond_wait(&cond,&mutex); + pthread_mutex_unlock(&mutex); + } + + pthread_cond_t cond; +#endif + pthread_mutex_t &mutex; + + std::string err; + Msg msg; + XmlRpc::XmlRpcValue &value,&result; + }; + + std::list msglist; + pthread_mutex_t qu_mutex,msg_mutex,px_mutex; + + //! Add message to queue (thread-safe) + void MsgPush(Message *m); + //! Fetch message from queue (thread-safe) + Message *MsgPop(); + + //! loaded PD patch + t_pd *canvas; + + //! bound symbols and their proxies + ProxyMapper objmap; + +#ifdef USECLOCK + /*! \brief trigger message worker + \remark thread-safe! + */ + void Trigger() { clock_delay(clk,0); } + + //! worker function + static void clk_worker(RPCServer *s) { s->MsgWorker(); } + + //! worker function + void MsgWorker(); + + //! worker clock + t_clock *clk; +#endif + + //! process message + void MsgProcess(Message &m); + + void Methods(Message::Msg msg,XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); + + void Method_Load(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); + void Method_Close(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); + void Method_Bind(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); + void Method_Unbind(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); + void Method_Send(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); + void Method_Query(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result); +}; + +RPCServer::RPCServer(int port): + canvas(NULL) +{ + // Create the server socket on the specified port + bindAndListen(port); + + // Enable introspection API + enableIntrospection(true); + + // make mutex for message queue + pthread_mutex_init(&qu_mutex,NULL); + pthread_mutex_init(&msg_mutex,NULL); + pthread_mutex_init(&px_mutex,NULL); + +#ifdef USECLOCK + // create worker clock + clk = clock_new(this,(t_method)clk_worker); +#endif +} + +RPCServer::~RPCServer() +{ + // delete message queue mutex + pthread_mutex_destroy(&qu_mutex); + pthread_mutex_destroy(&msg_mutex); + pthread_mutex_destroy(&px_mutex); + +#ifdef USECLOCK + clock_free(clk); +#endif +} + +void RPCServer::MsgPush(Message *m) +{ + pthread_mutex_lock(&qu_mutex); + msglist.push_back(m); + pthread_mutex_unlock(&qu_mutex); +} + +RPCServer::Message *RPCServer::MsgPop() +{ + Message *m = NULL; + + pthread_mutex_lock(&qu_mutex); + if(!msglist.empty()) { + // fetch reference from queue + m = msglist.front(); + msglist.pop_front(); + } + pthread_mutex_unlock(&qu_mutex); + + return m; +} + +//! process message +void RPCServer::MsgProcess(Message &m) +{ + try { + // choose function + switch(m.msg) { + case Message::load: Method_Load(m.value,m.result); break; + case Message::close: Method_Close(m.value,m.result); break; + case Message::bind: Method_Bind(m.value,m.result); break; + case Message::unbind: Method_Unbind(m.value,m.result); break; + case Message::send: Method_Send(m.value,m.result); break; + } + } + + // catch all exceptions as they can't propagate across threads + catch(const char *txt) { + m.err = txt; + } + catch(const std::string &txt) { + m.err = txt; + } + catch(const XmlRpc::XmlRpcException &exc) { + m.err = std::string("XMLRPC++ error: ")+exc.getMessage(); + } + catch(...) { + m.err = "Undefined method error"; + } +} + +#ifdef USECLOCK +//! worker function +void RPCServer::MsgWorker() +{ + for(;;) { + // fetch reference from queue + Message *m = MsgPop(); + if(!m) break; + + MsgProcess(*m); + + // signal message to be finished + m->Signal(); + } +} +#endif + + +//! dispatcher function +void RPCServer::Methods(Message::Msg msg,XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + switch(msg) { + // these can execute in the XMLRPC thread + case Message::query: + Method_Query(params,result); break; + + // all others need to be executed in the PD thread + default: { + // make new message + Message *m = new Message(msg,params,result,msg_mutex); + +#ifdef USECLOCK + // add to message queue + MsgPush(m); + + // trigger PD clock + Trigger(); + + // wait for function to finish in PD system thread + m->Wait(); +#else + // lock PD + sys_lock(); + + // process message + MsgProcess(*m); + + // unlock PD + sys_unlock(); +#endif + + // save eventual error string + std::string err = m->err; + + // delete message + delete m; + + // if error string is non-null throw exception + if(err.length()) throw err; + } + } +} + + +/*! \brief "load" method + \param string-array XML data representing a PD patch + + This method takes a string array containing the code of a PD patch to load. +*/ +void RPCServer::Method_Load(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + XmlRpc::XmlRpcValue *parr; + if(params.getType() == XmlRpc::XmlRpcValue::TypeArray && params[0].getType() == XmlRpc::XmlRpcValue::TypeString) + parr = ¶ms; + else if(params.size() == 1 && params[0].getType() == XmlRpc::XmlRpcValue::TypeArray) + parr = ¶ms[0]; + else + throw "Wrong syntax - should be 'load string-array'"; + +#ifndef _DEBUG + // stop dsp while the numerous objects are created + int dspstate = canvas_suspend_dsp(); +#endif + + // if there is already a canvas... trash it! + if(canvas) pd_free(canvas); + + // a current directory must be set so that PD creates an environment for the new canvas + // see pd/src/g_canvas.c - function canvas_new "if (canvas_newdirectory->s_name[0])" + // if there would be no canvas environment PD would crash + glob_setfilename(NULL,gensym("IAEM"),gensym(".")); + + for(int i = 0; i < parr->size(); ++i) { + // here, an exception can happen if (*parr)[i] is not a string! + const std::string &str = (std::string &)(*parr)[i]; + + // allocate binbuf storage + t_binbuf *b = binbuf_new(); + + // create binbuf from the string + binbuf_text(b, (char *)str.c_str(), str.size()); + // evaluate binbuf - create canvas, objects or whatever + binbuf_eval(b, 0, 0, 0); + + // free storage + binbuf_free(b); + + // here, it would be interesting if the object has been sucessfully created... + // we could use the following... +#if (PD_MINOR_VERSION >= 37) || defined(PD_DEVEL_VERSION) + // This pd_newest is currently only defined with PD veriosn >= 0.37 or in the + // CVS devel_0_36 branch on http://sf.net/projects/pure-data + // It is needed for knowing whether an object could be created or not + // However, it doesn't work for messages or comments for which pd_newest is not reset... + // Since the first statement is object a canvas, which (on success) sets pd_newest + // to non-null this should not be a problem for following msgs or comments. + t_pd *x = pd_newest(); + if(!x) throw "Could not create object"; +#endif + } + + // clear current file and path + // this is done because it is done in g_canvas.c + glob_setfilename(NULL,&s_,&s_); + + // now resume dsp +#ifndef _DEBUG + canvas_resume_dsp(dspstate); +#endif + + // get the patch canvas + t_pd *x = s__X.s_thing; + if(x) { + // pop newly created canvas + pd_vmess(x, gensym("pop"), "i", 1); + + // issue load bang + pd_vmess(x, gensym("loadbang"), ""); + + // save created canvas object + canvas = x; + } + else + throw "Canvas could not be created"; +} + +/*! \brief "close" method + + The previously loaded PD patch (by "load method") is closed +*/ +void RPCServer::Method_Close(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + if(params.valid() && params.size()) + throw "Wrong syntax - method 'close' takes no parameters"; + + if(canvas) { + pd_free(canvas); + canvas = NULL; + } + else + throw "No open canvas"; +} + +/*! \brief "bind" method + \parameter symbol PD symbol to bind a callback to + \return Fault if symbol has already been bound + + Registers a symbol for query or active feedback +*/ +void RPCServer::Method_Bind(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + if(!params.valid() || params.size() != 1) + throw "Wrong syntax - should be 'bind symbol'"; + + // check type of params[0] before evaluating the rest + // this throws an exception if it's not a string + std::string &name = (std::string &)params[0]; + + // look for existing proxy object + ProxyMapper::iterator iter = objmap.find(name); + + if(iter == objmap.end()) { + // new proxy object + bind_proxy *px = (bind_proxy *)pd_new(px_class); + px->Init(gensym((char *)name.c_str()),&px_mutex); + + // bind symbol to object + pd_bind(&px->obj.ob_pd,(t_symbol *)px->sym); + + // add to map + objmap[name] = px; + } + else + throw "The symbol is already bound"; +} + +/*! \brief "unbind" method + \parameter symbol Previously bound PD symbol + \return Fault if symbol is not found or has not been bound + + Free a previously bound symbol from its callback +*/ +void RPCServer::Method_Unbind(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + if(!params.valid() || params.size() != 1) + throw "Wrong syntax - should be 'unbind symbol'"; + + // check type of params[0] before evaluating the rest + // this throws an exception if it's not a string + std::string &name = (std::string &)params[0]; + + // look for existing proxy object + ProxyMapper::iterator iter = objmap.find(name); + + if(iter != objmap.end()) { + // retrieve pointer to proxy from map + bind_proxy *px = iter->second; + + // unbind and free proxy object + pd_unbind(&px->obj.ob_pd,gensym((char *)name.c_str())); + pd_free((t_pd *)&px->obj); + + // remove from map + objmap.erase(iter); + } + else + throw "The symbol is not bound"; +} + +/*! \brief "send" method + \param symbol PD symbol to send data to + \param parameters Data values (translated to PD atoms) + + Send data to a receiving symbol in PD +*/ +void RPCServer::Method_Send(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + if(!params.valid() || params.size() < 2) + throw "Wrong syntax - should be 'send symbol parameters ...'"; + + // check type of params[0] before evaluating the rest + // this throws an exception if it's not a string + std::string &recv = (std::string &)params[0]; + + int offset = 1; + XmlRpc::XmlRpcValue *val = ¶ms; + + // parameter 1 can also be an array (but no other parameters may follow!) + if(params.size() == 2 && params[1].getType() == XmlRpc::XmlRpcValue::TypeArray) { + offset = 0; + val = ¶ms[1]; + } + + t_symbol *sym = gensym((char *)recv.c_str()); + int argc = val->size()-offset; + t_atom *args = new t_atom[argc]; + + // iterate through argument list + for(int i = offset; i < val->size(); ++i) { + if(!Val2Atom((*val)[i],args[i-offset])) + throw "Parameter type not handled"; + } + +#ifdef WIN32 + // this is not very nice but I have no clue why sometimes this + // function hangs under windows. seems to be related to the + // pd function called and some mutex/thread issues + // remove this windows hack if it reveals why it blocks + Sleep(3); +#endif + + // forward parameters to receiving symbol + if(sym && sym->s_thing) + pd_forwardmess(sym->s_thing,argc,args); + else + post("XMLRPC: receiver %s not found",recv.c_str()); + + delete[] args; +} + +/*! \brief "query" method + \parameter symbol PD symbol to query + \return Value of sender symbol + + Queries the current value of a PD symbol +*/ +void RPCServer::Method_Query(XmlRpc::XmlRpcValue ¶ms, XmlRpc::XmlRpcValue &result) +{ + if(!params.valid() || params.size() != 1) + throw "Wrong syntax - should be 'query symbol'"; + + // check type of params[0] before evaluating the rest + // this throws an exception if it's not a string + std::string &name = (std::string &)params[0]; + + // lock mutex to protect objmap + pthread_mutex_lock(&px_mutex); + + // look for existing proxy object + ProxyMapper::iterator iter = objmap.find(name); + + // initialize px to NULL (serves as a flag for error exception) + bind_proxy *px = NULL; + + if(iter != objmap.end()) { + // retrieve pointer to proxy from map + px = iter->second; + + // return current value stored in proxy + result = px->value; + } + + pthread_mutex_unlock(&px_mutex); + + // throw exception if px has not been set + if(!px) throw "Symbol not found"; +} + + +//! Base class for all our XMLRPC methods +class RPCMethod: + public XmlRpc::XmlRpcServerMethod +{ +public: + RPCMethod(const char *name,RPCServer *s,RPCServer::Message::Msg msg,const char *help): + XmlRpc::XmlRpcServerMethod(name,s), + srvmsg(msg),helptext(help) + {} + +protected: + //! the actual worker function where all is done + virtual void worker(XmlRpc::XmlRpcValue& params, XmlRpc::XmlRpcValue& result) + { + getServer().Methods(srvmsg,params,result); + } + + //! return help string + virtual std::string help() { return helptext; } + + //! execute RPC method and handle exceptions + void execute(XmlRpc::XmlRpcValue& params, XmlRpc::XmlRpcValue& result) + { + try { worker(params,result); } + + // the exceptions are mostly typed as strings + catch(const char *str) { + throw XmlRpc::XmlRpcException(str); + } + catch(const std::string &str) { + throw XmlRpc::XmlRpcException(str); + } + catch(const XmlRpc::XmlRpcException &exc) { + throw exc; + } + + // treat others here generically and protect PD from crashing + catch(...) { + throw XmlRpc::XmlRpcException("Undefined error, please report"); + } + } + + //! get reference to RPC server + RPCServer &getServer() { return *(RPCServer *)_server; } + + std::string helptext; + const RPCServer::Message::Msg srvmsg; +}; + + + +//! our XMLRPC server +static RPCServer *xmlrpc_server = NULL; + + +//! Thread function where our XMLRPC server runs +static void *xmlrpc_worker(void *) +{ +#ifdef _DEBUG + // In debug mode display some messages + XmlRpc::setVerbosity(1); + + if(!xmlrpc_server) + error("XMLRPC: Internal error - version %s, file %s, line %i",__XMLRPC_VERSION,__FILE__,__LINE__); + else +#endif + { + // Register all methods + RPCMethod meth_load("load",xmlrpc_server,RPCServer::Message::load,"Load a patch - Syntax: 'load string-array'"); + RPCMethod meth_close("close",xmlrpc_server,RPCServer::Message::close,"Close the open patch"); + RPCMethod meth_send("send",xmlrpc_server,RPCServer::Message::send,"Send a value to a symbol - Syntax:'send symbol parameters ...'"); + RPCMethod meth_bind("bind",xmlrpc_server,RPCServer::Message::bind,"Bind a callback to a PD symbol - Syntax:'bind symbol'"); + RPCMethod meth_unbind("unbind",xmlrpc_server,RPCServer::Message::unbind,"Unbind a callback from a PD symbol - Syntax:'unbind symbol'"); + RPCMethod meth_query("query",xmlrpc_server,RPCServer::Message::query,"Query the value of PD symbol - Syntax:'query symbol'"); + + // Wait for requests indefinitely + xmlrpc_server->Work(); + } + + // This point is never reached + delete xmlrpc_server; xmlrpc_server = NULL; + + return NULL; +} + + +//! xmlrpc class holder +static t_class *xmlrpc_class = NULL; + + +/*! \brief xmlrpc object creation function + + This function launches the xmlrpc thread +*/ +static t_object *xmlrpc_new(t_floatarg p) +{ + t_object *ret = NULL; + + if(xmlrpc_server) + post("XMLRPC: server has already been launched"); + else { + if(p <= 0) + post("XMLRPC: port must be > 0"); + else { + int port = (int)p; + + // Initialize server + xmlrpc_server = new RPCServer(port); + + // launch worker function + pthread_t id; + if(pthread_create(&id,NULL,xmlrpc_worker,xmlrpc_server)) { + error("XMLRPC thread could not be launched"); + + delete xmlrpc_server; xmlrpc_server = NULL; + } + else { + // success -> create object + ret = (t_object *)pd_new(xmlrpc_class); + + post("XMLRPC: server is listening at port %i",port); + } + } + } + return ret; +} + + +/*! \brief Library setup routine + \note Must be exported from shared library +*/ +extern "C" +#ifdef NT +__declspec(dllexport) +#endif +void xmlrpc_setup() +{ + // post some message to the console + post("IEMXMLRPC, version " __XMLRPC_VERSION ", (C)2003 IEM Graz"); + post(""); + + // register the proxy class for binding symbols + px_class = class_new(gensym("xmlrpc proxy"),NULL,NULL,sizeof(bind_proxy),CLASS_PD|CLASS_NOINLET, A_NULL); + // one method for all messages to proxy + class_addanything(px_class,bind_proxy::px_method); + + // register the xmlrpc class + xmlrpc_class = class_new(gensym("xmlrpc"),(t_newmethod)xmlrpc_new,NULL,sizeof(t_object),CLASS_NOINLET, A_FLOAT,A_NULL); +} + diff --git a/xmlrpc++/COPYING b/xmlrpc++/COPYING new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/xmlrpc++/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/xmlrpc++/Makefile b/xmlrpc++/Makefile new file mode 100644 index 0000000..1c67616 --- /dev/null +++ b/xmlrpc++/Makefile @@ -0,0 +1,39 @@ +# modified by Christopher Frauenberger [frauenberger@iem.at] +# 25.8.2004 +# for the integration in iARS project + +# makefile written for gnu make +CXX = g++ +SRC = ./src +CPPFLAGS = -I$(SRC) +DEBUG = -g +OPTIMIZE = -O2 +GCCWARN = -Wall -Wstrict-prototypes +CXXFLAGS = $(DEBUG) $(GCCWARN) $(OPTIMIZE) $(INCLUDES) + +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 = $(SRC)/XmlRpcClient.o $(SRC)/XmlRpcDispatch.o \ + $(SRC)/XmlRpcServer.o $(SRC)/XmlRpcServerConnection.o \ + $(SRC)/XmlRpcServerMethod.o $(SRC)/XmlRpcSocket.o $(SRC)/XmlRpcSource.o \ + $(SRC)/XmlRpcUtil.o $(SRC)/XmlRpcValue.o + +all: $(LIB) + +$(LIB): $(OBJ) + $(AR) $(ARFLAGS) $(LIB) $(OBJ) + + +clean: + rm -f $(SRC)/*.o + rm -f $(SRC)/*~ + rm -f $(LIB) + diff --git a/xmlrpc++/Makefile.win b/xmlrpc++/Makefile.win new file mode 100644 index 0000000..0b98398 --- /dev/null +++ b/xmlrpc++/Makefile.win @@ -0,0 +1,189 @@ +# +# +# Makefile.win +# +# Microsoft Developer Studio Generated NMAKE File, Based on xmlrpc.dsp +# created 30.8.2004 +# modified by Christopher Frauenberger [frauenberger@iem.at]# +# +# + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +CPP=cl.exe +RSC=rc.exe + +OUTDIR=.\obj +INTDIR=.\obj + +ALL : ".\xmlrpc.lib" "$(OUTDIR)\xmlrpc.bsc" + + +CLEAN : + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\XmlRpcClient.obj" + -@erase "$(INTDIR)\XmlRpcClient.sbr" + -@erase "$(INTDIR)\XmlRpcDispatch.obj" + -@erase "$(INTDIR)\XmlRpcDispatch.sbr" + -@erase "$(INTDIR)\XmlRpcServer.obj" + -@erase "$(INTDIR)\XmlRpcServer.sbr" + -@erase "$(INTDIR)\XmlRpcServerConnection.obj" + -@erase "$(INTDIR)\XmlRpcServerConnection.sbr" + -@erase "$(INTDIR)\XmlRpcServerMethod.obj" + -@erase "$(INTDIR)\XmlRpcServerMethod.sbr" + -@erase "$(INTDIR)\XmlRpcSocket.obj" + -@erase "$(INTDIR)\XmlRpcSocket.sbr" + -@erase "$(INTDIR)\XmlRpcSource.obj" + -@erase "$(INTDIR)\XmlRpcSource.sbr" + -@erase "$(INTDIR)\XmlRpcUtil.obj" + -@erase "$(INTDIR)\XmlRpcUtil.sbr" + -@erase "$(INTDIR)\XmlRpcValue.obj" + -@erase "$(INTDIR)\XmlRpcValue.sbr" + -@erase "$(OUTDIR)\xmlrpc.bsc" + -@erase ".\xmlrpc.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +MTL=midl.exe +CPP_PROJ=/nologo /MD /W3 /GX /Zd /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\xmlrpc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\xmlrpc.bsc" +BSC32_SBRS= \ + "$(INTDIR)\XmlRpcClient.sbr" \ + "$(INTDIR)\XmlRpcDispatch.sbr" \ + "$(INTDIR)\XmlRpcServer.sbr" \ + "$(INTDIR)\XmlRpcServerConnection.sbr" \ + "$(INTDIR)\XmlRpcServerMethod.sbr" \ + "$(INTDIR)\XmlRpcSocket.sbr" \ + "$(INTDIR)\XmlRpcSource.sbr" \ + "$(INTDIR)\XmlRpcUtil.sbr" \ + "$(INTDIR)\XmlRpcValue.sbr" + +"$(OUTDIR)\xmlrpc.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LIB32=link.exe -lib +LIB32_FLAGS=/nologo /out:"xmlrpc.lib" +LIB32_OBJS= \ + "$(INTDIR)\XmlRpcClient.obj" \ + "$(INTDIR)\XmlRpcDispatch.obj" \ + "$(INTDIR)\XmlRpcServer.obj" \ + "$(INTDIR)\XmlRpcServerConnection.obj" \ + "$(INTDIR)\XmlRpcServerMethod.obj" \ + "$(INTDIR)\XmlRpcSocket.obj" \ + "$(INTDIR)\XmlRpcSource.obj" \ + "$(INTDIR)\XmlRpcUtil.obj" \ + "$(INTDIR)\XmlRpcValue.obj" + +".\xmlrpc.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("xmlrpc.dep") +!INCLUDE "xmlrpc.dep" +!ELSE +!MESSAGE Warning: cannot find "xmlrpc.dep" +!ENDIF +!ENDIF + + +SOURCE=.\src\XmlRpcClient.cpp + +"$(INTDIR)\XmlRpcClient.obj" "$(INTDIR)\XmlRpcClient.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcDispatch.cpp + +"$(INTDIR)\XmlRpcDispatch.obj" "$(INTDIR)\XmlRpcDispatch.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcServer.cpp + +"$(INTDIR)\XmlRpcServer.obj" "$(INTDIR)\XmlRpcServer.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcServerConnection.cpp + +"$(INTDIR)\XmlRpcServerConnection.obj" "$(INTDIR)\XmlRpcServerConnection.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcServerMethod.cpp + +"$(INTDIR)\XmlRpcServerMethod.obj" "$(INTDIR)\XmlRpcServerMethod.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcSocket.cpp + +"$(INTDIR)\XmlRpcSocket.obj" "$(INTDIR)\XmlRpcSocket.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcSource.cpp + +"$(INTDIR)\XmlRpcSource.obj" "$(INTDIR)\XmlRpcSource.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcUtil.cpp + +"$(INTDIR)\XmlRpcUtil.obj" "$(INTDIR)\XmlRpcUtil.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +SOURCE=.\src\XmlRpcValue.cpp + +"$(INTDIR)\XmlRpcValue.obj" "$(INTDIR)\XmlRpcValue.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + + + + + + + diff --git a/xmlrpc++/README.html b/xmlrpc++/README.html new file mode 100644 index 0000000..fd8e7f8 --- /dev/null +++ b/xmlrpc++/README.html @@ -0,0 +1,109 @@ + + + + XmlRpc++ Library + + + + + + +

XmlRpc++ Library

+

This is version 0.7 of XmlRpc++, an implementation of the + XmlRpc protocol written in C++, based upon Shilad Sen's excellent + 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 STL patches.) +
  • +
  • + Free   This library is released under the + GNU 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 + 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 http://validator.xmlrpc.com + (if you have access to the internet and are not behind a firewall etc). +

+

Author

+

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 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 +

+ + diff --git a/xmlrpc++/src/Doxyfile b/xmlrpc++/src/Doxyfile new file mode 100644 index 0000000..2d8a346 --- /dev/null +++ b/xmlrpc++/src/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 , where +# is the value of the INPUT_FILTER tag, and 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++/src/XmlRpc.h b/xmlrpc++/src/XmlRpc.h new file mode 100644 index 0000000..9611af7 --- /dev/null +++ b/xmlrpc++/src/XmlRpc.h @@ -0,0 +1,94 @@ +#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 +// + +#if defined(_MSC_VER) +# pragma warning(disable:4786) // identifier was truncated in debug info +#endif + +#ifndef MAKEDEPEND +# include +#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++/src/XmlRpcClient.cpp b/xmlrpc++/src/XmlRpcClient.cpp new file mode 100644 index 0000000..e706d0a --- /dev/null +++ b/xmlrpc++/src/XmlRpcClient.cpp @@ -0,0 +1,413 @@ + +#include "XmlRpcClient.h" + +#include "XmlRpcSocket.h" +#include "XmlRpc.h" + +#include +#include + + +using namespace XmlRpc; + +// Static data +const char XmlRpcClient::REQUEST_BEGIN[] = + "\r\n" + ""; +const char XmlRpcClient::REQUEST_END_METHODNAME[] = "\r\n"; +const char XmlRpcClient::PARAMS_TAG[] = ""; +const char XmlRpcClient::PARAMS_ETAG[] = ""; +const char XmlRpcClient::PARAM_TAG[] = ""; +const char XmlRpcClient::PARAM_ETAG[] = ""; +const char XmlRpcClient::REQUEST_END[] = "\r\n"; +const char XmlRpcClient::METHODRESPONSE_TAG[] = ""; +const char XmlRpcClient::FAULT_TAG[] = ""; + + + +XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/) +{ + XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port); + + _host = host; + _port = port; + if (uri) + _uri = uri; + else + _uri = "/RPC2"; + _connectionState = NO_CONNECTION; + _executing = false; + _eof = false; + + // Default to keeping the connection open until an explicit close is done + setKeepOpen(); +} + + +XmlRpcClient::~XmlRpcClient() +{ +} + +// Close the owned fd +void +XmlRpcClient::close() +{ + XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd()); + _connectionState = NO_CONNECTION; + _disp.exit(); + _disp.removeSource(this); + XmlRpcSource::close(); +} + + +// Clear the referenced flag even if exceptions or errors occur. +struct ClearFlagOnExit { + ClearFlagOnExit(bool& flag) : _flag(flag) {} + ~ClearFlagOnExit() { _flag = false; } + bool& _flag; +}; + +// Execute the named procedure on the remote server. +// Params should be an array of the arguments for the method. +// Returns true if the request was sent and a result received (although the result +// might be a fault). +bool +XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result) +{ + XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState); + + // This is not a thread-safe operation, if you want to do multithreading, use separate + // clients for each thread. If you want to protect yourself from multiple threads + // accessing the same client, replace this code with a real mutex. + if (_executing) + return false; + + _executing = true; + ClearFlagOnExit cf(_executing); + + _sendAttempts = 0; + _isFault = false; + + if ( ! setupConnection()) + return false; + + if ( ! generateRequest(method, params)) + return false; + + result.clear(); + double msTime = -1.0; // Process until exit is called + _disp.work(msTime); + + if (_connectionState != IDLE || ! parseResponse(result)) + return false; + + XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method); + _response = ""; + return true; +} + +// XmlRpcSource interface implementation +// Handle server responses. Called by the event dispatcher during execute. +unsigned +XmlRpcClient::handleEvent(unsigned eventType) +{ + if (eventType == XmlRpcDispatch::Exception) + { + if (_connectionState == WRITE_REQUEST && _bytesWritten == 0) + XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).", + XmlRpcSocket::getErrorMsg().c_str()); + else + XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.", + _connectionState, XmlRpcSocket::getErrorMsg().c_str()); + return 0; + } + + if (_connectionState == WRITE_REQUEST) + if ( ! writeRequest()) return 0; + + if (_connectionState == READ_HEADER) + if ( ! readHeader()) return 0; + + if (_connectionState == READ_RESPONSE) + if ( ! readResponse()) return 0; + + // This should probably always ask for Exception events too + return (_connectionState == WRITE_REQUEST) + ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent; +} + + +// Create the socket connection to the server if necessary +bool +XmlRpcClient::setupConnection() +{ + // If an error occurred last time through, or if the server closed the connection, close our end + if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof) + close(); + + _eof = false; + if (_connectionState == NO_CONNECTION) + if (! doConnect()) + return false; + + // Prepare to write the request + _connectionState = WRITE_REQUEST; + _bytesWritten = 0; + + // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable) + _disp.removeSource(this); // Make sure nothing is left over + _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception); + + return true; +} + + +// Connect to the xmlrpc server +bool +XmlRpcClient::doConnect() +{ + int fd = XmlRpcSocket::socket(); + if (fd < 0) + { + XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd); + this->setfd(fd); + + // Don't block on connect/reads/writes + if ( ! XmlRpcSocket::setNonBlocking(fd)) + { + this->close(); + XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + if ( ! XmlRpcSocket::connect(fd, _host, _port)) + { + this->close(); + XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + return true; +} + +// Encode the request to call the specified method with the specified parameters into xml +bool +XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params) +{ + std::string body = REQUEST_BEGIN; + body += methodName; + body += REQUEST_END_METHODNAME; + + // If params is an array, each element is a separate parameter + if (params.valid()) { + body += PARAMS_TAG; + if (params.getType() == XmlRpcValue::TypeArray) + { + for (int i=0; igetfd(), _request, &_bytesWritten)) { + XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length()); + + // Wait for the result + if (_bytesWritten == int(_request.length())) { + _header = ""; + _response = ""; + _connectionState = READ_HEADER; + } + return true; +} + + +// Read the header from the response +bool +XmlRpcClient::readHeader() +{ + // Read available data + if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) || + (_eof && _header.length() == 0)) { + + // If we haven't read any data yet and this is a keep-alive connection, the server may + // have timed out, so we try one more time. + if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) { + XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection"); + XmlRpcSource::close(); + _connectionState = NO_CONNECTION; + _eof = false; + return setupConnection(); + } + + XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.", + XmlRpcSocket::getErrorMsg().c_str(), getfd()); + return false; + } + + XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length()); + + char *hp = (char*)_header.c_str(); // Start of header + char *ep = hp + _header.length(); // End of string + char *bp = 0; // Start of body + char *lp = 0; // Start of content-length value + + for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) { + if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0)) + lp = cp + 16; + else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0)) + bp = cp + 4; + else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0)) + bp = cp + 2; + } + + // If we haven't gotten the entire header yet, return (keep reading) + if (bp == 0) { + if (_eof) // EOF in the middle of a response is an error + { + XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header"); + return false; // Close the connection + } + + return true; // Keep reading + } + + // Decode content length + if (lp == 0) { + XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified"); + return false; // We could try to figure it out by parsing as we read, but for now... + } + + _contentLength = atoi(lp); + if (_contentLength <= 0) { + XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength); + return false; + } + + XmlRpcUtil::log(4, "client read content length: %d", _contentLength); + + // Otherwise copy non-header data to response buffer and set state to read response. + _response = bp; + _header = ""; // should parse out any interesting bits from the header (connection, etc)... + _connectionState = READ_RESPONSE; + return true; // Continue monitoring this source +} + + +bool +XmlRpcClient::readResponse() +{ + // If we dont have the entire response yet, read available data + if (int(_response.length()) < _contentLength) { + if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) { + XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + // If we haven't gotten the entire _response yet, return (keep reading) + if (int(_response.length()) < _contentLength) { + if (_eof) { + XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response"); + return false; + } + return true; + } + } + + // Otherwise, parse and return the result + XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length()); + XmlRpcUtil::log(5, "response:\n%s", _response.c_str()); + + _connectionState = IDLE; + + return false; // Stop monitoring this source (causes return from work) +} + + +// Convert the response xml into a result value +bool +XmlRpcClient::parseResponse(XmlRpcValue& result) +{ + // Parse response xml into result + int offset = 0; + if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) { + XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str()); + return false; + } + + // Expect either ... or ... + 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++/src/XmlRpcClient.h b/xmlrpc++/src/XmlRpcClient.h new file mode 100644 index 0000000..ecf5811 --- /dev/null +++ b/xmlrpc++/src/XmlRpcClient.h @@ -0,0 +1,125 @@ + +#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 +#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 + //! @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 + XmlRpcClient(const char* host, int port, const char* uri=0); + + //! 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; } + + + // 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; + + // 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++/src/XmlRpcDispatch.cpp b/xmlrpc++/src/XmlRpcDispatch.cpp new file mode 100644 index 0000000..3bbca40 --- /dev/null +++ b/xmlrpc++/src/XmlRpcDispatch.cpp @@ -0,0 +1,209 @@ + +#include "XmlRpcDispatch.h" +#include "XmlRpcSource.h" +#include "XmlRpcUtil.h" + +#include +#include + +#if defined(_WINDOWS) +# include + +# define USE_FTIME +# if defined(_MSC_VER) +# define timeb _timeb +# define ftime _ftime +# endif +#else +# include +#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 + _endTime = (timeout < 0.0) ? -1.0 : (getTime() + timeout); + _doClear = false; + _inWork = true; + + // Only work while there is something to monitor + while (_sources.size() > 0) { + + // 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; // Not used on windows + 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 (timeout < 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) + { + XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents); + _inWork = false; + return; + } + + // Process events + for (it=_sources.begin(); it != _sources.end(); ) + { + SourceList::iterator thisIt = it++; + XmlRpcSource* src = thisIt->getSource(); + int fd = src->getfd(); + unsigned newMask = (unsigned) -1; + if (fd <= maxFd) { + // If you select on multiple event types this could be ambiguous + if (FD_ISSET(fd, &inFd)) + newMask &= src->handleEvent(ReadableEvent); + if (FD_ISSET(fd, &outFd)) + newMask &= src->handleEvent(WritableEvent); + if (FD_ISSET(fd, &excFd)) + newMask &= src->handleEvent(Exception); + + if ( ! newMask) { + _sources.erase(thisIt); // Stop monitoring this one + if ( ! src->getKeepOpen()) + src->close(); + } else if (newMask != (unsigned) -1) { + thisIt->getMask() = newMask; + } + } + } + + // Check whether to clear all sources + if (_doClear) + { + SourceList closeList = _sources; + _sources.clear(); + for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) { + XmlRpcSource *src = it->getSource(); + src->close(); + } + + _doClear = false; + } + + // Check whether end time has passed + if (0 <= _endTime && getTime() > _endTime) + break; + } + + _inWork = false; +} + + +// Exit from work routine. Presumably this will be called from +// one of the source event handlers. +void +XmlRpcDispatch::exit() +{ + _endTime = 0.0; // Return from work asap +} + +// Clear all sources from the monitored sources list +void +XmlRpcDispatch::clear() +{ + if (_inWork) + _doClear = true; // Finish reporting current events before clearing + else + { + SourceList closeList = _sources; + _sources.clear(); + for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) + it->getSource()->close(); + } +} + + +double +XmlRpcDispatch::getTime() +{ +#ifdef USE_FTIME + struct timeb tbuff; + + ftime(&tbuff); + return ((double) tbuff.time + ((double)tbuff.millitm / 1000.0) + + ((double) tbuff.timezone * 60)); +#else + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + return (tv.tv_sec + tv.tv_usec / 1000000.0); +#endif /* USE_FTIME */ +} + + diff --git a/xmlrpc++/src/XmlRpcDispatch.h b/xmlrpc++/src/XmlRpcDispatch.h new file mode 100644 index 0000000..b3c4ec0 --- /dev/null +++ b/xmlrpc++/src/XmlRpcDispatch.h @@ -0,0 +1,88 @@ + +#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 +#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 + 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: + + // 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++/src/XmlRpcException.h b/xmlrpc++/src/XmlRpcException.h new file mode 100644 index 0000000..6090450 --- /dev/null +++ b/xmlrpc++/src/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 +#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++/src/XmlRpcServer.cpp b/xmlrpc++/src/XmlRpcServer.cpp new file mode 100644 index 0000000..f6b4aa5 --- /dev/null +++ b/xmlrpc++/src/XmlRpcServer.cpp @@ -0,0 +1,284 @@ + +#include "XmlRpcServer.h" +#include "XmlRpcServerConnection.h" +#include "XmlRpcServerMethod.h" +#include "XmlRpcSocket.h" +#include "XmlRpcUtil.h" +#include "XmlRpcException.h" + + +using namespace XmlRpc; + + +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; +} + + +// 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"); + _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent); + } +} + + +// Create a new connection object for processing requests from a specific client. +XmlRpcServerConnection* +XmlRpcServer::createConnection(int s) +{ + // Specify that the connection object be deleted when it is closed + return new XmlRpcServerConnection(s, this, true); +} + + +void +XmlRpcServer::removeConnection(XmlRpcServerConnection* sc) +{ + _disp.removeSource(sc); +} + + +// Stop processing client requests +void +XmlRpcServer::exit() +{ + _disp.exit(); +} + + +// Close the server socket file descriptor and stop monitoring connections +void +XmlRpcServer::shutdown() +{ + // This closes and destroys all connections as well as closing this socket + _disp.clear(); +} + + +// Introspection support +static const std::string LIST_METHODS("system.listMethods"); +static const std::string METHOD_HELP("system.methodHelp"); +static const std::string MULTICALL("system.multicall"); + + +// List all methods available on a server +class ListMethods : public XmlRpcServerMethod +{ +public: + ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {} + + void execute(XmlRpcValue& 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 XmlRpcServerConnection + result[i] = MULTICALL; +} + + + diff --git a/xmlrpc++/src/XmlRpcServer.h b/xmlrpc++/src/XmlRpcServer.h new file mode 100644 index 0000000..8172733 --- /dev/null +++ b/xmlrpc++/src/XmlRpcServer.h @@ -0,0 +1,104 @@ + +#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 +# include +#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. + bool bindAndListen(int port, int backlog = 5); + + //! 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); + + // XmlRpcSource interface implementation + + //! Handle client connection requests + virtual unsigned handleEvent(unsigned eventType); + + //! Remove a connection from the dispatcher + virtual void removeConnection(XmlRpcServerConnection*); + + protected: + + //! Accept a client connection request + virtual void acceptConnection(); + + //! Create a new connection object for processing requests from a specific client. + virtual XmlRpcServerConnection* createConnection(int socket); + + // 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; + MethodMap _methods; + + // system methods + XmlRpcServerMethod* _listMethods; + XmlRpcServerMethod* _methodHelp; + + }; +} // namespace XmlRpc + +#endif //_XMLRPCSERVER_H_ diff --git a/xmlrpc++/src/XmlRpcServerConnection.cpp b/xmlrpc++/src/XmlRpcServerConnection.cpp new file mode 100644 index 0000000..b9d6def --- /dev/null +++ b/xmlrpc++/src/XmlRpcServerConnection.cpp @@ -0,0 +1,371 @@ + +#include "XmlRpcServerConnection.h" + +#include "XmlRpcSocket.h" +#include "XmlRpc.h" +#ifndef MAKEDEPEND +# include +# include +#endif + +using namespace XmlRpc; + +// Static data +const char XmlRpcServerConnection::METHODNAME_TAG[] = ""; +const char XmlRpcServerConnection::PARAMS_TAG[] = ""; +const char XmlRpcServerConnection::PARAMS_ETAG[] = ""; +const char XmlRpcServerConnection::PARAM_TAG[] = ""; +const char XmlRpcServerConnection::PARAM_ETAG[] = ""; + +const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall"; +const std::string XmlRpcServerConnection::METHODNAME = "methodName"; +const std::string XmlRpcServerConnection::PARAMS = "params"; + +const std::string XmlRpcServerConnection::FAULTCODE = "faultCode"; +const std::string XmlRpcServerConnection::FAULTSTRING = "faultString"; + + + +// The server delegates handling client requests to a serverConnection object. +XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) : + XmlRpcSource(fd, deleteOnClose) +{ + XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd); + _server = server; + _connectionState = READ_HEADER; + _keepAlive = true; +} + + +XmlRpcServerConnection::~XmlRpcServerConnection() +{ + XmlRpcUtil::log(4,"XmlRpcServerConnection dtor."); + _server->removeConnection(this); +} + + +// Handle input on the server socket by accepting the connection +// and reading the rpc request. Return true to continue to monitor +// the socket for events, false to remove it from the dispatcher. +unsigned +XmlRpcServerConnection::handleEvent(unsigned /*eventType*/) +{ + if (_connectionState == READ_HEADER) + if ( ! readHeader()) return 0; + + if (_connectionState == READ_REQUEST) + if ( ! readRequest()) return 0; + + if (_connectionState == WRITE_RESPONSE) + if ( ! writeResponse()) return 0; + + return (_connectionState == WRITE_RESPONSE) + ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent; +} + + +bool +XmlRpcServerConnection::readHeader() +{ + // Read available data + bool eof; + if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) { + // Its only an error if we already have read some data + if (_header.length() > 0) + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length()); + char *hp = (char*)_header.c_str(); // Start of header + char *ep = hp + _header.length(); // End of string + char *bp = 0; // Start of body + char *lp = 0; // Start of content-length value + char *kp = 0; // Start of connection value + + for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) { + if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0)) + lp = cp + 16; + else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0)) + kp = cp + 12; + else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0)) + bp = cp + 4; + else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0)) + bp = cp + 2; + } + + // If we haven't gotten the entire header yet, return (keep reading) + if (bp == 0) { + // EOF in the middle of a request is an error, otherwise its ok + if (eof) { + XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF"); + if (_header.length() > 0) + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header"); + return false; // Either way we close the connection + } + + return true; // Keep reading + } + + // Decode content length + if (lp == 0) { + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified"); + return false; // We could try to figure it out by parsing as we read, but for now... + } + + _contentLength = atoi(lp); + if (_contentLength <= 0) { + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength); + return false; + } + + XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength); + + // Otherwise copy non-header data to request buffer and set state to read request. + _request = bp; + + // Parse out any interesting bits from the header (HTTP version, connection) + _keepAlive = true; + if (_header.find("HTTP/1.0") != std::string::npos) { + if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0) + _keepAlive = false; // Default for HTTP 1.0 is to close the connection + } else { + if (kp != 0 && strncasecmp(kp, "close", 5) == 0) + _keepAlive = false; + } + XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive); + + + _header = ""; + _connectionState = READ_REQUEST; + return true; // Continue monitoring this source +} + +bool +XmlRpcServerConnection::readRequest() +{ + // If we dont have the entire request yet, read available data + if (int(_request.length()) < _contentLength) { + bool eof; + if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) { + XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + // If we haven't gotten the entire request yet, return (keep reading) + if (int(_request.length()) < _contentLength) { + if (eof) { + XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request"); + return false; // Either way we close the connection + } + return true; + } + } + + // Otherwise, parse and dispatch the request + XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length()); + //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str()); + + _connectionState = WRITE_RESPONSE; + + return true; // Continue monitoring this source +} + + +bool +XmlRpcServerConnection::writeResponse() +{ + if (_response.length() == 0) { + executeRequest(); + _bytesWritten = 0; + if (_response.length() == 0) { + XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response."); + return false; + } + } + + // Try to write the response + if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) { + XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length()); + + // Prepare to read the next request + if (_bytesWritten == int(_response.length())) { + _header = ""; + _request = ""; + _response = ""; + _connectionState = READ_HEADER; + } + + return _keepAlive; // Continue monitoring this source if true +} + +// Run the method, generate _response string +void +XmlRpcServerConnection::executeRequest() +{ + XmlRpcValue params, resultValue; + std::string methodName = parseRequest(params); + XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'", + methodName.c_str()); + + try { + + if ( ! executeMethod(methodName, params, resultValue) && + ! executeMulticall(methodName, params, resultValue)) + generateFaultResponse(methodName + ": unknown method name"); + else + generateResponse(resultValue.toXml()); + + } catch (const XmlRpcException& fault) { + XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.", + fault.getMessage().c_str()); + generateFaultResponse(fault.getMessage(), fault.getCode()); + } +} + +// Parse the method name and the argument values from the request. +std::string +XmlRpcServerConnection::parseRequest(XmlRpcValue& params) +{ + int offset = 0; // Number of chars parsed from the request + + std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset); + + if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset)) + { + int nArgs = 0; + while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) { + params[nArgs++] = XmlRpcValue(_request, &offset); + (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset); + } + + (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset); + } + + return methodName; +} + +// Execute a named method with the specified params. +bool +XmlRpcServerConnection::executeMethod(const std::string& methodName, + XmlRpcValue& params, XmlRpcValue& result) +{ + XmlRpcServerMethod* method = _server->findMethod(methodName); + + if ( ! method) return false; + + method->execute(params, result); + + // Ensure a valid result value + if ( ! result.valid()) + result = std::string(); + + return true; +} + +// Execute multiple calls and return the results in an array. +bool +XmlRpcServerConnection::executeMulticall(const std::string& methodName, + XmlRpcValue& params, XmlRpcValue& result) +{ + if (methodName != SYSTEM_MULTICALL) return false; + + // There ought to be 1 parameter, an array of structs + if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray) + throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)"); + + int nc = params[0].size(); + result.setSize(nc); + + for (int i=0; i\r\n" + "\r\n\t"; + const char RESPONSE_2[] = + "\r\n\r\n"; + + std::string body = RESPONSE_1 + resultXml + RESPONSE_2; + std::string header = generateHeader(body); + + _response = header + body; + XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str()); +} + +// Prepend http headers +std::string +XmlRpcServerConnection::generateHeader(std::string const& body) +{ + std::string header = + "HTTP/1.1 200 OK\r\n" + "Server: "; + header += XMLRPC_VERSION; + header += "\r\n" + "Content-Type: text/xml\r\n" + "Content-length: "; + + char buffLen[40]; + sprintf(buffLen,"%d\r\n\r\n", body.size()); + + return header + buffLen; +} + + +void +XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode) +{ + const char RESPONSE_1[] = + "\r\n" + "\r\n\t"; + const char RESPONSE_2[] = + "\r\n\r\n"; + + XmlRpcValue faultStruct; + faultStruct[FAULTCODE] = errorCode; + faultStruct[FAULTSTRING] = errorMsg; + std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2; + std::string header = generateHeader(body); + + _response = header + body; +} + diff --git a/xmlrpc++/src/XmlRpcServerConnection.h b/xmlrpc++/src/XmlRpcServerConnection.h new file mode 100644 index 0000000..9efbbaf --- /dev/null +++ b/xmlrpc++/src/XmlRpcServerConnection.h @@ -0,0 +1,102 @@ +#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 +#endif + +#include "XmlRpcValue.h" +#include "XmlRpcSource.h" + +namespace XmlRpc { + + + // The server waits for client connections and provides methods + class XmlRpcServer; + class XmlRpcServerMethod; + + //! A class to handle XML RPC requests from a particular client + class XmlRpcServerConnection : public XmlRpcSource { + public: + // 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; + + //! 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: + + bool readHeader(); + bool readRequest(); + bool writeResponse(); + + // Parses the request, runs the method, generates the response xml. + virtual void executeRequest(); + + // Parse the methodName and parameters from the request. + std::string parseRequest(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. + bool executeMulticall(const std::string& methodName, XmlRpcValue& params, XmlRpcValue& result); + + // Construct a response from the result XML. + void generateResponse(std::string const& resultXml); + void generateFaultResponse(std::string const& msg, int errorCode = -1); + std::string generateHeader(std::string const& body); + + + // The XmlRpc server that accepted this connection + XmlRpcServer* _server; + + // Possible IO states for the connection + enum ServerConnectionState { READ_HEADER, READ_REQUEST, WRITE_RESPONSE }; + 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++/src/XmlRpcServerMethod.cpp b/xmlrpc++/src/XmlRpcServerMethod.cpp new file mode 100644 index 0000000..1616ff4 --- /dev/null +++ b/xmlrpc++/src/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++/src/XmlRpcServerMethod.h b/xmlrpc++/src/XmlRpcServerMethod.h new file mode 100644 index 0000000..738a9c8 --- /dev/null +++ b/xmlrpc++/src/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 +#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++/src/XmlRpcSocket.cpp b/xmlrpc++/src/XmlRpcSocket.cpp new file mode 100644 index 0000000..b71ef94 --- /dev/null +++ b/xmlrpc++/src/XmlRpcSocket.cpp @@ -0,0 +1,260 @@ + +#include "XmlRpcSocket.h" +#include "XmlRpcUtil.h" + +#ifndef MAKEDEPEND + +#if defined(_WINDOWS) +# include +# include +//# pragma lib(WS2_32.lib) + +# define EINPROGRESS WSAEINPROGRESS +# define EWOULDBLOCK WSAEWOULDBLOCK +# define ETIMEDOUT WSAETIMEDOUT +#else +extern "C" { +# include +# include +# include +# include +# include +# include +# include +# include +} +#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. +static inline bool +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; +#if defined(_WINDOWS) + int +#else + socklen_t +#endif + 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) +{ + const int READ_SIZE = 4096; // Number of bytes to attempt to read at a time + char readBuf[READ_SIZE]; + + bool wouldBlock = false; + *eof = false; + + while ( ! wouldBlock && ! *eof) { +#if defined(_WINDOWS) + int n = recv(fd, readBuf, READ_SIZE-1, 0); +#else + int n = read(fd, readBuf, READ_SIZE-1); +#endif + XmlRpcUtil::log(5, "XmlRpcSocket::nbRead: read/recv returned %d.", n); + + if (n > 0) { + readBuf[n] = 0; + s.append(readBuf, n); + } else if (n == 0) { + *eof = true; + } else if (nonFatalError()) { + wouldBlock = true; + } else { + return false; // Error + } + } + return true; +} + + +// Write text to the specified socket. Returns false on error. +bool +XmlRpcSocket::nbWrite(int fd, std::string& s, int *bytesSoFar) +{ + int nToWrite = int(s.length()) - *bytesSoFar; + char *sp = const_cast(s.c_str()) + *bytesSoFar; + bool wouldBlock = false; + + while ( nToWrite > 0 && ! wouldBlock ) { +#if defined(_WINDOWS) + int n = send(fd, sp, nToWrite, 0); +#else + int n = write(fd, sp, nToWrite); +#endif + XmlRpcUtil::log(5, "XmlRpcSocket::nbWrite: send/write returned %d.", n); + + if (n > 0) { + sp += n; + *bytesSoFar += n; + nToWrite -= n; + } else if (nonFatalError()) { + wouldBlock = true; + } else { + return false; // Error + } + } + return true; +} + + +// Returns last errno +int +XmlRpcSocket::getError() +{ +#if defined(_WINDOWS) + return WSAGetLastError(); +#else + return errno; +#endif +} + + +// Returns message corresponding to last errno +std::string +XmlRpcSocket::getErrorMsg() +{ + return getErrorMsg(getError()); +} + +// Returns message corresponding to errno... well, it should anyway +std::string +XmlRpcSocket::getErrorMsg(int error) +{ + char err[60]; + snprintf(err,sizeof(err),"error %d", error); + return std::string(err); +} + + diff --git a/xmlrpc++/src/XmlRpcSocket.h b/xmlrpc++/src/XmlRpcSocket.h new file mode 100644 index 0000000..fa7f950 --- /dev/null +++ b/xmlrpc++/src/XmlRpcSocket.h @@ -0,0 +1,69 @@ +#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 +#endif + +namespace XmlRpc { + + //! A platform-independent socket API. + class XmlRpcSocket { + public: + + //! 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); + + //! Write text to the specified socket. Returns false on error. + static bool nbWrite(int socket, std::string& s, int *bytesSoFar); + + + // 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); + + + //! 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++/src/XmlRpcSource.cpp b/xmlrpc++/src/XmlRpcSource.cpp new file mode 100644 index 0000000..99203b0 --- /dev/null +++ b/xmlrpc++/src/XmlRpcSource.cpp @@ -0,0 +1,35 @@ + +#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; + } + } + +} // namespace XmlRpc diff --git a/xmlrpc++/src/XmlRpcSource.h b/xmlrpc++/src/XmlRpcSource.h new file mode 100644 index 0000000..135dce4 --- /dev/null +++ b/xmlrpc++/src/XmlRpcSource.h @@ -0,0 +1,55 @@ + +#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 + +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; + + 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++/src/XmlRpcUtil.cpp b/xmlrpc++/src/XmlRpcUtil.cpp new file mode 100644 index 0000000..1bd583a --- /dev/null +++ b/xmlrpc++/src/XmlRpcUtil.cpp @@ -0,0 +1,250 @@ + +#include "XmlRpcUtil.h" + +#ifndef MAKEDEPEND +# include +# include +# include +# include +# include +#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 +#endif + +// Version id +const char XmlRpc::XMLRPC_VERSION[] = "XMLRPC++ 0.7"; + +// Default log verbosity: 0 for no messages through 5 (writes everything) +int XmlRpcLogHandler::_verbosity = 0; + +// Default log handler +static class DefaultLogHandler : public XmlRpcLogHandler { +public: + + void log(int level, const char* msg) { +#ifdef USE_WINDOWS_DEBUG + if (level <= _verbosity) { OutputDebugString(msg); OutputDebugString("\n"); } +#else + if (level <= _verbosity) std::cout << msg << std::endl; +#endif + } + +} defaultLogHandler; + +// Message log singleton +XmlRpcLogHandler* XmlRpcLogHandler::_logHandler = &defaultLogHandler; + + +// Default error handler +static class DefaultErrorHandler : public XmlRpcErrorHandler { +public: + + 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 and , updates offset to char after +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 = "= int(xml.length())) return false; + size_t istart = xml.find(tag, *offset); + if (istart == std::string::npos) + return false; + + *offset = int(istart + strlen(tag)); + return true; +} + + +// Returns true if the tag is found at the specified offset (modulo any whitespace) +// and updates offset to the char after the tag +bool +XmlRpcUtil::nextTagIs(const char* tag, std::string const& xml, int* offset) +{ + if (*offset >= int(xml.length())) return false; + const char* cp = xml.c_str() + *offset; + int nc = 0; + while (*cp && isspace(*cp)) { + ++cp; + ++nc; + } + + int len = int(strlen(tag)); + if (*cp && (strncmp(cp, tag, len) == 0)) { + *offset += nc + len; + return true; + } + return false; +} + +// Returns the next tag and updates offset to the char after the tag, or empty string +// if the next non-whitespace character is not '<' +std::string +XmlRpcUtil::getNextTag(std::string const& xml, int* offset) +{ + if (*offset >= int(xml.length())) return std::string(); + + size_t pos = *offset; + const char* cp = xml.c_str() + pos; + while (*cp && isspace(*cp)) { + ++cp; + ++pos; + } + + if (*cp != '<') return std::string(); + + std::string s; + do { + s += *cp; + ++pos; + } while (*cp++ != '>' && *cp != 0); + + *offset = int(pos); + return s; +} + + + +// xml encodings (xml-encoded entities are preceded with '&') +static const char AMP = '&'; +static const char rawEntity[] = { '<', '>', '&', '\'', '\"', 0 }; +static const char* xmlEntity[] = { "lt;", "gt;", "amp;", "apos;", "quot;", 0 }; +static const int xmlEntLen[] = { 3, 3, 4, 5, 5 }; + + +// Replace xml-encoded entities with the raw text equivalents. + +std::string +XmlRpcUtil::xmlDecode(const std::string& encoded) +{ + std::string::size_type iAmp = encoded.find(AMP); + if (iAmp == std::string::npos) + return encoded; + + std::string decoded(encoded, 0, iAmp); + std::string::size_type iSize = encoded.size(); + decoded.reserve(iSize); + + const char* ens = encoded.c_str(); + while (iAmp != iSize) { + if (encoded[iAmp] == AMP && iAmp+1 < iSize) { + int iEntity; + for (iEntity=0; xmlEntity[iEntity] != 0; ++iEntity) + //if (encoded.compare(iAmp+1, xmlEntLen[iEntity], xmlEntity[iEntity]) == 0) + if (strncmp(ens+iAmp+1, xmlEntity[iEntity], xmlEntLen[iEntity]) == 0) + { + decoded += rawEntity[iEntity]; + iAmp += xmlEntLen[iEntity]+1; + break; + } + if (xmlEntity[iEntity] == 0) // unrecognized sequence + decoded += encoded[iAmp++]; + + } else { + decoded += encoded[iAmp++]; + } + } + + return decoded; +} + + +// Replace raw text with xml-encoded entities. + +std::string +XmlRpcUtil::xmlEncode(const std::string& raw) +{ + std::string::size_type iRep = raw.find_first_of(rawEntity); + if (iRep == std::string::npos) + return raw; + + std::string encoded(raw, 0, iRep); + std::string::size_type iSize = raw.size(); + + while (iRep != iSize) { + int iEntity; + for (iEntity=0; rawEntity[iEntity] != 0; ++iEntity) + if (raw[iRep] == rawEntity[iEntity]) + { + encoded += AMP; + encoded += xmlEntity[iEntity]; + break; + } + if (rawEntity[iEntity] == 0) + encoded += raw[iRep]; + ++iRep; + } + return encoded; +} + + + diff --git a/xmlrpc++/src/XmlRpcUtil.h b/xmlrpc++/src/XmlRpcUtil.h new file mode 100644 index 0000000..8128f72 --- /dev/null +++ b/xmlrpc++/src/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 +#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 and , updates offset to char after + 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++/src/XmlRpcValue.cpp b/xmlrpc++/src/XmlRpcValue.cpp new file mode 100644 index 0000000..d6cf3f0 --- /dev/null +++ b/xmlrpc++/src/XmlRpcValue.cpp @@ -0,0 +1,611 @@ + +#include "XmlRpcValue.h" +#include "XmlRpcException.h" +#include "XmlRpcUtil.h" +#include "base64.h" + +#ifndef MAKEDEPEND +# include +# include +# include +# include +#endif + +namespace XmlRpc { + + + static const char VALUE_TAG[] = ""; + static const char VALUE_ETAG[] = ""; + + static const char BOOLEAN_TAG[] = ""; + static const char BOOLEAN_ETAG[] = ""; + static const char DOUBLE_TAG[] = ""; + static const char DOUBLE_ETAG[] = ""; + static const char INT_TAG[] = ""; + static const char I4_TAG[] = ""; + static const char I4_ETAG[] = ""; + static const char STRING_TAG[] = ""; + static const char DATETIME_TAG[] = ""; + static const char DATETIME_ETAG[] = ""; + static const char BASE64_TAG[] = ""; + static const char BASE64_ETAG[] = ""; + + static const char ARRAY_TAG[] = ""; + static const char DATA_TAG[] = ""; + static const char DATA_ETAG[] = ""; + static const char ARRAY_ETAG[] = ""; + + static const char STRUCT_TAG[] = ""; + static const char MEMBER_TAG[] = ""; + static const char NAME_TAG[] = ""; + static const char NAME_ETAG[] = ""; + static const char MEMBER_ETAG[] = ""; + static const char STRUCT_ETAG[] = ""; + + + + // 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 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 tag + else if (typeTag == VALUE_ETAG) + { + *offset = afterValueOffset; // back up & try again + result = stringFromXml(valueXml, offset); + } + + if (result) // Skip over the tag + XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset); + else // Unrecognized tag after + *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]; + // this was fixed for windows use (frauenberger@iem.at) 12.10.2004 + snprintf(buf, sizeof(buf)-1, "%f" , _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_isdst = -1; + _type = TypeDateTime; + _value.asTime = new struct tm(t); + *offset += int(stime.length()); + return true; + } + + std::string XmlRpcValue::timeToXml() const + { + struct tm* t = _value.asTime; + char buf[20]; + snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", + t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); + buf[sizeof(buf)-1] = 0; + + std::string xml = VALUE_TAG; + xml += DATETIME_TAG; + xml += buf; + xml += DATETIME_ETAG; + xml += VALUE_ETAG; + return xml; + } + + + // Base64 + bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset) + { + size_t valueEnd = valueXml.find('<', *offset); + if (valueEnd == std::string::npos) + return false; // No end tag; + + _type = TypeBase64; + std::string asString = valueXml.substr(*offset, valueEnd-*offset); + _value.asBinary = new BinaryData(); + // check whether base64 encodings can contain chars xml encodes... + + // convert from base64 to binary + int iostatus = 0; + base64 decoder; + std::back_insert_iterator 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 base64data; + int iostatus = 0; + base64 encoder; + std::back_insert_iterator > 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 + (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; iat(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 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 out(os); + base64 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 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++/src/XmlRpcValue.h b/xmlrpc++/src/XmlRpcValue.h new file mode 100644 index 0000000..7535d4a --- /dev/null +++ b/xmlrpc++/src/XmlRpcValue.h @@ -0,0 +1,189 @@ + +#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 +# include +# include +# include +#endif + +namespace XmlRpc { + + //! RPC method arguments and results are represented by Values + // should probably refcount them... + class XmlRpcValue { + public: + + + enum Type { + TypeInvalid, + TypeBoolean, + TypeInt, + TypeDouble, + TypeString, + TypeDateTime, + TypeBase64, + TypeArray, + TypeStruct + }; + + // Non-primitive types + typedef std::vector BinaryData; + typedef std::vector ValueArray; + typedef std::map ValueStruct; + + + //! Constructors + XmlRpcValue() : _type(TypeInvalid) { _value.asBinary = 0; } + XmlRpcValue(bool value) : _type(TypeBoolean) { _value.asBool = value; } + XmlRpcValue(int value) : _type(TypeInt) { _value.asInt = value; } + XmlRpcValue(double value) : _type(TypeDouble) { _value.asDouble = value; } + + XmlRpcValue(std::string const& value) : _type(TypeString) + { _value.asString = new std::string(value); } + + XmlRpcValue(const char* value) : _type(TypeString) + { _value.asString = new std::string(value); } + + XmlRpcValue(struct tm* value) : _type(TypeDateTime) + { _value.asTime = new struct tm(*value); } + + + 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 + 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 + XmlRpcValue& operator=(XmlRpcValue const& rhs); + XmlRpcValue& operator=(int const& rhs) { return operator=(XmlRpcValue(rhs)); } + XmlRpcValue& operator=(double const& rhs) { return operator=(XmlRpcValue(rhs)); } + XmlRpcValue& operator=(const char* rhs) { return operator=(XmlRpcValue(std::string(rhs))); } + + bool operator==(XmlRpcValue const& other) const; + bool operator!=(XmlRpcValue const& other) const; + + operator bool&() { assertTypeOrInvalid(TypeBoolean); return _value.asBool; } + operator int&() { assertTypeOrInvalid(TypeInt); return _value.asInt; } + operator double&() { assertTypeOrInvalid(TypeDouble); return _value.asDouble; } + operator std::string&() { assertTypeOrInvalid(TypeString); return *_value.asString; } + operator BinaryData&() { assertTypeOrInvalid(TypeBase64); return *_value.asBinary; } + operator struct tm&() { assertTypeOrInvalid(TypeDateTime); return *_value.asTime; } + + XmlRpcValue const& operator[](int i) const { assertArray(i+1); return _value.asArray->at(i); } + XmlRpcValue& operator[](int i) { assertArray(i+1); return _value.asArray->at(i); } + + XmlRpcValue& operator[](std::string const& k) { assertStruct(); return (*_value.asStruct)[k]; } + XmlRpcValue& operator[](const char* k) { assertStruct(); std::string s(k); return (*_value.asStruct)[s]; } + + // 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++/src/base64.h b/xmlrpc++/src/base64.h new file mode 100644 index 0000000..519ee0f --- /dev/null +++ b/xmlrpc++/src/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 +#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 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 + _OI operator()(_OI _To) const{ + *_To = _Tr::to_char_type('\r'); ++_To; + *_To = _Tr::to_char_type('\n'); ++_To; + + return (_To); + } + }; + + + struct crlfsp + { + template + _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 + _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 + _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 + _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 diff --git a/xmlrpc-init.pd b/xmlrpc-init.pd new file mode 100644 index 0000000..d92b05f --- /dev/null +++ b/xmlrpc-init.pd @@ -0,0 +1,12 @@ +#N canvas 33 240 529 222 10; +#X obj 19 115 xmlrpc 8000; +#X text 21 68 XMLRPC configuration; +#X text 18 83 (this object can be destroyed again once it has been +created); +#X obj 11 10 cnv 15 500 40 empty empty xmlrpc 10 22 0 24 -260818 -1 +0; +#X text 148 14 xmlrpc external for PD \, author: Thomas Grill; +#X text 144 31 (C)2003 IEM \, Graz; +#X obj 21 180 netreceive; +#X text 25 144 For Windows user a dummy netreceive object MUST be present +Otherwise \, Pd uses 99% CPU power when invoked with -nogui; diff --git a/xmlrpc-test.pd b/xmlrpc-test.pd new file mode 100644 index 0000000..4eb0acd --- /dev/null +++ b/xmlrpc-test.pd @@ -0,0 +1,31 @@ +#N canvas 33 240 600 168 10; +#X floatatom 19 126 5 0 0 0 - - -; +#X text 19 66 some receivers; +#X text 232 64 some senders; +#X floatatom 234 92 5 0 0 0 - - -; +#X obj 19 91 r recv1; +#X obj 94 91 r recv2; +#X obj 234 122 s send1; +#X obj 327 123 s send2; +#X msg 316 91 a b; +#X msg 354 91 1 2 3 hula; +#X obj 469 107 env~; +#X obj 469 130 s send3; +#X floatatom 504 107 5 0 0 0 - - -; +#X obj 469 84 osc~ 0.1; +#X obj 93 123 print PD; +#X obj 11 10 cnv 15 500 40 empty empty xmlrpc 10 22 0 24 -260818 -1 +0; +#X text 146 12 xmlrpc external for PD \, author: Thomas Grill; +#X text 142 31 (C)2003 IEM \, Graz; +#X obj 526 10 loadbang; +#X msg 526 37 \; pd dsp 1; +#X connect 3 0 6 0; +#X connect 4 0 0 0; +#X connect 5 0 14 0; +#X connect 8 0 7 0; +#X connect 9 0 7 0; +#X connect 10 0 11 0; +#X connect 10 0 12 0; +#X connect 13 0 10 0; +#X connect 18 0 19 0; diff --git a/xmlrpc-test.py b/xmlrpc-test.py new file mode 100644 index 0000000..92f8ced --- /dev/null +++ b/xmlrpc-test.py @@ -0,0 +1,75 @@ +# testprogramm +#* Author: Thomas Grill t.grill [at] gmx.net +# License LGPL see LICENSE.txt +# IEM - Institute of Electronic Music and Acoustics, Graz +# Inffeldgasse 10/3, 8010 Graz, Austria +# http://iem.at +#************************************************************/ +# --- import some functions --- + +import xmlrpclib +import time +import sys + +# --- connect to external --- + +s = xmlrpclib.ServerProxy("http://localhost:8000") + +# --- load patch ---- + +if len(sys.argv) >= 2: + patchname = sys.argv[1] +else: + # defaulting + patchname = 'xmlrpc-test.pd' + +print "Loading file:",patchname + +# open PD patch as file +patch=open(patchname,'r') + +# read all lines of patch +lines=patch.readlines() + +# function to concatenate two lines +def concat(l,b): + if b[0] != "#": + l[len(l)-1] = l[len(l)-1]+b + else: + return l.append(b) + +# add all lines that don't begin with # to their predecendants +# this is necessary for objects with many parameters (e.g. IEM GUI objects) +clines = [] +for l in lines: + concat(clines,l) + +# send it to the external +s.load(clines) + +# close file +patch.close() + +# --- send some data --- + +s.send("recv1",4) +s.send("recv2",[1,2,3,"df"]) + +# --- receive some data --- + +# bind to a PD symbol +s.bind("send3") + +# get the value +print "Got: ", s.query("send3") + +# unbind again (only necessary to save PD resources) +s.unbind("send3") + +# --- wait a bit --- + +time.sleep(2) + +# --- close our PD test patch --- + +s.close() diff --git a/xmlrpc.dsp b/xmlrpc.dsp new file mode 100644 index 0000000..a2c693d --- /dev/null +++ b/xmlrpc.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="xmlrpc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** NICHT BEARBEITEN ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=xmlrpc - Win32 Debug +!MESSAGE Dies ist kein gьltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE +!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fьhren Sie den Befehl +!MESSAGE +!MESSAGE NMAKE /f "xmlrpc.mak". +!MESSAGE +!MESSAGE Sie kцnnen beim Ausfьhren von NMAKE eine Konfiguration angeben +!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: +!MESSAGE +!MESSAGE NMAKE /f "xmlrpc.mak" CFG="xmlrpc - Win32 Debug" +!MESSAGE +!MESSAGE Fьr die Konfiguration stehen zur Auswahl: +!MESSAGE +!MESSAGE "xmlrpc - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library") +!MESSAGE "xmlrpc - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "xmlrpc" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "xmlrpc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XMLRPC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "c:\programme\audio\pd\src" /I "../../xmlrpc++/src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XMLRPC_EXPORTS" /D "NT" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "NDEBUG" +# ADD RSC /l 0xc07 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 pd.lib pthreadVC.lib Ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"c:\programme\audio\pd\bin" + +!ELSEIF "$(CFG)" == "xmlrpc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XMLRPC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "d:\pd\src" /I "../../xmlrpc++/src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XMLRPC_EXPORTS" /D "NT" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "_DEBUG" +# ADD RSC /l 0xc07 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 pd.lib pthreadVC.lib Ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib xmlrpc.lib /nologo /entry:"xmlrpc_setup" /dll /debug /machine:I386 /pdbtype:sept /libpath:"d:\pd\bin" /libpath:"..\..\xmlrpc++" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "xmlrpc - Win32 Release" +# Name "xmlrpc - Win32 Debug" +# Begin Source File + +SOURCE=.\main.cpp +# End Source File +# End Target +# End Project diff --git a/xmlrpc.dsw b/xmlrpc.dsw new file mode 100644 index 0000000..fb7f779 --- /dev/null +++ b/xmlrpc.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELЦSCHT WERDEN! + +############################################################################### + +Project: "xmlrpc"=.\xmlrpc.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/xmlrpc.vcproj b/xmlrpc.vcproj new file mode 100644 index 0000000..e10efbf --- /dev/null +++ b/xmlrpc.vcproj @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1