summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjaromil <jaromil@949728d9-16ea-0310-a75c-cbdf8430a4b8>2006-09-12 14:34:11 (GMT)
committer jaromil <jaromil@949728d9-16ea-0310-a75c-cbdf8430a4b8>2006-09-12 14:34:11 (GMT)
commit950ca4ebb0781245ef89ec6adddfe0271be3f6cb (patch)
tree24d68fb8700b374f41087b930767fd9575b8eacc
parent7f710cc9a49700d9b749848c043970553bdd675c (diff)
interactive branch for Lydia's installation
git-svn-id: svn://dyne.org/montevideo/ivysync@69 949728d9-16ea-0310-a75c-cbdf8430a4b8
-rw-r--r--branches/lydia/AUTHORS17
-rw-r--r--branches/lydia/COPYING280
-rw-r--r--branches/lydia/Makefile50
-rw-r--r--branches/lydia/README102
-rw-r--r--branches/lydia/cmdline.cpp408
-rw-r--r--branches/lydia/daemon.h33
-rw-r--r--branches/lydia/decoder.cpp695
-rw-r--r--branches/lydia/decoder.h133
-rw-r--r--branches/lydia/gui.cpp610
-rw-r--r--branches/lydia/gui.h96
-rw-r--r--branches/lydia/ivysync-gui.glade604
-rwxr-xr-xbranches/lydia/ivysync-remote.py129
-rw-r--r--branches/lydia/ivysync-xmlrpc-spec.odtbin0 -> 9945 bytes
-rw-r--r--branches/lydia/linklist.cpp450
-rw-r--r--branches/lydia/linklist.h110
-rw-r--r--branches/lydia/thread.cpp46
-rw-r--r--branches/lydia/thread.h47
-rw-r--r--branches/lydia/utils.cpp168
-rw-r--r--branches/lydia/utils.h44
-rw-r--r--branches/lydia/xmlrpc++/Doxyfile1041
-rw-r--r--branches/lydia/xmlrpc++/Makefile34
-rw-r--r--branches/lydia/xmlrpc++/README102
-rw-r--r--branches/lydia/xmlrpc++/XmlRpc.h97
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcClient.cpp547
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcClient.h148
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcDispatch.cpp268
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcDispatch.h93
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcException.h42
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcMutex.cpp60
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcMutex.h46
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcServer.cpp494
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcServer.h160
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcServerConnection.cpp205
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcServerConnection.h82
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcServerMethod.cpp21
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcServerMethod.h47
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcSocket.cpp285
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcSocket.h76
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcSource.cpp40
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcSource.h67
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcThread.cpp74
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcThread.h60
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcThreadedServer.cpp27
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcThreadedServer.h67
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcUtil.cpp259
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcUtil.h61
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcValue.cpp611
-rw-r--r--branches/lydia/xmlrpc++/XmlRpcValue.h259
-rw-r--r--branches/lydia/xmlrpc++/base64.h379
-rw-r--r--branches/lydia/xmlrpc.cpp308
-rw-r--r--branches/lydia/xmlrpc.h209
51 files changed, 10291 insertions, 0 deletions
diff --git a/branches/lydia/AUTHORS b/branches/lydia/AUTHORS
new file mode 100644
index 0000000..89d2289
--- /dev/null
+++ b/branches/lydia/AUTHORS
@@ -0,0 +1,17 @@
+
+Author:
+
+ Denis "Jaromil" Rojo - coder and mantainer
+
+Contributors:
+
+ Ramon Coelho - experienced review of sync quality
+ Robert De Geuss - user friendly documentation
+ Wiel Seuskens - testing, debugging, web based XMLRPC client
+
+Artists:
+
+ IvySync has been employed by Montevideo / Time Based Arts to produce and
+ exhibit video-art installations by, among the others:
+ Linda Wallace, Calin Dan, Erwin Olaf, Sami Kallinen
+
diff --git a/branches/lydia/COPYING b/branches/lydia/COPYING
new file mode 100644
index 0000000..5a965fb
--- /dev/null
+++ b/branches/lydia/COPYING
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/branches/lydia/Makefile b/branches/lydia/Makefile
new file mode 100644
index 0000000..8c7e541
--- /dev/null
+++ b/branches/lydia/Makefile
@@ -0,0 +1,50 @@
+# We don't need kludgy automatizations here,
+# let's use a simple Makefile.
+# Just tweak the values below to fix your paths
+#
+# $Id: Makefile 60 2004-11-12 15:40:18Z jaromil $
+
+
+CPP = g++
+LINKER = ld
+
+GTKFLAGS = `pkg-config --cflags gtk+-2.0`
+GTKLIBS = `pkg-config --libs gtk+-2.0`
+AOFLAGS = `pkg-config --cflags ao`
+AOFLAGS = `pkg-config --libs ao`
+
+
+# debugging flags:
+CPPFLAGS = -I. -Ixmlrpc++ -Wall -g -ggdb $(GTKFLAGS) $(AOFLAGS)
+# optimized flags:
+#CPPFLAGS = -I. -Ixmlrpc++ -Wall -O2 -fomit-frame-pointer -ffast-math $(GTKFLAGS)
+
+
+
+
+LIBS = xmlrpc++/libxmlrpc++.a -lpthread -lssl
+
+IVYSYNC_OBJ = decoder.o thread.o linklist.o utils.o cmdline.o gui.o xmlrpc.o
+
+all: xmlrpc ivysync
+
+xmlrpc:
+ cd xmlrpc++ && $(MAKE)
+
+ivysync: $(IVYSYNC_OBJ)
+ $(CPP) $(CPPFLAGS) -o ivysync $(IVYSYNC_OBJ) $(LIBS) $(GTKLIBS) $(AOLIBS)
+
+#make clean
+clean:
+ rm -rf *.o *~ ivysync
+
+install: ivysync
+ install ivysync /usr/local/bin
+
+# generic make rules
+#%: %.c
+# $(CC) $(CFLAGS) -o $@ $< $(LIBS)
+#%.o: %.c
+# $(CC) $(CFLAGS) -c -o $@ $<
+
+
diff --git a/branches/lydia/README b/branches/lydia/README
new file mode 100644
index 0000000..ef389c1
--- /dev/null
+++ b/branches/lydia/README
@@ -0,0 +1,102 @@
+ ,-_/ .---.
+ ' | . , . . \___ . . ,-. ,-.
+ .^ | | / | | \ | | | | |
+ `--' `' `-| `---' `-| ' ' `-' [ver 0.4]
+ /| /|
+ `-' `-' by Denis "jaromil" Rojo
+ Montevideo Artlab R&D
+
+
+Interactive customization - branched from mainstream ivysync
+ commissioned by Lydia Schouten
+
+IvySync provides synced playback of multiple videos on multiple screens
+
+In the video art field there are many artworks requiring the synced and
+parallel playback of audio/video materials: IvySync provides such
+functionality for IvyTV supported hardware decoders, with accurate
+frame precision.
+
+IvySync reaches to sync up to 4 video channels with frame precision and
+comes together with a user friendly graphical interface (GTK2) to edit
+the playlists.
+
+It is possible to remotely control the playlist and playback of each
+video sending commands across the network (XMLRPC).
+Support for time based scheduling of different playlists is also
+implemented (but still lacking a graphical interface) to let a running
+syncstarter cycle various screening programs at different hours, days
+or months.
+
+
+IvySync is a dyne.org free software production (GNU GPL)
+commissioned by Montevideo / Time Based Arts
+used to exhibit video-art installations by, among the others:
+Linda Wallace, Calin Dan, Erwin Olaf, Sami Kallinen
+
+
+== DOCUMENTATION
+
+For more documentation, screenshots and download, go to the website:
+ ______ http://montevideo.dyne.org/trac.cgi/wiki/IvySync
+
+
+
+
+== FEATURES
+
+ * full quality s/video or composite multiple output
+
+ * professional grade low cost solution
+
+ * remotely controllable playback (XMLRPC)
+
+ * graphical interface for playlist scheduling
+
+ * extensible, scriptable and reliable *NIX technology
+
+ * frame accurate sync of DVD quality audio/video (MPEG2)
+
+ * streaming playback from a local network (with Samba)
+
+ * capable of syncing up to 4 videos
+
+ * time based scheduling of multiple playlists
+
+
+== REQUIREMENTS
+
+ * GNU/Linux supported platform with ~500Mhz CPU
+
+ * IvyTV compatible hardware decoder
+
+ *** RENT or BUY a syncstarter pre-configured for your needs ***
+ *** warranty and delivery services available, please contact: ***
+
+ Denis "Jaromil" Rojo Netherlands Media Art Institute
+ software inventor R&D Montevideo / Time Based Arts
+ GNU/Linux developer Dyne.org Foundation
+ ------------------------------------------------------------
+ Tel: +31 20 6237101 121 ___ Tue and Fri, 10:30 - 17:30 CET
+ Fax: +31 20 6244423 Irc: irc.freenode.net /msg jaromil
+ Post: NMAI/Artlab Keizersgracht 264 1016 EV Amsterdam
+
+
+
+== DISCLAIMER
+
+ IvySync is Copyright (C) 2004-2006 by Denis Roio <jaromil@dyne.org>
+
+# This source code is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Public License as published
+# by the Free Software Foundation; either version 2 of the License,
+# or (at your option) any later version.
+#
+# This source code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# Please refer to the GNU Public License for more details.
+#
+# You should have received a copy of the GNU Public License along with
+# this source code; if not, write to:
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/branches/lydia/cmdline.cpp b/branches/lydia/cmdline.cpp
new file mode 100644
index 0000000..c73ea80
--- /dev/null
+++ b/branches/lydia/cmdline.cpp
@@ -0,0 +1,408 @@
+
+/* IvySync - Video SyncStarter
+ *
+ * (c) Copyright 2004-2006 Denis Roio aka jaromil <jaromil@dyne.org>
+ * Nederlands Instituut voor Mediakunst
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * to compile this sourcecode: gcc -o ivysync ivysync.c -lpthreads
+ * it should work on any POSIX system, including embedded hardware
+ * wherever the IvyTV drivers can also run (see http://ivtv.sf.net)
+ *
+ */
+
+#include <cstdio>
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <decoder.h>
+
+#include <xmlrpc.h>
+#include <gui.h>
+
+#include <utils.h>
+
+
+bool syncstart = false;
+bool graphical = false;
+bool dummytest = false;
+bool rpcdaemon = false;
+int rpcdaemonport = 2640;
+int videobuf = 64;
+
+// our global linklist holding all instantiated decoders
+Linklist decoders;
+
+// graphical interface
+Gui *gui;
+
+// xmlrpc interface
+XmlRpcServer *xmlrpc;
+
+// Threaded daemon
+IvySyncDaemon *ivydaemon;
+
+char *help =
+"Usage: ivysync [-hsDgt] [ -d /dev/video16 [ -p playmode files ] ]\n"
+" -h --help show this help\n"
+" -t --test dummy testrun: don't open devices\n"
+" -D --debug print verbose debugging messages\n"
+" -s --scan scan for available devices\n"
+" -d --device activate a device (i.e. /dev/video16)\n"
+" -b --buffer size of video buffer in KB (default 64)\n"
+" -p --playmode playlist mode (play|cont|loop|rand)\n"
+" -x --xmlrpc run XmlRpc daemon on a network port\n"
+" -g --gui start the graphical user interface\n";
+
+char *short_options = "-hd:sb:x:p:gtD:";
+const struct option long_options[] = {
+ { "help", no_argument, NULL, 'h'},
+ { "device", required_argument, NULL, 'd'},
+ { "scan", no_argument, NULL, 's'},
+ { "buffer", required_argument, NULL, 'b'},
+ { "xmlrpc", required_argument, NULL, 'x'},
+ { "playmode", required_argument, NULL, 'p'},
+ { "gui", no_argument, NULL, 'g'},
+ { "test", no_argument, NULL, 't'},
+ { "debug", required_argument, NULL, 'D'},
+ {0, 0, 0, 0}
+};
+
+
+void quitproc (int Sig) { /* signal handling */
+ N("received signal %u on process %u",Sig,getpid());
+ A("please wait while quitting threads");
+
+ if(graphical) gtk_main_quit();
+
+ Decoder *dec;
+ dec = (Decoder*)decoders.begin();
+ while(dec) {
+ dec->close();
+ delete dec;
+ dec = (Decoder*)decoders.begin();
+ }
+
+}
+
+#define CHECK_DECODER \
+if(!dec) { \
+ dec = (Decoder*)decoders[1]; \
+ if(!dec) { \
+ dec = new Decoder(); \
+ if( dec->init("/dev/video16") ) { \
+ decoders.append( dec ); \
+ } else { \
+ delete dec; \
+ dec = NULL; \
+ } \
+ } \
+}
+
+int cmdline(int argc, char **argv) {
+ Decoder *dec = NULL;
+ FILE *fd = NULL;
+ int c;
+ int res;
+
+ N("IvySync 0.3 / (c)2004-2006 Denis Rojo <jaromil@dyne.org>");
+
+ do {
+ res = getopt_long(argc, argv, short_options, long_options, NULL);
+
+ switch(res) {
+
+ case 'h':
+ fprintf(stderr,"%s",help);
+ exit(1);
+ break;
+
+ case 'd':
+ dec = new Decoder();
+ dec->dummy = dummytest;
+ if( dec->init(optarg) )
+ decoders.append( dec );
+ else {
+ E("can't initialize device %s",optarg);
+ delete dec;
+ dec = NULL;
+ }
+ break;
+
+ case 's':
+ N("Scanning for available playback devices...");
+ dec = new Decoder();
+ c=0;
+
+ if( dec->init("/dev/video16") ) {
+ A("1. /dev/video16 is present");
+ c++;
+ }
+ dec->close();
+
+
+ if( dec->init("/dev/video17") ) {
+ A("2. /dev/video17 is present");
+ c++;
+ }
+ dec->close();
+
+
+ if( dec->init("/dev/video18") ) {
+ A("3. /dev/video18 is present");
+ c++;
+ }
+ dec->close();
+
+
+ if( dec->init("/dev/video19") ) {
+ A("4. /dev/video19");
+ c++;
+ }
+ dec->close();
+
+ delete dec;
+ N("Total of %u device(s) found",c);
+ exit(1);
+ break;
+
+ case 'p':
+ CHECK_DECODER;
+ if( strncasecmp(optarg,"play",4) ==0 ) {
+
+ dec->playmode = PLAY;
+
+ } else if( strncasecmp(optarg,"cont",4) ==0 ) {
+
+ dec->playmode = CONT;
+
+ } else if( strncasecmp(optarg,"loop",4) ==0 ) {
+
+ dec->playmode = LOOP;
+
+ } else if( strncasecmp(optarg,"rand",4) ==0 ) {
+
+ dec->playmode = RAND;
+
+ } else if( strncasecmp(optarg,"single",6) ==0 ) {
+
+ dec->playmode = SINGLE;
+
+ } else
+ E("unrecognized playmode: %s",optarg);
+
+ break;
+
+ case 'x':
+ rpcdaemon = true;
+ rpcdaemonport = atoi(optarg);
+ break;
+
+ case 'g':
+ graphical = true;
+ break;
+
+ case 't':
+ dummytest = true;
+ break;
+
+ case 'D':
+ set_debug( atoi(optarg) );
+ break;
+
+ case 'b':
+ videobuf = atoi(optarg);
+ break;
+
+ case 1:
+ fd = fopen(optarg,"rb");
+ if(!fd) {
+
+ E("file %s is not readable: %s",optarg, strerror(errno));
+
+ } else {
+
+ CHECK_DECODER;
+ dec->append( optarg );
+ fclose(fd);
+ }
+
+ break;
+
+ default: break;
+
+ }
+
+ } while(res > 0);
+
+ return res;
+}
+
+
+int main(int argc, char **argv) {
+ Decoder *dec;
+
+ set_debug(1);
+
+ /* register quit signal handlers */
+ if (signal (SIGINT, quitproc) == SIG_ERR) {
+ fprintf(stderr,"Couldn't install SIGINT handler\n");
+ exit (0);
+ }
+ if (signal (SIGQUIT, quitproc) == SIG_ERR) {
+ fprintf(stderr,"Couldn't install SIGQUIT handler\n");
+ exit (0);
+ }
+ if (signal (SIGTERM, quitproc) == SIG_ERR) {
+ fprintf(stderr,"Couldn't install SIGTERM handler\n");
+ exit (0);
+ }
+
+
+ cmdline(argc, argv);
+
+ if( !decoders.len() ) {
+ E("no decoder device is initialized, aborting operations");
+ exit(0);
+ }
+
+ /////////////////////////////////
+ // setup the graphical interface
+ if(graphical)
+ if(!getenv("DISPLAY")) {
+ graphical = false;
+ E("can't use graphical interface: no display found");
+ }
+
+ if(graphical) {
+ A("activating graphical user interface for playlist configuration");
+ // gtk_set_locale();
+ gtk_init(&argc,&argv);
+ // add_pixmap_directory(PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
+
+ gui = new Gui();
+ gui->init(&decoders);
+ gui->start();
+
+ exit(1);
+ }
+ ////////////////////////////////
+
+
+ ////////////////////////////////
+ /// setup the XMLRPC interface
+ if(rpcdaemon) {
+ A("activating XMLRPC daemon listener for remote network control");
+
+ xmlrpc = new XmlRpcServer();
+
+ // instantiate all classes
+ new Play (xmlrpc, &decoders);
+ new Stop (xmlrpc, &decoders);
+ new Pause (xmlrpc, &decoders);
+ new GetPos(xmlrpc, &decoders);
+ new SetPos(xmlrpc, &decoders);
+ new GetOffset(xmlrpc, &decoders);
+ new SetOffset(xmlrpc, &decoders);
+ new Open (xmlrpc, &decoders);
+ new Quit (xmlrpc, &decoders);
+
+ ivydaemon = new IvySyncDaemon(xmlrpc);
+
+ if( ! ivydaemon->init( rpcdaemonport) ) {
+ E("can't initialize daemon listening");
+ delete ivydaemon;
+ delete xmlrpc;
+ rpcdaemon = false;
+ } else
+ A("XMLRPC daemon listening for commands on port %u",
+ rpcdaemonport);
+ }
+
+ ////////////////////////////////
+
+
+
+ ////////////////////////////////
+ /// Syncstart!
+
+ N("Proceeding to syncstart");
+ dec = (Decoder*)decoders.begin();
+ while(dec) {
+
+ dec->setup( &syncstart, videobuf );
+
+ dec->launch();
+
+ if( ! rpcdaemon ) {
+ // try to load the playlist
+ dec->load();
+ dec->play();
+ }
+
+ dec = (Decoder*)dec->next;
+ }
+
+ if( ! rpcdaemon ) {
+ A("Syncing %i players...",decoders.len());
+
+ jsleep(0,500);
+ A("Start!");
+ }
+
+ syncstart = 1;
+ ////////////////////////////////
+
+
+
+
+ if(rpcdaemon) {
+
+ // run as a daemon: quit only when requested
+ while( ! ivydaemon->quit ) {
+ ivydaemon->run(1.0);
+ jsleep(0,10);
+ }
+
+ } else {
+
+ // run until all the channels are at the end
+ int still_running = decoders.len();
+
+ while(still_running) {
+
+ still_running = 0;
+ dec = (Decoder*)decoders.begin();
+
+ while(dec) {
+
+ if(dec->playing) still_running++;
+
+ jsleep(1,0); // 1 second delay check
+
+ dec = (Decoder*)dec->next;
+ }
+ }
+ }
+
+ N("quit!");
+
+ exit(1);
+}
diff --git a/branches/lydia/daemon.h b/branches/lydia/daemon.h
new file mode 100644
index 0000000..c49e9ef
--- /dev/null
+++ b/branches/lydia/daemon.h
@@ -0,0 +1,33 @@
+#ifndef __DAEMON_H__
+#define __DAEMON_H__
+
+#include <decoder.h>
+#include <utils.h>
+#include <fakiir.h>
+
+class Daemon {
+ public:
+ Daemon();
+ ~Daemon();
+
+ bool init(vector<Decoder*> *devices);
+ void launch();
+ void quit();
+
+
+ /// fakiir external api
+ int get_num_decoders();
+
+ int play(int dec, char *path);
+ int stop();
+
+ float get_position(int dec);
+ int set_position(float pos);
+ ///
+
+
+ private:
+ vector<Decoder*> *decoders;
+};
+
+#endif
diff --git a/branches/lydia/decoder.cpp b/branches/lydia/decoder.cpp
new file mode 100644
index 0000000..ae46d9d
--- /dev/null
+++ b/branches/lydia/decoder.cpp
@@ -0,0 +1,695 @@
+/* IvySync - Video SyncStarter
+ *
+ * (c) Copyright 2004 - 2006 Denis Rojo <jaromil@dyne.org>
+ * Nederlands Instituut voor Mediakunst
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/select.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <decoder.h>
+#include <utils.h>
+#include <gui.h>
+
+
+
+Decoder::Decoder()
+ : Thread(), Entry() {
+ fd = 0;
+ playmode = SINGLE;
+ position = -1;
+ playing = false;
+ stopped = false;
+ dummy = false;
+ gui = false;
+ quit = true;
+
+ filesize = 0L;
+ filepos = 0L;
+ newfilepos = 0L;
+
+ playlist_fd = 0;
+
+ buffo = NULL;
+}
+
+Decoder::~Decoder() {
+ close();
+ free(buffo);
+ quit = true;
+}
+
+bool Decoder::init(char *dev) {
+ int len;
+
+ if(dummy) {
+
+ N("running in dummy test run - no device opened");
+
+ } else {
+
+ fd = ::open(dev, O_WRONLY|O_NDELAY,S_IWUSR|S_IWGRP|S_IWOTH);
+ if(fd<0) {
+ D("error opening device %s: %s",dev,strerror(errno));
+ return false;
+ }
+
+ }
+
+ // save the path to the device
+ strncpy(device, dev, MAXPATH);
+
+ // parse the last 2 cyphers of the device path
+ len = strlen(dev);
+ // last two chars of the device name are the number
+ device_num = atoi(&dev[len-2]);
+
+
+ return true;
+}
+
+bool Decoder::setup(bool *sync, int bufsize) {
+
+ // save the syncstarter flag
+ syncstart = sync;
+
+ if(buffo) free(buffo);
+
+ buffo = (uint8_t*) calloc( bufsize+1, 1024); // +1 safety bound
+ if(!buffo) {
+ E("fatal error: can't allocate %uKB of memory for decoder", bufsize);
+ return(false);
+ }
+
+ buffo_size = bufsize*1024;
+
+ quit = false;
+
+ return(true);
+
+}
+
+void Decoder::close() {
+ playing = false;
+ quit = true;
+
+ if(running) {
+ D("thread was running, waiting to join...");
+ join();
+ }
+ if(fd) ::close(fd);
+
+ position = -1;
+
+ // delete playlist entries
+ empty();
+
+
+}
+
+void Decoder::update() {
+ if(position<0) { // first time we play from the playlist
+
+ position = 1;
+
+ } else {
+
+ switch(playmode) {
+
+ case PLAY: // next
+
+ position++;
+
+ if( position > playlist.len() ) {
+ // stop();
+ position = 1;
+ }
+
+ break;
+
+ case CONT: // next or first if at the end
+
+ if( position >= (int)playlist.len() ) position = 1;
+ else position++;
+
+ break;
+
+ case LOOP:
+ // play the same again
+ break;
+
+ case RAND:
+ // play a random one
+ break;
+
+ case SINGLE:
+ stop();
+ break;
+
+ default:
+ stop();
+ // just stop
+ break;
+ }
+
+ }
+
+ // current movie is now in playlist[position];
+ if( ! playlist.len() ) D("playlist empty");
+ else {
+ D("current playlist at position %u:", position);
+ int c;
+ for(c=1; c<=playlist.len(); c++) {
+ D("%s[%u] %s",
+ (c==position)?"->":" ",
+ c, playlist[c]->name);
+ }
+ }
+
+ // refresh the GUI if present
+ if(gui) gui->refresh();
+
+}
+
+
+
+void Decoder::run() {
+ int in, written, writing;
+ uint8_t *buf;
+ Entry *movie;
+
+
+ if(!fd && !dummy) {
+ E("thread %u falling down: no device opened",pthread_self());
+ return;
+ }
+
+ running = true;
+
+ D("thread %u launched",pthread_self());
+
+// set max realtime priority
+// if( set_rtpriority(true) )
+// A("thread %u running on max realtime priority",pthread_self());
+
+ while(!quit) {
+
+ update();
+
+ if(quit) break;
+
+ if(stopped) {
+ stopped = false;
+ playing = false;
+ }
+
+ // if is not playing, sleep
+ while(!playing && !quit) jsleep(0,100);
+ ///////////////////////////
+
+ // check it out from the playlist
+ movie = playlist[position];
+ if(!movie) {
+ E("no movie at position %i", position);
+ playing = false;
+ continue;
+ }
+
+ // just to be sure
+ if(playlist_fd) fclose(playlist_fd);
+
+ // STAT METHOD FOR FILESIZE
+// // check if it exists and ackowledge filesize
+// if( stat(movie->name, &moviestat) < 0 ) {
+// E("error %u on movie %s: %s", errno, movie->name, strerror(errno));
+// if(errno == 75) // value too large for defined datatype
+// // we just deactivate seek and position handling
+// filesize = 0x0;
+// else
+// continue;
+// } else
+// filesize = moviestat.st_size;
+
+ playlist_fd = fopen64( movie->name, "rb" );
+ if(!playlist_fd) {
+ E("can't open %s: %s (%i)", movie->name, strerror(errno), errno);
+
+ if(errno==27) { // EOVERFLOW - file too large on Linux/Glibc
+
+ int tmpfd;
+ tmpfd = open( movie->name, O_RDONLY|O_LARGEFILE);
+
+ if(!tmpfd) {
+ E("failed opening with largefile support: %s",strerror(errno));
+ continue;
+ } else playlist_fd = fdopen( tmpfd , "rb" );
+
+ } else {
+ continue;
+ }
+ }
+
+ N("now playing %s",movie->name);
+
+ // read the total length of movie file
+ fseeko64(playlist_fd, 0L, SEEK_END);
+ filesize = ftello64(playlist_fd);
+ // set position at the beginning
+ filepos = 0L;
+ fseeko64(playlist_fd, filepos, SEEK_SET);
+ //////////////////////////////////////
+
+ // ??? or should we use:
+ // fgetpos(playlist_fd, &filesize);
+ // fsetpos(playlist_fd, &filepos);
+ // none seem to work with files > 2GB
+
+ if(filesize)
+ A("movie length: %lu KB",filesize/1024);
+ else
+ A("movie is too large to be seekable");
+
+ do { // inner reading loop
+
+ // update the GUI
+ if(gui) gui->refresh();
+
+ // process asynchronous flags
+ if(quit || stopped) break;
+ while(!playing && !quit) jsleep(0,100);
+ //////////////////////////////
+
+
+ // read in the data
+ in = fread(buffo, 1, buffo_size, playlist_fd);
+ if( feof(playlist_fd) || in<1 ) { // EOF
+ D("end of file %s",movie->name);
+ break;
+ }
+
+ written = 0;
+ writing = in;
+ buf = buffo;
+
+ if(!*syncstart) {
+ // unlock();
+ while(!*syncstart) jsleep(0,1); // check every nanosecond
+ }
+
+ while(writing) { // writing loop
+
+ buf += written;
+
+ // if(quit || stopped) break;
+
+ if(dummy) // emulation mode with no device (for devel)
+ written = writing;
+ else
+ written = ::write(fd, buf, writing);
+
+ if(written<0) // error on write
+ continue;
+ else
+ filepos += written;
+
+ writing -= written;
+
+ flush();
+
+ }
+
+ } while(in>0 && !quit); // read/write inner loop
+
+ clear();
+ // close the file playing
+ A("end of movie %s", movie->name);
+ if(playlist_fd) {
+ fclose(playlist_fd);
+ playlist_fd = 0;
+ }
+
+ } // run() thread loop
+
+ clear();
+ // close the file playing
+ if(playlist_fd) {
+ fclose(playlist_fd);
+ playlist_fd = 0;
+ }
+ A("quitting decoder for %s",device);
+
+ D("thread %u finished", pthread_self());
+ return;
+}
+
+void Decoder::flush() {
+ struct pollfd fdled;
+ fdled.fd = fd;
+ fdled.events=POLLOUT;
+
+ while( poll(&fdled,1,1000) < 1 ) { // wait infinite until ready
+ if(fdled.revents & POLLOUT) return;
+ else {
+ W("device %i still not ready for writing",fd);
+ if(quit) return;
+ }
+ }
+
+ // if there is a seek to do, do it now
+ if((newfilepos > 0L) && playlist_fd) {
+ D("seeking to new position %lu", newfilepos);
+ fseeko64(playlist_fd, newfilepos, SEEK_SET);
+ filepos = newfilepos;
+ newfilepos = 0;
+ }
+}
+
+bool Decoder::play() {
+ playing = true;
+ stopped = false;
+ return true;
+}
+
+bool Decoder::stop() {
+ // newfilepos = 1;
+ // clear();
+ // playing = false;
+ stopped = true;
+ return true;
+}
+
+bool Decoder::pause() {
+ playing = false;
+ return true;
+}
+
+bool Decoder::clear() {
+ if(!fd) return false;
+
+ // close the device
+ flush();
+
+ ::close(fd); // *BLANK*
+
+
+ // reopen the device again
+ fd = ::open(device, O_WRONLY|O_NDELAY,S_IWUSR|S_IWGRP|S_IWOTH);
+ if(fd<0) {
+ D("error opening device %s: %s",device,strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+
+int Decoder::getpos() {
+ // filesize : 100 = filepos : x
+ // filesize : filepos = 100 : x
+ int percent;
+
+ if(!playlist_fd) return 0;
+ if(!filesize) return 0;
+
+ percent = (int) ( (filepos * 100) / filesize );
+ D("movie %s at position %u %% (%lu byte)",
+ playlist[position]->name, percent, filepos);
+ return percent;
+}
+
+void Decoder::setpos(int pos) {
+ // filesize : 100 = x : pos
+
+ if(!playlist_fd) return;
+ if(!filesize) return;
+
+ newfilepos = (filesize * pos) / 100;
+
+ D("Decoder::setpos(%u) : newfilepos = %lu",
+ pos, newfilepos);
+}
+
+off64_t Decoder::getoffset() {
+ return filepos;
+}
+
+void Decoder::setoffset(off64_t pos) {
+ (pos < filesize) ? newfilepos = pos : newfilepos = filesize;
+}
+
+bool Decoder::prepend(char *file) {
+
+ Entry *ent = new Entry();
+
+ ent->set_name(file);
+
+ playlist.prepend( ent );
+
+ return true;
+}
+
+bool Decoder::append(char *file) {
+
+ Entry *ent = new Entry();
+
+ ent->set_name(file);
+
+ playlist.append( ent );
+
+ return true;
+}
+
+bool Decoder::insert(char *file, int pos) {
+ Entry *ent = new Entry();
+
+ ent->set_name(file);
+
+ playlist.insert( ent, pos );
+
+ return true;
+}
+
+/*
+bool Decoder::remove(char *file) {
+ A("TODO: Decoder::remove(char *file)");
+ return true;
+}
+*/
+bool Decoder::remove(int pos) {
+ Entry *ent;
+
+ ent = playlist[pos];
+
+ if(!ent) return false;
+
+ delete ent;
+
+ return true;
+}
+
+bool Decoder::empty() {
+ Entry *ent = playlist.begin();
+ while(ent) {
+ ent->rem();
+ delete ent;
+ ent = playlist.begin();
+ }
+ // playlist.clear()
+ position = 1;
+ return true;
+}
+
+static time_t now_epoch;
+static struct tm now;
+
+int playlist_selector(const struct dirent *dir) {
+ char today_str[32];
+
+ strftime(today_str,31,"%d%b%y",&now);
+
+ if( strstr(dir->d_name, today_str) )
+ return 1;
+
+ if( strstr(dir->d_name, "video") )
+ return 1;
+
+ return 0;
+}
+
+
+
+int Decoder::load() {
+ // load the playlist from the .ivysync/ directory
+ // renders a date string of today in the format of DDMMMYY-HHMM (12Aug)
+ // if the playlist .ivysync/DDMMM*-videoNN is there load that one
+ // otherwise fallback on the .ivysync/videoNN playlist
+ // if that is not even there then we don't have a playlist.
+ FILE *fd;
+ char path[512];
+ char line[1024];
+ int c = 0;
+ struct stat st;
+
+ struct tm pltime;
+ struct tm plseltime;
+
+ struct dirent **filelist;
+ int found;
+
+ char ThePlaylist[512];
+ char videodev[64];
+ char *home = getenv("HOME");
+
+ snprintf(path,511,"%s/.ivysync",home);
+ if( stat(path, &st) != 0) {
+ D("no saved playlists in %s: %s",path,strerror(errno));
+ return -1;
+ }
+
+ // when we are now
+ now_epoch = time(NULL);
+ localtime_r( &now_epoch, &now );
+ snprintf(videodev,63,"video%u",device_num);
+ // use the default playlist
+ snprintf(ThePlaylist,511,"video%u",device_num);
+
+ // scan the directory for scheduled playlists starting with date
+ found = scandir(path, &filelist, playlist_selector, alphasort);
+ if(found < 0) {
+ E("playlist scandir: %s",strerror(errno));
+ return -1;
+ }
+
+ // setup time selection of the latest playlist of today
+ memcpy(&plseltime, &now, sizeof(struct tm));
+ plseltime.tm_hour = 0;
+ plseltime.tm_min = 0;
+
+ // in filelist[] we have all playlists of today
+ // now need to sort out the ones that are not from this device
+ // and the ones that are in the future (hour and minutes)
+ while(found--) {
+
+ D("checking playlist for today %s", filelist[found]->d_name);
+ // eliminate the ones that are not for this device
+ if( ! strstr( filelist[found]->d_name, videodev ) ) continue;
+
+ // read and check the exact time on the filename
+ // in case the playlist filename doesn't starts with video*
+ if ( filelist[found]->d_name[0] == 'v') {
+
+ snprintf(ThePlaylist,511,"%s",filelist[found]->d_name);
+
+ } else {
+
+ get_time( filelist[found]->d_name, &pltime );
+
+ // skip if we already have a more recent playlist
+ if(plseltime.tm_hour > pltime.tm_hour) continue;
+ else if(plseltime.tm_hour == pltime.tm_hour)
+ if(plseltime.tm_min >= pltime.tm_min) continue;
+
+ if(now.tm_hour > pltime.tm_hour) {
+
+ D("this playlist is actual, we're going to use this");
+ snprintf(ThePlaylist,511,"%s",filelist[found]->d_name);
+ memcpy(&plseltime,&pltime,sizeof(struct tm));
+
+ } else if(now.tm_hour == pltime.tm_hour) {
+
+ // same hour, let's check the minutes
+ if(now.tm_min >= pltime.tm_min) {
+
+ D("this playlist is scheduled right now");
+ snprintf(ThePlaylist,511,"%s",filelist[found]->d_name);
+ memcpy(&plseltime,&pltime,sizeof(struct tm));
+
+ }
+ } else D("this playlist will be activated later");
+ }
+ }
+
+ snprintf(path,511,"%s/.ivysync/%s",home,ThePlaylist);
+
+ fd = fopen(path,"r");
+ if(!fd) {
+ E("can't load playlist %s: %s", path, strerror(errno));
+ return -1;
+ }
+
+ A("reading from playlist file %s",path);
+ while( fgets(line,1023,fd) ) {
+ if( feof(fd) ) break;
+
+ chomp(line);
+ if( append(line) ) {
+ c++;
+ D("%u+ %s",c,line);
+ }
+ }
+ fclose(fd);
+ return c;
+}
+
+int Decoder::save() {
+ FILE *fd;
+ char *home = getenv("HOME");
+ char path[512];
+ int c;
+ struct stat st;
+
+ vector<string>::iterator pl_iter;
+ string pl;
+
+ // create the configuration directory if doesn't exist
+ snprintf(path,511,"%s/.ivysync",home);
+ if( stat(path, &st) != 0) {
+ if(errno==ENOENT) mkdir(path,0744);
+ else {
+ E("error saving in %s: %s",path, strerror(errno));
+ return -1;
+ }
+ }
+
+ snprintf(path,511,"%s/.ivysync/video%u",home,device_num);
+ fd = fopen(path,"w+");
+ if(!fd) {
+ E("can't save to %s: %s", path, strerror(errno));
+ return -1;
+ }
+
+ D("saving to configuration file %s",path);
+ Entry *ent;
+ ent = playlist.begin();
+ c = 0;
+ while(ent) {
+ fputs(ent->name, fd);
+ fputs("\n",fd);
+ D("%u - %s", c, ent->name);
+ ent = ent->next;
+ c++;
+ }
+ fflush(fd);
+ fclose(fd);
+ return c;
+}
diff --git a/branches/lydia/decoder.h b/branches/lydia/decoder.h
new file mode 100644
index 0000000..dee8c59
--- /dev/null
+++ b/branches/lydia/decoder.h
@@ -0,0 +1,133 @@
+/* IvySync - Video SyncStarter
+ *
+ * (c) Copyright 2004 - 2006 Denis Rojo <jaromil@dyne.org>
+ * Nederlands Instituut voor Mediakunst
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#ifndef __DECODER_H__
+#define __DECODER_H__
+
+// for large file support (64bit file offsets)
+#define _LARGEFILE_SOURCE 1
+
+/* On many architectures both off_t and long are 32-bit types, but
+ compilation with #define _FILE_OFFSET_BITS 64 will turn off_t into
+ a 64-bit type. - see man fseeko(3) */
+#define _FILE_OFFSET_BITS 64
+
+
+#include <iostream>
+#include <pthread.h>
+#include <inttypes.h>
+
+#include <linklist.h>
+#include <thread.h>
+
+
+
+// playmode values
+#define PLAY 1
+#define CONT 2
+#define LOOP 3
+#define RAND 4
+#define SINGLE 5
+
+// maximum path lenght
+#define MAXPATH 512
+
+class Playlist; // graphical interface
+
+class Decoder : public Thread, public Entry {
+
+ public:
+ Decoder();
+ ~Decoder();
+
+ bool init(char *dev);
+
+ bool setup(bool *sync, int bufsize);
+
+ void close();
+
+ // playlist stuff
+ bool prepend(char *file); ///< prepend *file at the beginning of the playlist
+ bool append(char *file); ///< append *file at the end of the playlist
+ bool insert(char *file, int pos); ///< insert *file in playlist at pos
+ // bool remove(char *file); ///< remove the first occurrence of *file
+ bool remove(int pos); ///< remove the playlist entry at pos
+ bool empty(); ///< empty all the playlist discarding all entries
+
+ // save on file
+ int load();
+ int save();
+
+ int playmode; ///< PLAY, CONT, LOOP or RAND
+ int position; ///< current position in playlist (read-only)
+
+
+ bool play();
+ bool stop();
+ bool pause();
+ bool clear();
+
+ int getpos();
+ void setpos(int pos);
+
+ off64_t getoffset();
+ void setoffset(off64_t pos);
+
+ /** state flags for use in the inner loop
+ the following booleans are changed by asynchronous calls
+ then behaviour is synched and executed in the main loop */
+ bool playing;
+ bool stopped;
+
+ bool *syncstart;
+
+ char device[MAXPATH];
+ int device_num;
+
+ Linklist playlist;
+ Entry *current; ///< path of movie currently playing
+
+ Playlist *gui; ///< pointer to the GUI, NULL if none
+
+ bool dummy; // for dummy test run without devices
+
+ private:
+ void run();
+ void update();
+
+ void flush();
+
+ off64_t filesize; // current file playing, size in bytes
+ off64_t filepos; // current file playing, position in bytes
+ off64_t newfilepos; // new position to skip in file
+
+ int fd;
+ FILE *playlist_fd;
+
+// uint8_t buffo[CHUNKSIZE+1024]; // + 1k bound
+ uint8_t *buffo;
+ int buffo_size;
+
+};
+
+
+#endif
diff --git a/branches/lydia/gui.cpp b/branches/lydia/gui.cpp
new file mode 100644
index 0000000..6464e60
--- /dev/null
+++ b/branches/lydia/gui.cpp
@@ -0,0 +1,610 @@
+
+#include <iostream>
+#include <cstdio>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtk/gtk.h>
+
+#include <gui.h>
+#include <utils.h>
+
+
+#define PACKAGE_DATA_DIR "/usr/share/ivysync"
+#define CONFIGFILE "/home/jaromil/ivysyncrc"
+
+static GtkWindow *main_window;
+
+// playlist model columns
+enum {
+ POSITION,
+ FILENAME,
+ COLUMNS
+};
+
+/* DND stuff ripped from wolfpack.twu.net/docs/gtkdnd */
+#define DRAG_TAR_NAME_0 "text/plain"
+#define DRAG_TAR_INFO_0 0
+
+#define DRAG_TAR_NAME_1 "text/uri-list" /* not url-list */
+#define DRAG_TAR_INFO_1 1
+
+#define DRAG_TAR_NAME_2 "STRING"
+#define DRAG_TAR_INFO_2 2
+/* end */
+
+
+// CALLBACKS
+
+void on_add_button(GtkWidget *widget, gpointer *data) {
+ GtkWidget *dialog;
+ Playlist *pl = (Playlist*)data;
+
+ dialog = gtk_file_chooser_dialog_new ("Open File", main_window,
+ GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN,
+ GTK_RESPONSE_ACCEPT, NULL);
+
+ gtk_file_chooser_set_select_multiple((GtkFileChooser*)dialog, true);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ GSList *filenames;
+ filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog));
+ while(filenames) {
+ D("file choosen: %s",filenames->data);
+ pl->decoder->append((char*)filenames->data);
+ g_free(filenames->data);
+ filenames = filenames->next;
+ }
+ g_slist_free(filenames);
+ }
+
+ gtk_widget_destroy(dialog);
+ pl->refresh();
+}
+
+
+void on_save_button(GtkWidget *widget, gpointer *data) {
+ Playlist *pl = (Playlist*)data;
+ pl->decoder->save();
+}
+
+void on_syncstart_button(GtkWidget *widget, gpointer *data) {
+ vector<Playlist*>::iterator pl_iter;
+ Gui *gui = (Gui*)data;
+ Playlist *pl;
+
+ gui->syncstart = false;
+
+ for( pl_iter = gui->playlist.begin();
+ pl_iter != gui->playlist.end();
+ ++pl_iter) {
+ pl = *pl_iter;
+ pl->decoder->stop();
+ pl->decoder->position = 0;
+ pl->decoder->play();
+ pl->refresh();
+
+ }
+
+ jsleep(1,0);
+ gui->syncstart = true;
+
+}
+
+void on_play_button(GtkWidget *widget, gpointer *data) {
+ Playlist *pl = (Playlist*)data;
+ if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ if(!pl->decoder->running) pl->decoder->launch();
+ pl->decoder->play();
+ } else {
+ pl->decoder->stop();
+ }
+}
+
+void on_rewind_button(GtkWidget *widget, gpointer *data) {
+ Playlist *pl = (Playlist*)data;
+ pl->decoder->stop();
+ pl->decoder->position = 0;
+ pl->refresh();
+}
+
+void on_delete_button(GtkWidget *widget, gpointer *data) {
+ GtkTreeSelection *select;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GList *rowlist;
+ char *pos, *p;
+ int numpos;
+
+ Playlist *pl = (Playlist*)data;
+
+ // get out a rowlist of selections
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(pl->treeview));
+ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl->treeview));
+ rowlist = gtk_tree_selection_get_selected_rows(select, &model);
+
+ if(!rowlist) return;
+
+ rowlist = g_list_reverse(rowlist);
+ rowlist = g_list_first(rowlist);
+
+ // fill in filelist with all the selected FILENAME entries
+ if(rowlist->data) {
+
+ while(rowlist && rowlist->data) {
+
+ gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)rowlist->data);
+ gtk_tree_model_get(model, &iter, POSITION, &pos, -1);
+
+ // strip out the -> selection
+ for(p = pos; *p!='\0'; p++)
+ if(!isdigit(*p)) *p=' ';
+
+
+ numpos = atoi(pos);
+
+ D("removing playlist entry at pos %u",numpos);
+ pl->decoder->remove(numpos);
+
+ g_free(pos); // free the char*
+ gtk_tree_path_free((GtkTreePath*)rowlist->data); // free the path
+
+ rowlist = g_list_next(rowlist);
+ }
+ }
+
+ // free the rowlist
+ g_list_free(rowlist);
+
+ pl->refresh();
+}
+
+
+void on_set_playmode(GtkWidget *w, gpointer *data) {
+ Playlist *pl = (Playlist*)data;
+ gint mode;
+ if((mode = gtk_option_menu_get_history(GTK_OPTION_MENU(w))) != -1)
+ pl->decoder->playmode = mode+1;
+ D("playmode set to %u",mode+1);
+}
+
+void DND_begin(GtkWidget *w, GdkDragContext *dc, gpointer *data) { D("drag begin"); }
+void DND_end(GtkWidget *w, GdkDragContext *dc, gpointer *data) { D("drag end"); }
+
+gboolean DND_data_get(GtkWidget *w, GdkDragContext *dc,
+ GtkSelectionData *selection, guint info, guint t, gpointer *data) {
+
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *select;
+ // gint row=0;
+ gchar *title;
+ GList *rowlist;
+
+ Playlist *pl = (Playlist*)data;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(w));
+ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+ rowlist = gtk_tree_selection_get_selected_rows(select, &model);
+
+ rowlist = g_list_first(rowlist);
+ if(rowlist->data) {
+ // rowsrc = gtk_tree_path_get_indices((GtkTreePath *)rowlist->data)[0];
+ while(rowlist && rowlist->data) {
+ gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)rowlist->data);
+ gtk_tree_model_get(model, &iter, POSITION, &title, -1);
+
+ pl->draglist = g_list_append(pl->draglist, (void *) title);
+ D("dragging entry at position %s",title);
+ // gtk_tree_path_free((GtkTreePath *)rowlist->data);
+
+ rowlist = g_list_next(rowlist);
+ }
+ } else
+ return FALSE;
+
+ // free the rowlist
+ g_list_free(rowlist);
+
+ return TRUE;
+}
+
+gboolean DND_drop(GtkWidget *w, GdkDragContext *dc, gint x, gint y,
+ guint t, gpointer *data) {
+
+ GtkTreePath *path;
+ gint row =0;
+ char *p;
+ int numpos;
+ Playlist *pl = (Playlist*)data;
+
+ if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
+ x, y, &path, NULL, NULL, NULL)) {
+ row = gtk_tree_path_get_indices(path)[0];
+ D("DND drop on row = %d", row);
+ gtk_tree_path_free(path);
+ }
+
+ g_list_first(pl->draglist);
+ if(!pl->draglist || !pl->draglist->data) return FALSE;
+
+ while(pl->draglist) {
+
+ if(row) { // if there is a place where to move it
+
+ // strip out the -> selection and get the position
+ for(p = (char*)pl->draglist->data; *p!='\0'; p++)
+ if(!isdigit(*p)) *p=' ';
+ numpos = atoi((char*)pl->draglist->data);
+ D("move entry %u:%s into slot %u",numpos,
+ (char*)pl->decoder->playlist[numpos-1]->name,row+1);
+ numpos--;
+
+ if(row>numpos) row++;
+
+ pl->decoder->insert
+ ( (char*)pl->decoder->playlist[numpos]->name, row );
+
+ if(row<numpos) numpos++; // if moves up then the source shifts down one pos
+
+ pl->decoder->remove(numpos+1);
+
+ }
+
+ // g_free(pl->draglist->data);
+ pl->draglist = g_list_next(pl->draglist);
+ }
+
+ // g_list_free(pl->draglist);
+ // pl->draglist = NULL;
+
+ pl->refresh();
+ return TRUE;
+}
+
+gboolean DND_data_motion(GtkWidget *w, GdkDragContext *dc, gint x, gint y,
+ guint t, gpointer *data) {
+ gdk_drag_status(dc, GDK_ACTION_MOVE, t);
+ // D("drag_data_motion");
+ return FALSE;
+}
+
+
+
+////////////////// PLAYLIST WIDGET CLASS
+
+Playlist::Playlist(int num) {
+
+ selected = 0;
+ draglist = NULL;
+
+ widget = gtk_vbox_new (FALSE, 0);
+ snprintf(widget_name,255,"widget_%u",num);
+ gtk_widget_set_name (widget, widget_name);
+ gtk_widget_show (widget);
+
+ scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+ snprintf(scrolledwindow_name,255,"scrolledwindow_%u",num);
+ gtk_widget_set_name (scrolledwindow, scrolledwindow_name);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_widget_show (scrolledwindow);
+ gtk_box_pack_start (GTK_BOX (widget), scrolledwindow, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);
+
+ treeview = gtk_tree_view_new ();
+ snprintf(treeview_name,255,"treeview_%u",num);
+ gtk_widget_set_name (treeview, treeview_name);
+ gtk_widget_show (treeview);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow), treeview);
+
+ buttonbox = gtk_hbox_new (FALSE, 0);
+ snprintf(buttonbox_name,255,"buttonbox_%u",num);
+ gtk_widget_set_name (buttonbox, buttonbox_name);
+ gtk_widget_show (buttonbox);
+ gtk_box_pack_start (GTK_BOX (widget), buttonbox, FALSE, FALSE, 0);
+
+ add_button = gtk_button_new_from_stock ("gtk-add");
+ snprintf(add_button_name,255,"add_button_%u",num);
+ gtk_widget_set_name (add_button, add_button_name);
+ gtk_widget_show (add_button);
+ gtk_box_pack_start (GTK_BOX (buttonbox), add_button, FALSE, FALSE, 0);
+ g_signal_connect((gpointer)add_button, "pressed", G_CALLBACK(on_add_button), this);
+
+ delete_button = gtk_button_new_from_stock ("gtk-delete");
+ snprintf(delete_button_name,255,"delete_button_%u",num);
+ gtk_widget_set_name (delete_button, delete_button_name);
+ gtk_widget_show (delete_button);
+ gtk_box_pack_start (GTK_BOX (buttonbox), delete_button, FALSE, FALSE, 0);
+ g_signal_connect((gpointer)delete_button, "pressed", G_CALLBACK(on_delete_button), this);
+
+ /*
+ up_button = gtk_button_new ();
+ snprintf(up_button_name,255,"up_button_%u",num);
+ gtk_widget_set_name (up_button, up_button_name);
+ gtk_widget_show (up_button);
+ gtk_box_pack_start (GTK_BOX (buttonbox), up_button, FALSE, FALSE, 0);
+
+ image2 = gtk_image_new_from_stock ("gtk-go-up", GTK_ICON_SIZE_BUTTON);
+ snprintf(image2_name,255,"image2_%u",num);
+ gtk_widget_set_name (image2, image2_name);
+ gtk_widget_show (image2);
+ gtk_container_add (GTK_CONTAINER (up_button), image2);
+
+ down_button = gtk_button_new ();
+ snprintf(down_button_name,255,"down_button_%u",num);
+ gtk_widget_set_name (down_button, down_button_name);
+ gtk_widget_show (down_button);
+ gtk_box_pack_start (GTK_BOX (buttonbox), down_button, FALSE, FALSE, 0);
+
+ image3 = gtk_image_new_from_stock ("gtk-go-down", GTK_ICON_SIZE_BUTTON);
+ snprintf(image3_name,255,"image3_%u",num);
+ gtk_widget_set_name (image3, image3_name);
+ gtk_widget_show (image3);
+ gtk_container_add (GTK_CONTAINER (down_button), image3);
+ */
+
+ rewind_button = gtk_button_new();
+ snprintf(rewind_button_name,255,"rewind_button_%u",num);
+ gtk_widget_set_name(rewind_button, rewind_button_name);
+ gtk_widget_show(rewind_button);
+ gtk_box_pack_start(GTK_BOX(buttonbox), rewind_button, FALSE, FALSE, 0);
+ rewind_image = gtk_image_new_from_stock("gtk-media-previous", GTK_ICON_SIZE_BUTTON);
+ snprintf(rewind_image_name,255,"rewind_image_%u",num);
+ gtk_widget_set_name(rewind_image, rewind_image_name);
+ gtk_widget_show(rewind_image);
+ gtk_container_add(GTK_CONTAINER(rewind_button), rewind_image);
+ g_signal_connect((gpointer)rewind_button, "pressed", G_CALLBACK(on_rewind_button), this);
+
+ play_button = gtk_toggle_button_new_with_mnemonic ("gtk-media-play");
+ gtk_button_set_use_stock (GTK_BUTTON (play_button), TRUE);
+ snprintf(play_button_name,255,"play_button_%u",num);
+ gtk_widget_set_name (play_button, play_button_name);
+ gtk_widget_show (play_button);
+ gtk_box_pack_start (GTK_BOX (buttonbox), play_button, FALSE, FALSE, 0);
+ g_signal_connect((gpointer)play_button, "pressed", G_CALLBACK(on_play_button), this);
+
+ {
+ GtkWidget *tmpwid;
+ playmode_menu = gtk_menu_new();
+ tmpwid = gtk_menu_item_new_with_label("once");
+ gtk_menu_append(GTK_MENU(playmode_menu), tmpwid);
+ gtk_widget_show(tmpwid);
+ tmpwid = gtk_menu_item_new_with_label("loop");
+ gtk_menu_append(GTK_MENU(playmode_menu), tmpwid);
+ gtk_widget_show(tmpwid);
+ tmpwid = gtk_menu_item_new_with_label("continuous");
+ gtk_menu_append(GTK_MENU(playmode_menu), tmpwid);
+ gtk_widget_show(tmpwid);
+ gtk_widget_show(playmode_menu);
+
+ playmode_menuopt = gtk_option_menu_new();
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(playmode_menuopt), playmode_menu);
+ gtk_widget_show(playmode_menuopt);
+ gtk_box_pack_start (GTK_BOX (buttonbox), playmode_menuopt, TRUE, TRUE, 0);
+
+ g_signal_connect(G_OBJECT(playmode_menuopt), "changed", G_CALLBACK(on_set_playmode), this);
+ }
+
+ save_button = gtk_button_new_from_stock ("gtk-save");
+ snprintf(save_button_name,255,"save_button_%u",num);
+ gtk_widget_set_name (save_button, save_button_name);
+ gtk_widget_show (save_button);
+ gtk_box_pack_start (GTK_BOX (buttonbox), save_button, TRUE, TRUE, 0);
+ g_signal_connect((gpointer)save_button, "pressed", G_CALLBACK(on_save_button), this);
+
+ /*
+ statusbar = gtk_statusbar_new ();
+ snprintf(statusbar_name,255,"statusbar_%u",num);
+ gtk_widget_set_name (statusbar, statusbar_name);
+ gtk_widget_show (statusbar);
+ gtk_box_pack_start (GTK_BOX (widget), statusbar, FALSE, FALSE, 0);
+ */
+
+ // now setup the model view for the treeview
+ treestore = gtk_tree_store_new(COLUMNS,
+ G_TYPE_STRING, // position
+ G_TYPE_STRING); // name
+ gtk_tree_view_set_model((GtkTreeView*)treeview,GTK_TREE_MODEL(treestore));
+
+ { // then the selection handling
+ GtkTreeSelection *treeselect;
+ treeselect = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gtk_tree_selection_set_mode(treeselect, GTK_SELECTION_MULTIPLE);
+ // g_signal_connect(G_OBJECT(treeselect), "changed",
+ // G_CALLBACK(on_playlist_select), this);
+ }
+
+
+ { // then the drag and drop stuff
+ target_entry[0].target = DRAG_TAR_NAME_0;
+ target_entry[0].flags = 0;
+ target_entry[0].info = DRAG_TAR_INFO_0;
+
+ target_entry[1].target = DRAG_TAR_NAME_1;
+ target_entry[1].flags = 0;
+ target_entry[1].info = DRAG_TAR_INFO_1;
+
+ target_entry[2].target = DRAG_TAR_NAME_2;
+ target_entry[2].flags = 0;
+ target_entry[2].info = DRAG_TAR_INFO_2;
+
+ gtk_drag_source_set
+ (treeview,
+ (GdkModifierType) (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK),
+ target_entry, sizeof(target_entry) / sizeof(GtkTargetEntry),
+ (GdkDragAction)GDK_ACTION_MOVE );
+// g_signal_connect(G_OBJECT(treeview), "drag_motion", G_CALLBACK(DND_data_motion), this);
+ g_signal_connect(G_OBJECT(treeview), "drag_begin", G_CALLBACK(DND_begin), this);
+ g_signal_connect(G_OBJECT(treeview), "drag_end", G_CALLBACK(DND_end), this);
+ g_signal_connect(G_OBJECT(treeview), "drag_data_get", G_CALLBACK(DND_data_get), this);
+ g_signal_connect(G_OBJECT(treeview), "drag_drop", G_CALLBACK(DND_drop), this);
+// g_signal_connect(G_OBJECT(treeview), "drag_data_delete", G_CALLBACK(DND_data_delete), this);
+
+ gtk_drag_dest_set
+ (treeview,
+ // (GtkDestDefaults)(GTK_DEST_DEFAULT_ALL),
+ (GtkDestDefaults)(GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP),
+ target_entry, sizeof(target_entry) / sizeof(GtkTargetEntry),
+ (GdkDragAction)GDK_ACTION_MOVE );
+// g_signal_connect(G_OBJECT(treeview), "drag_data_received", G_CALLBACK(DND_data_received), this);
+
+ }
+
+ { // and finally the cell rendering
+ GtkCellRenderer *rend;
+ GtkTreeViewColumn *col;
+ rend = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(rend),"background","orange",NULL);
+ col = gtk_tree_view_column_new_with_attributes
+ ("Pos",rend,"text",POSITION,NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview),col);
+
+ rend = gtk_cell_renderer_text_new();
+ col = gtk_tree_view_column_new_with_attributes
+ ("Filename",rend,"text",FILENAME,NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview),col);
+ }
+}
+
+Playlist::~Playlist() {
+ gtk_widget_destroy(statusbar);
+ gtk_widget_destroy(save_button);
+ gtk_widget_destroy(image3);
+ gtk_widget_destroy(down_button);
+ gtk_widget_destroy(image2);
+ gtk_widget_destroy(up_button);
+ gtk_widget_destroy(delete_button);
+ gtk_widget_destroy(add_button);
+ gtk_widget_destroy(buttonbox);
+ gtk_widget_destroy(treeview);
+ gtk_widget_destroy(scrolledwindow);
+
+ gtk_widget_destroy(widget);
+
+}
+
+// read again the playlist from the decoder and fill it up
+int Playlist::refresh() {
+ GtkTreeIter iter; // static
+ Entry *ent;
+ char tmp[16];
+ int c;
+
+ gtk_tree_store_clear(treestore);
+
+ c = 0;
+ ent = decoder->playlist.begin();
+ while(ent) {
+ gtk_tree_store_append(treestore,&iter,NULL);
+
+ snprintf(tmp,15,"%s%u",
+ ((decoder->position+1)==c)?"->":" ",
+ c);
+
+ gtk_tree_store_set(treestore,&iter,
+ POSITION, tmp,
+ FILENAME, ent->name,
+ -1);
+
+ ent = ent->next;
+ c++;
+ }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////
+
+
+
+
+Gui::Gui() {
+
+ D("instantiating Gui class");
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (window, "window");
+ gtk_widget_set_size_request (window, 400, 300);
+ gtk_window_set_title (GTK_WINDOW (window), "IvySync Graphical Interface");
+ main_window = (GtkWindow*)window; // global static pointer
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (vbox, "main_vbox");
+ gtk_widget_show (vbox);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+
+ D("gtk_notebook_new");
+ notebook = gtk_notebook_new ();
+ gtk_widget_set_name (notebook, "notebook");
+ gtk_widget_show (notebook);
+ gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
+ //gtk_container_add (GTK_CONTAINER (vbox), notebook);
+
+ D("syncbutton");
+ syncbutton = gtk_toggle_button_new_with_mnemonic ("gtk-media-play");
+ gtk_button_set_use_stock (GTK_BUTTON (syncbutton), TRUE);
+ gtk_widget_set_name (syncbutton, "sync_button");
+ gtk_widget_show (syncbutton);
+ gtk_box_pack_start (GTK_BOX (vbox), syncbutton, FALSE, FALSE, 0);
+ g_signal_connect((gpointer)syncbutton, "pressed", G_CALLBACK(on_syncstart_button), this);
+
+ // QUAA
+ syncstart = true;
+}
+
+Gui::~Gui() {
+ gtk_widget_destroy(notebook);
+ gtk_widget_destroy(window);
+}
+
+bool Gui::init(Linklist *devices) {
+ Decoder *dec;
+ Playlist *pl;
+ int c;
+
+ c = 1;
+
+ D("Gui::init");
+
+ dec = (Decoder*)devices->begin();
+ while(dec) {
+
+ dec->syncstart = &syncstart;
+
+ D("new Playlist(%i)",c);
+ pl = new Playlist(c);
+
+ pl->decoder = dec; // store the decoder pointer in the playlist
+ dec->gui = pl; // store the GUI pointer in the decoder (cross reference)
+
+ D("refresh the playlist");
+ pl->refresh(); // refresh with the filenames
+ playlist.push_back(pl); // store the playlist in the gui array
+
+ D("add to the container");
+ gtk_container_add(GTK_CONTAINER(notebook), pl->widget);
+ char tmp[256];
+ snprintf(tmp,255,"/dev/video%u",dec->device_num);
+ gtk_notebook_set_tab_label_text ((GtkNotebook*)notebook,
+ gtk_notebook_get_nth_page((GtkNotebook*)notebook,
+ c-1), tmp);
+ dec = (Decoder*)dec->next;
+ c++;
+ }
+ return true;
+}
+void Gui::status(char *format, ...) {
+ // TODO status bar messages
+ return;
+}
+
+
+
+void Gui::start() {
+ g_signal_connect((gpointer)window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
+ gtk_widget_show(window);
+ gtk_main();
+}
diff --git a/branches/lydia/gui.h b/branches/lydia/gui.h
new file mode 100644
index 0000000..58de6db
--- /dev/null
+++ b/branches/lydia/gui.h
@@ -0,0 +1,96 @@
+
+#ifndef __GUI_H__
+#define __GUI_H__
+
+
+
+#include <decoder.h>
+#include <utils.h>
+
+#include <gtk/gtk.h>
+
+// damn STL
+#include <vector>
+using namespace std;
+using namespace __gnu_cxx;
+
+
+class Playlist {
+ public:
+ Playlist(int num);
+ ~Playlist();
+
+ int refresh();
+
+ GtkWidget *widget;
+
+ Decoder *decoder;
+
+ int selected;
+
+ GtkWidget *treeview;
+
+ GList *draglist;
+ bool draglock;
+
+ private:
+ // rendered widget names
+ char widget_name[256];
+ char scrolledwindow_name[256];
+ char treeview_name[256];
+ char buttonbox_name[256];
+ char add_button_name[256];
+ char delete_button_name[256];
+ char up_button_name[256];
+ char down_button_name[256];
+ char play_button_name[256];
+ char image2_name[256];
+ char image3_name[256];
+ char save_button_name[256];
+ char statusbar_name[256];
+ char rewind_button_name[256];
+ char rewind_image_name[256];
+
+ // widget pointers
+ GtkTreeStore *treestore;
+
+ GtkWidget *scrolledwindow;
+ GtkWidget *buttonbox;
+ GtkWidget *add_button;
+ GtkWidget *delete_button;
+ GtkWidget *up_button;
+ GtkWidget *image2;
+ GtkWidget *down_button;
+ GtkWidget *play_button;
+ GtkWidget *image3;
+ GtkWidget *save_button;
+ GtkWidget *statusbar;
+ GtkWidget *playmode_menu;
+ GtkWidget *playmode_menuopt;
+ GtkWidget *rewind_button;
+ GtkWidget *rewind_image;
+ GtkTargetEntry target_entry[3];
+};
+
+class Gui {
+ public:
+ Gui();
+ ~Gui();
+
+ bool init(Linklist *devices);
+ void start();
+
+ bool syncstart;
+
+ vector<Playlist*> playlist;
+
+ private:
+ void status(char *format, ...);
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *syncbutton;
+ GtkWidget *notebook;
+
+};
+
+#endif
diff --git a/branches/lydia/ivysync-gui.glade b/branches/lydia/ivysync-gui.glade
new file mode 100644
index 0000000..a993574
--- /dev/null
+++ b/branches/lydia/ivysync-gui.glade
@@ -0,0 +1,604 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+
+ <child>
+ <widget class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="playlist_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="buttonbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="add_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="delete_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-delete</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="up_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-up</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="down_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-down</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="save_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkStatusbar" id="statusbar">
+ <property name="visible">True</property>
+ <property name="has_resize_grip">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label2</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="button10">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button11">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-delete</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button12">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-up</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button13">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-down</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkStatusbar" id="statusbar3">
+ <property name="visible">True</property>
+ <property name="has_resize_grip">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label3</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="button15">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button16">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-delete</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button17">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-up</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button18">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image7">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-down</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button19">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkStatusbar" id="statusbar4">
+ <property name="visible">True</property>
+ <property name="has_resize_grip">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label4</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/branches/lydia/ivysync-remote.py b/branches/lydia/ivysync-remote.py
new file mode 100755
index 0000000..df0c2c6
--- /dev/null
+++ b/branches/lydia/ivysync-remote.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+# python xmlrpc client for ivysync
+# (C)2006 by Denis "Jaromil" Rojo
+# released under the GNU General Public License
+
+# general system functions
+import sys
+from string import atoi
+
+# xmlrpclib client functions
+from socket import gethostname
+from xmlrpclib import Transport, dumps
+
+
+
+
+
+######################################################################
+# XMLRPC Client connection class
+
+class xmlrpc_connection:
+ """The xmlrpc_connection class tests the xmlrpc_server. You must
+ download and install the medusa and xmlrpclib libraries to run
+ this code: http://www.nightmare.com http://www.pythonware.com"""
+
+ def __init__(self, host=None, port=2640):
+ if host is None:
+ host = gethostname()
+ self.host = "%s:%s" % (host, port)
+ self.transport = Transport()
+
+ def remote(self, method, params=()):
+ """remote invokes the server with the method name and an
+ optional set of parameters. The return value is always a
+ tuple."""
+
+ response = self.transport.request(self.host,
+ '/RPC2',
+ dumps(params, method))
+ return response
+
+###################################################################
+
+
+
+
+
+###################################################################
+## MAIN
+
+
+
+if __name__ == '__main__':
+
+ commands = ("Play", "Stop", "Open", "Pause", "Quit", "GetPos", "SetPos", "GetOffset", "SetOffset")
+
+ connection = xmlrpc_connection()
+
+# if(sys.argc):
+# cmd = "none"
+# else:
+ cmd = sys.argv[1]
+
+
+ if not cmd in commands:
+
+ print "no valid command recognized, list of valid commands:"
+ print commands
+ sys.exit(2)
+
+
+ if cmd == "Quit":
+
+ (res,) = connection.remote(cmd, (0, 0))
+ if res == 1:
+ answer = "Server is now quitting"
+ else:
+ answer = "Server refuses to quit"
+
+
+ elif cmd == "SetPos":
+
+ chan = int( sys.argv[2] )
+ pos = int( sys.argv[3] )
+
+ (res,) = connection.remote(cmd, (chan, pos))
+ answer = "Channel " + str(chan) + " is now at " + str(pos) + "%"
+
+ elif cmd == "SetOffset":
+ chan = int( sys.argv[2] )
+ pos = int( sys.argv[3] )
+
+ (res,) = connection.remote(cmd, (chan, pos))
+ answer = "Channel " + str(chan) + " is now at " + str(pos) + " byte offset"
+
+ elif cmd == "GetPos":
+
+ chan = int( sys.argv[2] )
+ (res,) = connection.remote(cmd, (chan, 0))
+ answer = "Channel " + str(chan) + " is now at " + str(res) + "%"
+
+ elif cmd == "GetOffset":
+
+ chan = int( sys.argv[2] )
+ (res,) = connection.remote(cmd, (chan, 0))
+ answer = "Channel " + str(chan) + " is now at " + str(res) + " byte offset"
+
+ elif cmd == "Open":
+
+ chan = int(sys.argv[2] )
+ path = str(sys.argv[3] )
+ (res,) = connection.remote(cmd, (chan, path))
+ if res == 1:
+ answer = "File " + path + " opened on channel " + str(chan)
+ else:
+ answer = "Error opening file " + path
+
+ else: # all other commands needing only one argument (channel num)
+
+ chan = int( sys.argv[2] )
+
+ (res,) = connection.remote(cmd, (chan, 0))
+ if res == 1:
+ answer = "Command '" + cmd + "' succesfully executed on channel " + str(chan)
+ else:
+ answer = "Error executing command '" + cmd + "' on channel " + str(chan)
+
+ print answer # 42! ;D
diff --git a/branches/lydia/ivysync-xmlrpc-spec.odt b/branches/lydia/ivysync-xmlrpc-spec.odt
new file mode 100644
index 0000000..4be2dbb
--- /dev/null
+++ b/branches/lydia/ivysync-xmlrpc-spec.odt
Binary files differ
diff --git a/branches/lydia/linklist.cpp b/branches/lydia/linklist.cpp
new file mode 100644
index 0000000..4c6d1b4
--- /dev/null
+++ b/branches/lydia/linklist.cpp
@@ -0,0 +1,450 @@
+/* C++ Linked list class, threadsafe (boolean is atom)
+ *
+ * (c) Copyright 2001-2004 Denis Roio aka jaromil <jaromil@dyne.org>
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * "$Id: linklist.cpp 274 2005-05-04 13:29:10Z kysucix $"
+ *
+ -------------------------------------------------------------------------
+ linked list container class
+
+ NOTE: add and rem don't take care of deleting pointers
+ that has to be done by the process that creates them and
+ knows which inheriting class they are (delete is done in main)
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils.h>
+#include <linklist.h>
+
+
+Linklist::Linklist() {
+ length = 0;
+ first = NULL;
+ last = NULL;
+#ifdef THREADSAFE
+ pthread_mutex_init(&mutex,NULL);
+#endif
+
+// unlock();
+}
+
+Linklist::~Linklist() {
+ clear();
+}
+
+/* adds one element at the end of the list */
+void Linklist::append(Entry *addr) {
+ Entry *ptr = NULL;
+ if(!addr) {
+ W("Linklist::append called with NULL argument");
+ return;
+ }
+ if(addr->list) addr->rem();
+#ifdef THREADSAFE
+ lock();
+#endif
+
+ if(!last) { /* that's the first entry */
+ last = addr;
+ last->next = NULL;
+ last->prev = NULL;
+ first = last;
+ } else { /* add the entry to the end */
+ ptr = last;
+ ptr->next = addr;
+ addr->next = NULL;
+ addr->prev = ptr;
+ last = addr;
+ }
+ /* save the pointer to this list */
+ addr->list = this;
+ length++;
+#ifdef THREADSAFE
+ unlock();
+#endif
+}
+
+void Linklist::prepend(Entry *addr) {
+ Entry *ptr = NULL;
+ if(addr->list) addr->rem();
+#ifdef THREADSAFE
+ lock();
+#endif
+
+ if(!first) { /* that's the first entry */
+ first = addr;
+ first->next = NULL;
+ first->prev = NULL;
+ last = first;
+ } else { /* add an entry to the beginning */
+ ptr = first;
+ ptr->prev = addr;
+ addr->next = ptr;
+ addr->prev = NULL;
+ first = addr;
+ }
+ addr->list = this;
+ length++;
+#ifdef THREADSAFE
+ unlock();
+#endif
+}
+
+
+// inserts an element after the given one
+void Linklist::insert_after(Entry *addr, Entry *pos) {
+
+ // take it out from other lists
+ if(addr->list) addr->rem();
+
+#ifdef THREADSAFE
+ lock();
+#endif
+ if(pos->next) {
+ pos->next->prev = addr;
+ addr->next = pos->next;
+ } else last = addr; // it's the last
+
+ addr->prev = pos;
+ pos->next = addr;
+
+ length++;
+ addr->list = this;
+
+#ifdef THREADSAFE
+ unlock();
+#endif
+}
+
+
+/* adds an element at the position specified
+ if pos is out of bounds adds it at the beginning or the end
+ the element occupying allready the position slides down
+ THIS FUNCTION IS NOT YET RELIABLE
+*/
+void Linklist::insert(Entry *addr, int pos) {
+ if(length<=pos) { /* adds it at the end */
+ append(addr);
+ return;
+ } else if(pos<=1) {
+ prepend(addr);
+ return;
+ }
+
+ if(addr->list) addr->rem();
+
+ Entry *ptr = pick(pos);
+
+#ifdef THREADSAFE
+ lock();
+#endif
+ ptr->prev->next = addr;
+ addr->prev = ptr->prev;
+
+ ptr->prev = addr;
+ addr->next = ptr;
+
+ length++;
+ addr->list = this;
+#ifdef THREADSAFE
+ unlock();
+#endif
+}
+
+/* clears the list
+ i don't delete filters here because they have to be deleted
+ from the procedure creating them. so this call simply discards
+ the pointers stored into the linked list. OBJECTS ARE NOT FREED */
+void Linklist::clear() {
+#ifdef THREADSAFE
+ lock();
+#endif
+ sel(0);
+ length = 0;
+ first = NULL;
+ last = NULL;
+#ifdef THREADSAFE
+ unlock();
+#endif
+}
+
+/* takes one element from the list
+ === STARTING FROM 1 ===
+ returns NULL if called with pos=0 or pos>length
+ returns Entry pointer otherwise
+ this function is then overloading the operator[]
+*/
+Entry *Linklist::pick(int pos) {
+ if((length<pos)||(pos<1)) return(NULL);
+ if(pos==1) return(first);
+ if(pos==length) return(last);
+
+ Entry *ptr = first;
+ register int c;
+ for(c=1;c<pos;c++) {
+ if(!ptr) break;
+ ptr = ptr->next;
+ }
+
+ return(ptr);
+}
+
+/* search the linklist for the entry matching *name
+ returns the Entry* on success, NULL on failure */
+Entry *Linklist::search(char *name) {
+ Entry *ptr = first;
+ while(ptr) {
+ if( strcasecmp(ptr->name,name)==0 ) break;
+ ptr = ptr->next;
+ }
+ return(ptr);
+}
+/* searches all the linklist for entries starting with *needle
+ returns a list of indexes where to reach the matches */
+int *Linklist::completion(char *needle) {
+ register int c;
+ int found;
+ int len = strlen(needle);
+
+ /* cleanup */
+ memset(compbuf,0,MAX_COMPLETION);
+
+ /* check it */
+ Entry *ptr = first;
+ if(!ptr) return compbuf;
+
+ for( found=0, c=1 ; ptr ; c++ , ptr=ptr->next ) {
+ if(!len) { // 0 lenght needle: return the full list
+ compbuf[found] = c;
+ found++;
+ } else if( strncasecmp(needle,ptr->name,len)==0 ) {
+ compbuf[found] = c;
+ found++;
+ }
+ }
+
+ D("completion found %i hits",found);
+ return compbuf;
+}
+
+
+/* this function is a wrapper around Entry::up()
+ better to use that if you have a pointer to your Entry */
+bool Linklist::moveup(int pos) {
+ Entry *p = pick(pos);
+ if(!p) return(false);
+ return( p->up() );
+}
+bool Linklist::movedown(int pos) {
+ Entry *p = pick(pos);
+ if(!p) return(false);
+ return( p->down() );
+}
+bool Linklist::moveto(int num, int pos) {
+ Entry
+ *p = pick(num);
+ if(!p) return(false);
+ return( p->move(pos) );
+}
+/* removes one element from the list */
+void Linklist::rem(int pos) {
+ Entry *ptr = pick(pos);
+ if(ptr==NULL) return;
+ ptr->rem();
+}
+
+/* selects ONLY ONE, deselects the others
+ use Entry::sel() if you want to do multiple selects */
+void Linklist::sel(int pos) {
+ int c;
+ Entry *ptr = last;
+
+ if(pos>length) return;
+
+ for(c=length;c>0;c--) {
+ if(c==pos) ptr->sel(true);
+ else ptr->sel(false);
+ ptr = ptr->prev;
+ }
+}
+
+/* returns the last one selected
+ this is supposed to be used with single selections */
+Entry *Linklist::selected() {
+ int c;
+ Entry *ptr = last;
+ for(c=length;c>0;c--) {
+ if(ptr->select) return ptr;
+ ptr = ptr->prev;
+ }
+ return NULL;
+}
+
+Entry::Entry() {
+ next = NULL;
+ prev = NULL;
+ list = NULL;
+ data = NULL;
+ select = false;
+ strcpy(name,"noname");
+}
+
+Entry::~Entry() {
+ rem();
+ if(data) free(data);
+}
+
+void Entry::set_name(char *nn) {
+ strncpy(name,nn,MAX_ENTRY_NAME-1);
+}
+
+bool Entry::up() {
+ if(!prev || !list) return(false);
+#ifdef THREADSAFE
+ list->lock();
+#endif
+
+ Entry *tprev = prev,
+ *tnext = next,
+ *pp = prev->prev;
+
+ if(!next)
+ list->last = prev;
+
+ if(tnext)
+ tnext->prev = tprev;
+
+ next = tprev;
+ prev = pp;
+ tprev->next = tnext;
+ tprev->prev = this;
+
+ if(pp)
+ pp->next = this;
+
+ if(!prev)
+ list->first = this;
+
+#ifdef THREADSAFE
+ list->unlock();
+#endif
+ return(true);
+}
+
+bool Entry::down() {
+ if(!next || !list) return(false);
+#ifdef THREADSAFE
+ list->lock();
+#endif
+
+ Entry *tprev = prev,
+ *tnext = next,
+ *nn = next->next;
+
+ if(!prev)
+ list->first = next;
+
+ if(tprev)
+ tprev->next = tnext;
+
+ prev = tnext;
+ next = nn;
+ tnext->prev = tprev;
+ tnext->next = this;
+ if(nn)
+ nn->prev = this;
+
+ if(!next)
+ list->last = this;
+
+#ifdef THREADSAFE
+ list->unlock();
+#endif
+ return(true);
+}
+
+bool Entry::move(int pos) {
+ D("Entry::move(%i) - NEW LINKLIST MOVE, TRYING IT...");
+ if(!list) return(false);
+#ifdef THREADSAFE
+ list->lock();
+#endif
+
+ Entry *tn, *tp;
+
+ Entry *swapping = list->pick(pos);
+ if(swapping == this) return(true);
+ if(!swapping) return(false);
+
+ tn = swapping->next;
+ tp = swapping->prev;
+
+ swapping->next = next;
+ swapping->prev = prev;
+ if(next) next->prev = swapping;
+ else list->last = swapping;
+ if(prev) prev->next = swapping;
+ else list->first = swapping;
+
+ next = tn;
+ prev = tp;
+ if(next) next->prev = this;
+ else list->last = this;
+ if(prev) prev->next = this;
+ else list->first = this;
+
+#ifdef THREADSAFE
+ list->unlock();
+#endif
+ D("LINKLIST MOVE RETURNS SUCCESS");
+
+ return(true);
+}
+
+void Entry::rem() {
+ bool lastone = false;
+ if(!list) return;
+#ifdef THREADSAFE
+ list->lock();
+#endif
+
+ if(next) { // if there is a next
+ next->prev = prev; // link it to the previous
+ next->select = select; // inherit selection
+ } else {
+ list->last = prev; // else just make it the last
+ lastone = true;
+ }
+
+ if(prev) { // if there is a previous
+ prev->next = next; // link it to the next
+ if(lastone) prev->select = select;
+ } else list->first = next; // else just make it a first
+
+ list->length--;
+#ifdef THREADSAFE
+ list->unlock();
+#endif
+
+ list = NULL;
+}
+
+void Entry::sel(bool on) {
+ select = on;
+}
diff --git a/branches/lydia/linklist.h b/branches/lydia/linklist.h
new file mode 100644
index 0000000..db030e5
--- /dev/null
+++ b/branches/lydia/linklist.h
@@ -0,0 +1,110 @@
+/* C++ Linked list class, threadsafe (boolean is atom)
+ *
+ * (c) Copyright 2001 Denis Roio aka jaromil <jaromil@dyne.org>
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __linklist_h__
+#define __linklist_h__
+
+
+// uncomment to have mutex locked operations
+// can be slow on OSX and adds dependency to pthreads
+//#define THREADSAFE 1
+#ifdef THREADSAFE
+#include <pthread.h>
+#endif
+
+// maximum lenght of names
+#define MAX_ENTRY_NAME 512
+
+// maximum number of members returned by the completion
+#define MAX_COMPLETION 256
+
+class Entry;
+
+class Linklist {
+ public:
+ Linklist();
+ virtual ~Linklist();
+
+ Entry *begin() { return(first); };
+ Entry *end() { return(last); };
+ int len() { return(length); };
+
+ void append(Entry *addr);
+ void add(Entry *addr) { append(addr); }; /* lazy backward compatibility */
+ void prepend(Entry *addr);
+ void insert(Entry *addr, int pos);
+ void insert_after(Entry *addr, Entry *pos);
+ void rem(int pos);
+ void sel(int pos);
+ void clear();
+ bool moveup(int pos);
+ bool movedown(int pos);
+ bool moveto(int num, int pos);
+ Entry *pick(int pos);
+ Entry *search(char *name);
+ int *completion(char *needle);
+
+ Entry *Linklist::selected();
+
+ Entry *operator[](int pos) { return pick(pos); };
+
+ /* don't touch these directly */
+ Entry *first;
+ Entry *last;
+ int length;
+
+
+#ifdef THREADSAFE
+ pthread_mutex_t mutex;
+ void lock() { pthread_mutex_lock(&mutex); };
+ void unlock() { pthread_mutex_unlock(&mutex); };
+#endif
+
+ int compbuf[MAX_COMPLETION]; // maximum completion listsize allowed
+
+};
+
+class Entry {
+ friend class Linklist;
+
+ public:
+ Entry();
+ ~Entry();
+
+ void set_name(char *nn);
+
+ Entry *next;
+ Entry *prev;
+
+ Linklist *list;
+
+ bool up();
+ bool down();
+ bool move(int pos);
+ void rem();
+ void sel(bool on);
+
+ bool select;
+
+ char name[MAX_ENTRY_NAME];
+
+ void *data; ///< generic data pointer
+};
+
+#endif
diff --git a/branches/lydia/thread.cpp b/branches/lydia/thread.cpp
new file mode 100644
index 0000000..fa1c0ef
--- /dev/null
+++ b/branches/lydia/thread.cpp
@@ -0,0 +1,46 @@
+
+#include <thread.h>
+#include <utils.h>
+
+static void *kickoff(void *arg) {
+ ((Thread*)arg)->run();
+ return NULL;
+}
+
+Thread::Thread() {
+ if(pthread_mutex_init(&mutex,NULL) == -1)
+ E("error initializing thread mutex");
+ if(pthread_attr_init(&attr) == -1)
+ E("error initializing thread attribute");
+
+ pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
+
+ running = false;
+ quit = false;
+}
+
+Thread::~Thread() {
+ if(pthread_mutex_destroy(&mutex) == -1)
+ E("error destroying thread mutex");
+ if(pthread_attr_destroy(&attr) == -1)
+ E("error destroying thread attribute");
+}
+
+bool Thread::launch() {
+ lock(); // the runner will unlock when ready
+ return pthread_create(&thread,&attr,&kickoff, this);
+ lock(); // wait until the thread is ready
+ unlock();
+}
+
+void Thread::lock() {
+ pthread_mutex_lock(&mutex);
+}
+
+void Thread::unlock() {
+ pthread_mutex_unlock(&mutex);
+}
+
+void Thread::join() {
+ pthread_join(thread,NULL);
+}
diff --git a/branches/lydia/thread.h b/branches/lydia/thread.h
new file mode 100644
index 0000000..8c8a4bf
--- /dev/null
+++ b/branches/lydia/thread.h
@@ -0,0 +1,47 @@
+/* Pthread handling class
+ * (c) Copyright 2001 - 2006 Denis Rojo <jaromil@dyne.org>
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pthread.h>
+
+#ifndef __THREAD_H__
+#define __THREAD_H__
+
+class Thread {
+
+ public:
+ Thread();
+ virtual ~Thread();
+
+ bool launch();
+ bool running;
+ bool quit;
+
+ virtual void run() =0;
+
+ void lock();
+ void unlock();
+ void join();
+
+ private:
+
+ pthread_t thread;
+ pthread_attr_t attr;
+ pthread_mutex_t mutex;
+};
+
+#endif
diff --git a/branches/lydia/utils.cpp b/branches/lydia/utils.cpp
new file mode 100644
index 0000000..bfda08d
--- /dev/null
+++ b/branches/lydia/utils.cpp
@@ -0,0 +1,168 @@
+/* Generic utils
+ * (c) Copyright 2001-2006 Denis Roio aka jaromil <jaromil@dyne.org>
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <iostream>
+#include <string>
+#include <cstdio>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <utils.h>
+
+#define MAX_DEBUG 3
+
+#define FUNC 2 // se il debug level e' questo ci sono le funzioni chiamate
+#define WARN 1 // se il debug level e' >= a questo ci sono i warning
+
+static char msg[255];
+
+static int verbosity = 0;
+
+void set_debug(int lev) {
+ lev = lev<0 ? 0 : lev;
+ lev = lev>MAX_DEBUG ? MAX_DEBUG : lev;
+ verbosity = lev;
+}
+
+int get_debug() {
+ return(verbosity);
+}
+
+void N(char *format, ...) {
+ va_list arg;
+ va_start(arg, format);
+
+ vsnprintf(msg, 254, format, arg);
+ fprintf(stderr,"[*] %s\n",msg);
+
+ va_end(arg);
+}
+
+void D(char *format, ...) {
+ if(verbosity>=FUNC) {
+ va_list arg;
+ va_start(arg, format);
+
+ vsnprintf(msg, 254, format, arg);
+ fprintf(stderr,"[F] %s\n",msg);
+
+ va_end(arg);
+ }
+}
+
+void E(char *format, ...) {
+ va_list arg;
+ va_start(arg, format);
+
+ vsnprintf(msg, 254, format, arg);
+ fprintf(stderr,"[!] %s\n",msg);
+
+ va_end(arg);
+}
+
+void A(char *format, ...) {
+ va_list arg;
+ va_start(arg, format);
+
+ vsnprintf(msg, 254, format, arg);
+ fprintf(stderr," . %s\n",msg);
+
+ va_end(arg);
+}
+
+void W(char *format, ...) {
+ if(verbosity>=WARN) {
+ va_list arg;
+ va_start(arg, format);
+
+ vsnprintf(msg, 254, format, arg);
+ fprintf(stderr,"[W] %s\n",msg);
+
+ va_end(arg);
+ }
+}
+
+void jsleep(int sec, long nsec) {
+ struct timespec timelap;
+ timelap.tv_sec = sec;
+ timelap.tv_nsec = nsec;
+ nanosleep(&timelap,NULL);
+}
+
+// jaromil's chomp
+#define MAX_CHOMP_SIZE 1024
+void chomp(char *str) {
+ size_t len; //, ilen;
+ char tmp[MAX_CHOMP_SIZE], *p = str;
+
+ memset(tmp,'\0',MAX_CHOMP_SIZE);
+
+ /* eliminate space and tabs at the beginning */
+ while (*p == ' ' || *p == '\t') p++;
+ strncpy(tmp, p, MAX_CHOMP_SIZE);
+
+ /* point *p at the end of string */
+ len = strlen(tmp);
+ p = &tmp[len-1];
+
+ while ((*p == ' ' || *p == '\t' || *p == '\n') && len) {
+ *p = '\0'; p--; len--;
+ }
+
+ strncpy(str, tmp, MAX_CHOMP_SIZE);
+}
+
+void get_time(char *f, struct tm *tt) {
+ strptime(f,"%d%b%y-%H%M",tt);
+ return;
+}
+
+char *mark_time() {
+ static char dm[32];
+ struct tm *tm;
+ time_t now;
+
+ now = time(NULL);
+ tm = localtime(&now);
+ strftime(dm,31,"%d%b%y-%H%M",tm);
+ return(dm);
+}
+
+#ifdef linux
+#include <sched.h>
+/* sets the process to "policy" policy, if max=1 then set at max priority,
+ else use min priority */
+
+bool set_rtpriority(bool max) {
+ struct sched_param schp;
+ // set the process to realtime privs
+
+ memset(&schp, 0, sizeof(schp));
+
+ if(max)
+ schp.sched_priority = sched_get_priority_max(SCHED_RR);
+ else
+ schp.sched_priority = sched_get_priority_min(SCHED_RR);
+
+ if (sched_setscheduler(0, SCHED_RR, &schp) != 0)
+ return false;
+ else
+ return true;
+}
+#endif
diff --git a/branches/lydia/utils.h b/branches/lydia/utils.h
new file mode 100644
index 0000000..30684bd
--- /dev/null
+++ b/branches/lydia/utils.h
@@ -0,0 +1,44 @@
+/* Generic utils
+ * (c) Copyright 2001-2006 Denis Roio aka jaromil <jaromil@dyne.org>
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <inttypes.h>
+
+void set_debug(int lev);
+int get_debug();
+
+void N(char *format, ...);
+void A(char *format, ...);
+void W(char *format, ...);
+void E(char *format, ...);
+void D(char *format, ...);
+
+void jsleep(int sec, long nsec);
+
+void chomp(char *str);
+
+char *mark_time();
+void get_time(char *f, struct tm *tt);
+
+bool set_rtpriority(bool max);
+
+#endif
+
+
diff --git a/branches/lydia/xmlrpc++/Doxyfile b/branches/lydia/xmlrpc++/Doxyfile
new file mode 100644
index 0000000..2d8a346
--- /dev/null
+++ b/branches/lydia/xmlrpc++/Doxyfile
@@ -0,0 +1,1041 @@
+# Doxyfile 1.3-rc3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = XmlRpc++
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.7
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ../doc
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = base64.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output dir.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse the
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/branches/lydia/xmlrpc++/Makefile b/branches/lydia/xmlrpc++/Makefile
new file mode 100644
index 0000000..bc8080a
--- /dev/null
+++ b/branches/lydia/xmlrpc++/Makefile
@@ -0,0 +1,34 @@
+# makefile written for gnu make
+CXX = g++
+CXXFLAGS = -Wall -ggdb
+VERSION = 0.8
+LIB = ./libxmlrpc++.a
+
+# Add your system-dependent network libs here. These are
+# only used to build the tests (your application will need them too).
+# Linux: none
+# Solaris: -lsocket -lnsl
+#SYSTEMLIBS = -lsocket -lnsl
+SYSTEMLIBS =
+LDLIBS = $(LIB) $(SYSTEMLIBS)
+
+OBJ = XmlRpcClient.o XmlRpcDispatch.o \
+ XmlRpcServer.o XmlRpcServerConnection.o \
+ XmlRpcServerMethod.o XmlRpcSocket.o XmlRpcSource.o \
+ XmlRpcUtil.o XmlRpcValue.o
+
+all: $(LIB)
+
+$(LIB): $(OBJ)
+ $(AR) $(ARFLAGS) $(LIB) $(OBJ)
+
+doc doxygen:
+ doxygen Doxyfile
+
+distclean: clean
+
+clean:
+ rm -f *.o
+ rm -f *~
+ rm -f $(LIB)
+
diff --git a/branches/lydia/xmlrpc++/README b/branches/lydia/xmlrpc++/README
new file mode 100644
index 0000000..c5cb7d1
--- /dev/null
+++ b/branches/lydia/xmlrpc++/README
@@ -0,0 +1,102 @@
+ XmlRpc++ Library
+
+ This is version 0.7 of XmlRpc++, an implementation of the [1]XmlRpc
+ protocol written in C++, based upon Shilad Sen's excellent
+ [2]py-xmlrpc library. XmlRpc++ is designed to make it easy to
+ incorporate XmlRpc client and server support into C++ applications. Or
+ use both client and server objects in your app for easy peer-to-peer
+ support.
+
+ Features
+
+ * Easy This library is easy to incorporate into C++ applications.
+ No other libraries are required, other than your system's socket
+ libraries. Simple XML parsing and HTTP support are built in.
+ * Fast All IO is non-blocking, so a slow client or network will
+ not slow down the server.
+ * Portable Written in standard C++ to the POSIX and Windows sockets
+ APIs. You do need a fairly recent compiler (g++ 3.1 or MSVC++ .Net
+ or MSVC++ 6 with the [3]STL patches.)
+ * Free This library is released under the [4]GNU [5]LGPL.
+
+
+ Changes
+
+ * Better handling of fault responses: server methods can throw an
+ XmlRpcException to return a fault and XmlRpcClient has a new
+ method to test whether the last response was a fault.
+ * Support for system.listMethods and system.methodHelp from the
+ introspection API.
+ * Support for system.multicall to process multiple requests in a
+ single transaction.
+ * Fixed a problem in the XmlRpcServer destructor (it should not have
+ been deleting the methods).
+ * The server ensures a valid result value is returned even if the
+ method does not set the result. The default result is an empty
+ string.
+ * Doxygen comments in header files and a doc target in the makefile.
+
+
+ Installation
+
+ There are VC++ 6 and VC++ .Net project files building on Windows. If
+ you are using VC++ 6, you should apply SP3 and the fixes at
+ [6]http://www.dinkumware.com/vc_fixes.html. Be sure to set the
+ appropriate code generation switches. In particular, ensure that the
+ runtime library (single/multi-threaded, static library/DLL) used is
+ the same for the XmlRpc++ code and whatever application it will be
+ linked to.
+
+ For Linux, Solaris, and other Unix-like platforms there is a GNU
+ Makefile which can be edited to suit your system. Specify your C++
+ compiler, compiler flags, and your system's socket libraries.
+
+ In the test directory there are various test programs that are built
+ by default. To verify that the library built correctly, you can start
+ the HelloServer example:
+HelloServer 8000
+
+ and the HelloClient example in another terminal window:
+HelloClient localhost 8000
+
+ You should see two Hello messages and a sum displayed (amongst a bunch
+ of debug output). You can also try the XML server validator program
+ (eg, "Validator 80") and then attempt to connect to it from
+ [7]http://validator.xmlrpc.com (if you have access to the internet and
+ are not behind a firewall etc).
+
+ Author
+
+ [8]Chris Morley
+
+ Although no code was re-used, the design and structure of the library
+ is based upon the py-xmlrpc library implementation.
+ The base64 decoder/encoder is by [9]Konstantin Pilipchuk.
+
+ License
+
+ A full copy of the LGPL license is included in the file COPYING. The
+ source code is Copyright (c) 2002-2003 by Chris Morley. This library
+ is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at
+ your option) any later version. This library is distributed in the
+ hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the GNU Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA
+
+References
+
+ 1. http://www.xmlrpc.org/
+ 2. http://py-xmlrpc.sourceforge.net/
+ 3. http://www.dinkumware.com/vc_fixes.html
+ 4. http://www.gnu.org/
+ 5. http://www.gnu.org/copyleft/lesser.html
+ 6. http://www.dinkumware.com/vc_fixes.html
+ 7. http://validator.xmlrpc.com/
+ 8. mailto:cmorley@users.sourceforge.net
+ 9. mailto:lostd@ukr.net
diff --git a/branches/lydia/xmlrpc++/XmlRpc.h b/branches/lydia/xmlrpc++/XmlRpc.h
new file mode 100644
index 0000000..aa761f9
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpc.h
@@ -0,0 +1,97 @@
+#ifndef _XMLRPC_H_
+#define _XMLRPC_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+//
+// The XmlRpc++ home page is http://xmlrpcpp.sourceforge.net/
+// My home page is http://www.vermontel.net/~cmorley/
+//
+
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+#include "XmlRpcClient.h"
+#include "XmlRpcException.h"
+#include "XmlRpcServer.h"
+#include "XmlRpcServerMethod.h"
+#include "XmlRpcValue.h"
+#include "XmlRpcUtil.h"
+
+namespace XmlRpc {
+
+
+ //! An interface allowing custom handling of error message reporting.
+ class XmlRpcErrorHandler {
+ public:
+ //! Returns a pointer to the currently installed error handling object.
+ static XmlRpcErrorHandler* getErrorHandler()
+ { return _errorHandler; }
+
+ //! Specifies the error handler.
+ static void setErrorHandler(XmlRpcErrorHandler* eh)
+ { _errorHandler = eh; }
+
+ //! Report an error. Custom error handlers should define this method.
+ virtual void error(const char* msg) = 0;
+
+ protected:
+ static XmlRpcErrorHandler* _errorHandler;
+ };
+
+ //! An interface allowing custom handling of informational message reporting.
+ class XmlRpcLogHandler {
+ public:
+ //! Returns a pointer to the currently installed message reporting object.
+ static XmlRpcLogHandler* getLogHandler()
+ { return _logHandler; }
+
+ //! Specifies the message handler.
+ static void setLogHandler(XmlRpcLogHandler* lh)
+ { _logHandler = lh; }
+
+ //! Returns the level of verbosity of informational messages. 0 is no output, 5 is very verbose.
+ static int getVerbosity()
+ { return _verbosity; }
+
+ //! Specify the level of verbosity of informational messages. 0 is no output, 5 is very verbose.
+ static void setVerbosity(int v)
+ { _verbosity = v; }
+
+ //! Output a message. Custom error handlers should define this method.
+ virtual void log(int level, const char* msg) = 0;
+
+ protected:
+ static XmlRpcLogHandler* _logHandler;
+ static int _verbosity;
+ };
+
+ //! Returns log message verbosity. This is short for XmlRpcLogHandler::getVerbosity()
+ int getVerbosity();
+ //! Sets log message verbosity. This is short for XmlRpcLogHandler::setVerbosity(level)
+ void setVerbosity(int level);
+
+
+ //! Version identifier
+ extern const char XMLRPC_VERSION[];
+
+} // namespace XmlRpc
+
+#endif // _XMLRPC_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcClient.cpp b/branches/lydia/xmlrpc++/XmlRpcClient.cpp
new file mode 100644
index 0000000..af70715
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcClient.cpp
@@ -0,0 +1,547 @@
+
+#include "XmlRpcClient.h"
+
+#include "XmlRpcSocket.h"
+#include "XmlRpc.h"
+
+#include "base64.h" // For HTTP authentication encoding
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+using namespace XmlRpc;
+using namespace std;
+
+// Static data
+const char XmlRpcClient::REQUEST_BEGIN[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<methodCall><methodName>";
+const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
+const char XmlRpcClient::PARAMS_TAG[] = "<params>";
+const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
+const char XmlRpcClient::PARAM_TAG[] = "<param>";
+const char XmlRpcClient::PARAM_ETAG[] = "</param>";
+const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
+const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
+const char XmlRpcClient::FAULT_TAG[] = "<fault>";
+
+
+
+XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
+{
+ XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
+
+ _host = host;
+ _port = port;
+ if (uri && *uri)
+ _uri = uri;
+ else
+ _uri = "/RPC2";
+ _connectionState = NO_CONNECTION;
+ _executing = false;
+ _eof = false;
+ _ssl = false; _ssl_ssl = (SSL *) NULL;
+
+ // Default to keeping the connection open until an explicit close is done
+ setKeepOpen();
+}
+XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri, bool ssl)
+{
+ XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
+
+ _host = host;
+ _port = port;
+ if (uri && *uri)
+ _uri = uri;
+ else
+ _uri = "/RPC2";
+ _connectionState = NO_CONNECTION;
+ _executing = false;
+ _eof = false;
+ _ssl = ssl;
+ if (!_ssl) { _ssl_ssl = (SSL *) NULL; }
+
+ // Default to keeping the connection open until an explicit close is done
+ setKeepOpen();
+}
+
+
+XmlRpcClient::XmlRpcClient(const char* host, int port,
+ const char* login, const char* password, const char* uri/*=0*/)
+{
+ XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d, login %s.", host, port, login);
+
+ _host = host;
+ _port = port;
+ if (uri)
+ _uri = uri;
+ else
+ _uri = "/RPC2";
+
+ _login = login;
+ _password = password;
+
+ _connectionState = NO_CONNECTION;
+ _executing = false;
+ _eof = false;
+
+ // Default to keeping the connection open until an explicit close is done
+ setKeepOpen();
+}
+
+XmlRpcClient::XmlRpcClient(const char* host, int port,
+ const char* login, const char* password,
+ const char* uri/*=0*/, bool ssl)
+{
+ XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d, login %s.", host, port, login);
+
+ _host = host;
+ _port = port;
+ if (uri)
+ _uri = uri;
+ else
+ _uri = "/RPC2";
+
+ _login = login;
+ _password = password;
+
+ _connectionState = NO_CONNECTION;
+ _executing = false;
+ _eof = false;
+ _ssl = ssl;
+ if (!_ssl) { _ssl_ssl = (SSL *) NULL; }
+
+ // Default to keeping the connection open until an explicit close is done
+ setKeepOpen();
+}
+
+
+XmlRpcClient::~XmlRpcClient()
+{
+ XmlRpcUtil::log(1, "XmlRpcClient dtor client: host %s, port %d.", _host.c_str(), _port);
+ if (_connectionState != NO_CONNECTION) close();
+}
+
+
+// Close the owned fd
+void
+XmlRpcClient::close()
+{
+ XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
+ _connectionState = NO_CONNECTION;
+ _disp.exit();
+ _disp.removeSource(this);
+ if (_ssl) {
+ // Pre-socket shutdown
+ XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_shutdown");
+ SSL_shutdown(_ssl_ssl);
+ XmlRpcUtil::log(4, "XmlRpcClient::close: after SSL_shutdown");
+ }
+ XmlRpcSource::close();
+ if (_ssl) {
+ // Post-socket shutdown
+ XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_free(_ssl_ssl)");
+ SSL_free(_ssl_ssl);
+ XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_CTX_free(_ssl_ctx)");
+ SSL_CTX_free(_ssl_ctx);
+ XmlRpcUtil::log(4, "XmlRpcClient::close: SSL shutdown successful!");
+ }
+}
+
+
+// Clear the referenced flag even if exceptions or errors occur.
+struct ClearFlagOnExit {
+ ClearFlagOnExit(bool& flag) : _flag(flag) {}
+ ~ClearFlagOnExit() { _flag = false; }
+ bool& _flag;
+};
+
+// Execute the named procedure on the remote server.
+// Params should be an array of the arguments for the method.
+// Returns true if the request was sent and a result received (although the result
+// might be a fault).
+bool
+XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
+{
+ XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
+
+ // This is not a thread-safe operation, if you want to do multithreading, use separate
+ // clients for each thread. If you want to protect yourself from multiple threads
+ // accessing the same client, replace this code with a real mutex.
+ if (_executing)
+ return false;
+
+ _executing = true;
+ ClearFlagOnExit cf(_executing);
+
+ _sendAttempts = 0;
+ _isFault = false;
+
+ if ( ! setupConnection())
+ return false;
+
+ if ( ! generateRequest(method, params))
+ return false;
+
+ result.clear();
+ double msTime = -1.0; // Process until exit is called
+ _disp.work(msTime);
+
+ if (_connectionState != IDLE || ! parseResponse(result))
+ return false;
+
+ XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
+ _response = "";
+ return true;
+}
+
+// XmlRpcSource interface implementation
+// Handle server responses. Called by the event dispatcher during execute.
+unsigned
+XmlRpcClient::handleEvent(unsigned eventType)
+{
+ if (eventType == XmlRpcDispatch::Exception)
+ {
+ //if (XmlRpcSocket::nonFatalError())
+ // return (_connectionState == WRITE_REQUEST)
+ // ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
+
+ if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
+ XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
+ XmlRpcSocket::getErrorMsg().c_str());
+ else
+ XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
+ _connectionState, XmlRpcSocket::getErrorMsg().c_str());
+ return 0;
+ }
+
+ if (_connectionState == WRITE_REQUEST)
+ if ( ! writeRequest()) return 0;
+
+ if (_connectionState == READ_HEADER)
+ if ( ! readHeader()) return 0;
+
+ if (_connectionState == READ_RESPONSE)
+ if ( ! readResponse()) return 0;
+
+ // This should probably always ask for Exception events too
+ return (_connectionState == WRITE_REQUEST)
+ ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
+}
+
+
+// Create the socket connection to the server if necessary
+bool
+XmlRpcClient::setupConnection()
+{
+ // If an error occurred last time through, or if the server closed the connection, close our end
+ if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
+ close();
+
+ _eof = false;
+ if (_connectionState == NO_CONNECTION)
+ if (! doConnect())
+ return false;
+
+ // Prepare to write the request
+ _connectionState = WRITE_REQUEST;
+ _bytesWritten = 0;
+
+ // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
+ _disp.removeSource(this); // Make sure nothing is left over
+ _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
+
+ return true;
+}
+
+
+// Connect to the xmlrpc server
+bool
+XmlRpcClient::doConnect()
+{
+ int fd = XmlRpcSocket::socket();
+ if (fd < 0)
+ {
+ XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
+ this->setfd(fd);
+
+ // Don't block on connect/reads/writes
+ if ( ! XmlRpcSocket::setNonBlocking(fd))
+ {
+ this->close();
+ XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ if ( ! XmlRpcSocket::connect(fd, _host, _port))
+ {
+ this->close();
+ XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ // Perform SSL if needed
+ if (_ssl) {
+ SSLeay_add_ssl_algorithms();
+ _ssl_meth = SSLv23_client_method();
+ SSL_load_error_strings();
+ _ssl_ctx = SSL_CTX_new (_ssl_meth);
+ _ssl_ssl = SSL_new (_ssl_ctx);
+ SSL_set_fd (_ssl_ssl, fd);
+ int err = SSL_connect (_ssl_ssl);
+ }
+
+ return true;
+}
+
+// Encode the request to call the specified method with the specified parameters into xml
+bool
+XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
+{
+ std::string body = REQUEST_BEGIN;
+ body += methodName;
+ body += REQUEST_END_METHODNAME;
+
+ // If params is an array, each element is a separate parameter
+ if (params.valid()) {
+ body += PARAMS_TAG;
+ if (params.getType() == XmlRpcValue::TypeArray)
+ {
+ for (int i=0; i<params.size(); ++i) {
+ body += PARAM_TAG;
+ body += params[i].toXml();
+ body += PARAM_ETAG;
+ }
+ }
+ else
+ {
+ body += PARAM_TAG;
+ body += params.toXml();
+ body += PARAM_ETAG;
+ }
+
+ body += PARAMS_ETAG;
+ }
+ body += REQUEST_END;
+
+ std::string header = generateHeader(body);
+ XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
+ header.length(), body.length());
+
+ _request = header + body;
+ return true;
+}
+
+// Prepend http headers
+std::string
+XmlRpcClient::generateHeader(std::string const& body)
+{
+ std::string header =
+ "POST " + _uri + " HTTP/1.1\r\n"
+ "User-Agent: ";
+ header += XMLRPC_VERSION;
+ header += "\r\nHost: ";
+ header += _host;
+
+ char buff[40];
+ sprintf(buff,":%d\r\n", _port);
+
+ header += buff;
+
+ if (_login.length() != 0)
+ {
+ // convert to base64
+ std::vector<char> base64data;
+ int iostatus = 0;
+ base64<char> encoder;
+ std::back_insert_iterator<std::vector<char> > ins =
+ std::back_inserter(base64data);
+
+ std::string authBuf = _login + ":" + _password;
+
+ encoder.put(authBuf.begin(), authBuf.end(), ins, iostatus,
+ base64<>::crlf());
+
+ header += "Authorization: Basic ";
+ std::string authEnc(base64data.begin(), base64data.end());
+ // handle pesky linefeed characters
+ string::size_type lf;
+ while ( (lf = authEnc.find("\r")) != string::npos ) {
+ authEnc.erase(lf, 1);
+ }
+ while ( (lf = authEnc.find("\n")) != string::npos ) {
+ authEnc.erase(lf, 1);
+ }
+ header += authEnc;
+ header += "\r\n";
+ }
+
+ header += "Content-Type: text/xml\r\nContent-length: ";
+
+ sprintf(buff,"%d\r\n\r\n", body.size());
+
+ return header + buff;
+}
+
+bool
+XmlRpcClient::writeRequest()
+{
+ if (_bytesWritten == 0)
+ XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
+
+ // Try to write the request
+ if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten, _ssl_ssl)) {
+ XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
+
+ // Wait for the result
+ if (_bytesWritten == int(_request.length())) {
+ _header = "";
+ _response = "";
+ _connectionState = READ_HEADER;
+ }
+ return true;
+}
+
+
+// Read the header from the response
+bool
+XmlRpcClient::readHeader()
+{
+ // Read available data
+ if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof, _ssl_ssl) ||
+ (_eof && _header.length() == 0)) {
+
+ // If we haven't read any data yet and this is a keep-alive connection, the server may
+ // have timed out, so we try one more time.
+ if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
+ XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
+ XmlRpcSource::close();
+ _connectionState = NO_CONNECTION;
+ _eof = false;
+ return setupConnection();
+ }
+
+ XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
+ XmlRpcSocket::getErrorMsg().c_str(), getfd());
+ return false;
+ }
+
+ XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
+
+ char *hp = (char*)_header.c_str(); // Start of header
+ char *ep = hp + _header.length(); // End of string
+ char *bp = 0; // Start of body
+ char *lp = 0; // Start of content-length value
+
+ for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
+ if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
+ lp = cp + 16;
+ else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
+ bp = cp + 4;
+ else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
+ bp = cp + 2;
+ }
+
+ // If we haven't gotten the entire header yet, return (keep reading)
+ if (bp == 0) {
+ if (_eof) // EOF in the middle of a response is an error
+ {
+ XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
+ return false; // Close the connection
+ }
+
+ return true; // Keep reading
+ }
+
+ // Decode content length
+ if (lp == 0) {
+ XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
+ return false; // We could try to figure it out by parsing as we read, but for now...
+ }
+
+ _contentLength = atoi(lp);
+ if (_contentLength <= 0) {
+ XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
+ return false;
+ }
+
+ XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
+
+ // Otherwise copy non-header data to response buffer and set state to read response.
+ _response = bp;
+ _header = ""; // should parse out any interesting bits from the header (connection, etc)...
+ _connectionState = READ_RESPONSE;
+ return true; // Continue monitoring this source
+}
+
+
+bool
+XmlRpcClient::readResponse()
+{
+ // If we dont have the entire response yet, read available data
+ if (int(_response.length()) < _contentLength) {
+ if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof, _ssl_ssl)) {
+ XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ // If we haven't gotten the entire _response yet, return (keep reading)
+ if (int(_response.length()) < _contentLength) {
+ if (_eof) {
+ XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // Otherwise, parse and return the result
+ XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
+ XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
+
+ _connectionState = IDLE;
+
+ return false; // Stop monitoring this source (causes return from work)
+}
+
+
+// Convert the response xml into a result value
+bool
+XmlRpcClient::parseResponse(XmlRpcValue& result)
+{
+ // Parse response xml into result
+ int offset = 0;
+ if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
+ XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
+ return false;
+ }
+
+ // Expect either <params><param>... or <fault>...
+ if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
+ XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
+ XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true))
+ {
+ if ( ! result.fromXml(_response, &offset)) {
+ XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
+ _response = "";
+ return false;
+ }
+ } else {
+ XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
+ _response = "";
+ return false;
+ }
+
+ _response = "";
+ return result.valid();
+}
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcClient.h b/branches/lydia/xmlrpc++/XmlRpcClient.h
new file mode 100644
index 0000000..433e012
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcClient.h
@@ -0,0 +1,148 @@
+
+#ifndef _XMLRPCCLIENT_H_
+#define _XMLRPCCLIENT_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+#include "XmlRpcDispatch.h"
+#include "XmlRpcSource.h"
+
+namespace XmlRpc {
+
+ // Arguments and results are represented by XmlRpcValues
+ class XmlRpcValue;
+
+ //! A class to send XML RPC requests to a server and return the results.
+ class XmlRpcClient : public XmlRpcSource {
+ public:
+ // Static data
+ static const char REQUEST_BEGIN[];
+ static const char REQUEST_END_METHODNAME[];
+ static const char PARAMS_TAG[];
+ static const char PARAMS_ETAG[];
+ static const char PARAM_TAG[];
+ static const char PARAM_ETAG[];
+ static const char REQUEST_END[];
+ // Result tags
+ static const char METHODRESPONSE_TAG[];
+ static const char FAULT_TAG[];
+
+ //! Construct a client to connect to the server at the specified host:port address
+ //! @param host The name of the remote machine hosting the server, eg "myserver.mycompany.com"
+ //! @param port The port on the remote machine where the server is listening
+ //! @param uri An optional string to be sent as the URI in the HTTP GET header
+ //! Note that the host is not a URL, do not prepend "http://" or other protocol specifiers.
+ XmlRpcClient(const char* host, int port, const char* uri=0);
+ XmlRpcClient(const char* host, int port, const char* uri=0, bool ssl=false);
+
+ //! Construct a client to connect to the server at the specified host:port address including HTTP authentication
+ //! @param host The name of the remote machine hosting the server
+ //! @param port The port on the remote machine where the server is listening
+ //! @param login The username passed to the server
+ //! @param pass The password passed to the server
+ //! @param uri An optional string to be sent as the URI in the HTTP GET header
+ XmlRpcClient(const char* host, int port, const char* login, const char* password, const char* uri=0);
+ XmlRpcClient(const char* host, int port, const char* login, const char* password, const char* uri=0, bool ssl=false);
+
+ //! Destructor
+ virtual ~XmlRpcClient();
+
+ //! Execute the named procedure on the remote server.
+ //! @param method The name of the remote procedure to execute
+ //! @param params An array of the arguments for the method
+ //! @param result The result value to be returned to the client
+ //! @return true if the request was sent and a result received
+ //! (although the result might be a fault).
+ //!
+ //! Currently this is a synchronous (blocking) implementation (execute
+ //! does not return until it receives a response or an error). Use isFault()
+ //! to determine whether the result is a fault response.
+ bool execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result);
+
+ //! Returns true if the result of the last execute() was a fault response.
+ bool isFault() const { return _isFault; }
+
+ //! Return the host name of the server
+ const char* const host() const { return _host.c_str(); }
+
+ //! Return the port
+ int port() const { return _port; }
+
+ //! Return the URI
+ const char* const uri() const { return _uri.c_str(); }
+
+ // XmlRpcSource interface implementation
+ //! Close the connection
+ virtual void close();
+
+ //! Handle server responses. Called by the event dispatcher during execute.
+ //! @param eventType The type of event that occurred.
+ //! @see XmlRpcDispatch::EventType
+ virtual unsigned handleEvent(unsigned eventType);
+
+ protected:
+ // Execution processing helpers
+ virtual bool doConnect();
+ virtual bool setupConnection();
+
+ virtual bool generateRequest(const char* method, XmlRpcValue const& params);
+ virtual std::string generateHeader(std::string const& body);
+ virtual bool writeRequest();
+ virtual bool readHeader();
+ virtual bool readResponse();
+ virtual bool parseResponse(XmlRpcValue& result);
+
+ // Possible IO states for the connection
+ enum ClientConnectionState { NO_CONNECTION, CONNECTING, WRITE_REQUEST, READ_HEADER, READ_RESPONSE, IDLE };
+ ClientConnectionState _connectionState;
+
+ // Server location
+ std::string _host;
+ std::string _uri;
+ int _port;
+
+ // Login information for HTTP authentication
+ std::string _login;
+ std::string _password;
+
+ // The xml-encoded request, http header of response, and response xml
+ std::string _request;
+ std::string _header;
+ std::string _response;
+
+ // Number of times the client has attempted to send the request
+ int _sendAttempts;
+
+ // Number of bytes of the request that have been written to the socket so far
+ int _bytesWritten;
+
+ // True if we are currently executing a request. If you want to multithread,
+ // each thread should have its own client.
+ bool _executing;
+
+ // True if the server closed the connection
+ bool _eof;
+
+ // True if a fault response was returned by the server
+ bool _isFault;
+
+ // Number of bytes expected in the response body (parsed from response header)
+ int _contentLength;
+
+ // Event dispatcher
+ XmlRpcDispatch _disp;
+
+ }; // class XmlRpcClient
+
+} // namespace XmlRpc
+
+#endif // _XMLRPCCLIENT_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcDispatch.cpp b/branches/lydia/xmlrpc++/XmlRpcDispatch.cpp
new file mode 100644
index 0000000..9d675dc
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcDispatch.cpp
@@ -0,0 +1,268 @@
+
+#include "XmlRpcDispatch.h"
+#include "XmlRpcSource.h"
+#include "XmlRpcUtil.h"
+
+#include <errno.h>
+#include <math.h>
+#include <sys/timeb.h>
+
+#if defined(_WINDOWS)
+# include <winsock2.h>
+
+# define USE_FTIME
+# if defined(_MSC_VER)
+# define timeb _timeb
+# define ftime _ftime
+# endif
+#else
+# include <sys/time.h>
+#endif // _WINDOWS
+
+
+using namespace XmlRpc;
+
+
+XmlRpcDispatch::XmlRpcDispatch()
+{
+ _endTime = -1.0;
+ _doClear = false;
+ _inWork = false;
+}
+
+
+XmlRpcDispatch::~XmlRpcDispatch()
+{
+}
+
+// Monitor this source for the specified events and call its event handler
+// when the event occurs
+void
+XmlRpcDispatch::addSource(XmlRpcSource* source, unsigned mask)
+{
+ _sources.push_back(MonitoredSource(source, mask));
+}
+
+// Stop monitoring this source. Does not close the source.
+void
+XmlRpcDispatch::removeSource(XmlRpcSource* source)
+{
+ for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
+ if (it->getSource() == source)
+ {
+ _sources.erase(it);
+ break;
+ }
+}
+
+
+// Modify the types of events to watch for on this source
+void
+XmlRpcDispatch::setSourceEvents(XmlRpcSource* source, unsigned eventMask)
+{
+ for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
+ if (it->getSource() == source)
+ {
+ it->getMask() = eventMask;
+ break;
+ }
+}
+
+
+
+// Watch current set of sources and process events
+void
+XmlRpcDispatch::work(double timeout)
+{
+ // Compute end time
+ double timeNow = getTime();
+ _endTime = (timeout < 0.0) ? -1.0 : (timeNow + timeout);
+ _doClear = false;
+ _inWork = true;
+
+ // Only work while there is something to monitor
+ while (_sources.size() > 0) {
+
+ // Wait for and dispatch events
+ if ( ! waitForAndProcessEvents(timeout))
+ {
+ _inWork = false;
+ return;
+ }
+
+
+ // Check whether to clear all sources
+ if (_doClear)
+ {
+ SourceList closeList = _sources;
+ _sources.clear();
+ for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) {
+ XmlRpcSource *src = it->getSource();
+ src->close();
+ }
+
+ _doClear = false;
+ }
+
+ // Check whether end time has passed or exit has been called
+ if (_endTime == 0.0) // Exit
+ break;
+ else if (_endTime > 0.0) // Check for timeout
+ {
+ double t = getTime();
+ if (t > _endTime)
+ break;
+
+ // Decrement timeout by elapsed time
+ timeout -= (t - timeNow);
+ if (timeout < 0.0)
+ timeout = 0.0; // Shouldn't happen but its fp math...
+ timeNow = t;
+ }
+ }
+
+ _inWork = false;
+}
+
+
+
+// Exit from work routine. Presumably this will be called from
+// one of the source event handlers.
+void
+XmlRpcDispatch::exit()
+{
+ _endTime = 0.0; // Return from work asap
+}
+
+
+// Clear all sources from the monitored sources list
+void
+XmlRpcDispatch::clear()
+{
+ if (_inWork)
+ _doClear = true; // Finish reporting current events before clearing
+ else
+ {
+ SourceList closeList = _sources;
+ _sources.clear();
+ for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it)
+ it->getSource()->close();
+ }
+}
+
+
+// Time utility
+double
+XmlRpcDispatch::getTime()
+{
+#ifdef USE_FTIME
+ struct timeb tbuff;
+
+ ftime(&tbuff);
+ return ((double) tbuff.time + ((double)tbuff.millitm / 1000.0) +
+ ((double) tbuff.timezone * 60));
+#else
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday(&tv, &tz);
+ return (tv.tv_sec + tv.tv_usec / 1000000.0);
+#endif /* USE_FTIME */
+}
+
+
+// Wait for I/O on any source, timeout, or interrupt signal.
+bool
+XmlRpcDispatch::waitForAndProcessEvents(double timeout)
+{
+#if defined(_WINDOWS) && 0
+
+ int nHandles = 0;
+ SourceList::iterator it;
+ for (it=_sources.begin(); it!=_sources.end(); ++it) {
+ int fd = it->getSource()->getfd();
+ int mask = 0;
+ if (it->getMask() & ReadableEvent) mask = (FD_READ | FD_CLOSE | FD_ACCEPT);
+ if (it->getMask() & WritableEvent) mask |= (FD_WRITE | FD_CLOSE);
+
+#else // Posix
+
+ // Construct the sets of descriptors we are interested in
+ fd_set inFd, outFd, excFd;
+ FD_ZERO(&inFd);
+ FD_ZERO(&outFd);
+ FD_ZERO(&excFd);
+
+ int maxFd = -1;
+ SourceList::iterator it;
+ for (it=_sources.begin(); it!=_sources.end(); ++it) {
+ int fd = it->getSource()->getfd();
+ if (it->getMask() & ReadableEvent) FD_SET(fd, &inFd);
+ if (it->getMask() & WritableEvent) FD_SET(fd, &outFd);
+ if (it->getMask() & Exception) FD_SET(fd, &excFd);
+ if (it->getMask() && fd > maxFd) maxFd = fd;
+ }
+
+ // Check for events
+ int nEvents;
+ if (_endTime < 0.0)
+ nEvents = select(maxFd+1, &inFd, &outFd, &excFd, NULL);
+ else
+ {
+ struct timeval tv;
+ tv.tv_sec = (int)floor(timeout);
+ tv.tv_usec = ((int)floor(1000000.0 * (timeout-floor(timeout)))) % 1000000;
+ nEvents = select(maxFd+1, &inFd, &outFd, &excFd, &tv);
+ }
+
+ if (nEvents < 0 && errno != EINTR)
+ {
+ XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents);
+ return false;
+ }
+
+ // Process events
+ for (it=_sources.begin(); it != _sources.end(); )
+ {
+ SourceList::iterator thisIt = it++;
+ XmlRpcSource* src = thisIt->getSource();
+ int fd = src->getfd();
+
+ if (fd <= maxFd) {
+ // handleEvent is called once per event type signalled
+ unsigned newMask = 0;
+ int nset = 0;
+ if (FD_ISSET(fd, &inFd))
+ {
+ newMask |= src->handleEvent(ReadableEvent);
+ ++nset;
+ }
+ if (FD_ISSET(fd, &outFd))
+ {
+ newMask |= src->handleEvent(WritableEvent);
+ ++nset;
+ }
+ if (FD_ISSET(fd, &excFd))
+ {
+ newMask |= src->handleEvent(Exception);
+ ++nset;
+ }
+
+ // Some event occurred
+ if (nset)
+ {
+ if (newMask)
+ thisIt->getMask() = newMask;
+ else // Stop monitoring this one
+ {
+ _sources.erase(thisIt);
+ if ( ! src->getKeepOpen())
+ src->close();
+ }
+ }
+ }
+ }
+#endif
+
+ return true;
+}
diff --git a/branches/lydia/xmlrpc++/XmlRpcDispatch.h b/branches/lydia/xmlrpc++/XmlRpcDispatch.h
new file mode 100644
index 0000000..88fd657
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcDispatch.h
@@ -0,0 +1,93 @@
+
+#ifndef _XMLRPCDISPATCH_H_
+#define _XMLRPCDISPATCH_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <list>
+#endif
+
+namespace XmlRpc {
+
+ // An RPC source represents a file descriptor to monitor
+ class XmlRpcSource;
+
+ //! An object which monitors file descriptors for events and performs
+ //! callbacks when interesting events happen.
+ class XmlRpcDispatch {
+ public:
+ //! Constructor
+ XmlRpcDispatch();
+ ~XmlRpcDispatch();
+
+ //! Values indicating the type of events a source is interested in
+ enum EventType {
+ ReadableEvent = 1, //!< data available to read
+ WritableEvent = 2, //!< connected/data can be written without blocking
+ Exception = 4 //!< uh oh
+ };
+
+ //! Monitor this source for the event types specified by the event mask
+ //! and call its event handler when any of the events occur.
+ //! @param source The source to monitor
+ //! @param eventMask Which event types to watch for. \see EventType
+ void addSource(XmlRpcSource* source, unsigned eventMask);
+
+ //! Stop monitoring this source.
+ //! @param source The source to stop monitoring
+ //! The source socket is not closed.
+ void removeSource(XmlRpcSource* source);
+
+ //! Modify the types of events to watch for on this source
+ void setSourceEvents(XmlRpcSource* source, unsigned eventMask);
+
+
+ //! Watch current set of sources and process events for the specified
+ //! duration (in ms, -1 implies wait forever, or until exit is called)
+ void work(double msTime);
+
+ //! Exit from work routine
+ void exit();
+
+ //! Clear all sources from the monitored sources list. Sources are closed.
+ void clear();
+
+ protected:
+
+ //! Wait for I/O on any source, timeout, or interrupt signal.
+ bool waitForAndProcessEvents(double timeout);
+
+
+ // helper
+ double getTime();
+
+ // A source to monitor and what to monitor it for
+ struct MonitoredSource {
+ MonitoredSource(XmlRpcSource* src, unsigned mask) : _src(src), _mask(mask) {}
+ XmlRpcSource* getSource() const { return _src; }
+ unsigned& getMask() { return _mask; }
+ XmlRpcSource* _src;
+ unsigned _mask;
+ };
+
+ // A list of sources to monitor
+ typedef std::list< MonitoredSource > SourceList;
+
+ // Sources being monitored
+ SourceList _sources;
+
+ // When work should stop (-1 implies wait forever, or until exit is called)
+ double _endTime;
+
+ bool _doClear;
+ bool _inWork;
+
+ };
+} // namespace XmlRpc
+
+#endif // _XMLRPCDISPATCH_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcException.h b/branches/lydia/xmlrpc++/XmlRpcException.h
new file mode 100644
index 0000000..6090450
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcException.h
@@ -0,0 +1,42 @@
+
+#ifndef _XMLRPCEXCEPTION_H_
+#define _XMLRPCEXCEPTION_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+
+namespace XmlRpc {
+
+ //! A class representing an error.
+ //! If server methods throw this exception, a fault response is returned
+ //! to the client.
+ class XmlRpcException {
+ public:
+ //! Constructor
+ //! @param message A descriptive error message
+ //! @param code An integer error code
+ XmlRpcException(const std::string& message, int code=-1) :
+ _message(message), _code(code) {}
+
+ //! Return the error message.
+ const std::string& getMessage() const { return _message; }
+
+ //! Return the error code.
+ int getCode() const { return _code; }
+
+ private:
+ std::string _message;
+ int _code;
+ };
+
+}
+
+#endif // _XMLRPCEXCEPTION_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcMutex.cpp b/branches/lydia/xmlrpc++/XmlRpcMutex.cpp
new file mode 100644
index 0000000..b29455d
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcMutex.cpp
@@ -0,0 +1,60 @@
+#if defined(XMLRPC_THREADS)
+
+#include "XmlRpcMutex.h"
+
+#if defined(_WINDOWS)
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#else
+# include <pthread.h>
+#endif
+
+using namespace XmlRpc;
+
+
+//! Destructor.
+XmlRpcMutex::~XmlRpcMutex()
+{
+ if (_pMutex)
+ {
+#if defined(_WINDOWS)
+ ::CloseHandle((HANDLE)_pMutex);
+#else
+ ::pthread_mutex_destroy((pthread_mutex_t*)_pMutex);
+ delete _pMutex;
+#endif
+ _pMutex = 0;
+ }
+}
+
+//! Wait for the mutex to be available and then acquire the lock.
+void XmlRpcMutex::acquire()
+{
+#if defined(_WINDOWS)
+ if ( ! _pMutex)
+ _pMutex = ::CreateMutex(0, TRUE, 0);
+ else
+ ::WaitForSingleObject(_pMutex, INFINITE);
+#else
+ if ( ! _pMutex)
+ {
+ _pMutex = new pthread_mutex_t;
+ ::pthread_mutex_init((pthread_mutex_t*)_pMutex, 0);
+ }
+ ::pthread_mutex_lock((pthread_mutex_t*)_pMutex);
+#endif
+}
+
+//! Release the mutex.
+void XmlRpcMutex::release()
+{
+ if (_pMutex)
+#if defined(_WINDOWS)
+ ::ReleaseMutex(_pMutex);
+#else
+ ::pthread_mutex_unlock((pthread_mutex_t*)_pMutex);
+#endif
+}
+
+#endif // XMLRPC_THREADS
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcMutex.h b/branches/lydia/xmlrpc++/XmlRpcMutex.h
new file mode 100644
index 0000000..f46373d
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcMutex.h
@@ -0,0 +1,46 @@
+#ifndef _XMLRPCMUTEX_H_
+#define _XMLRPCMUTEX_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+namespace XmlRpc {
+
+ //! A simple platform-independent mutex API implemented for posix and windows.
+ class XmlRpcMutex {
+ public:
+ //! Construct a Mutex object.
+ XmlRpcMutex() : _pMutex(0) {}
+
+ //! Destroy a Mutex object.
+ ~XmlRpcMutex();
+
+ //! Wait for the mutex to be available and then acquire the lock.
+ void acquire();
+
+ //! Release the mutex.
+ void release();
+
+ //! Utility class to acquire a mutex at construction and release it when destroyed.
+ struct AutoLock {
+ //! Acquire the mutex at construction
+ AutoLock(XmlRpcMutex& m) : _m(m) { _m.acquire(); }
+ //! Release at destruction
+ ~AutoLock() { _m.release(); }
+ //! The mutex being held
+ XmlRpcMutex& _m;
+ };
+
+ private:
+
+ //! Native Mutex object
+ void* _pMutex;
+
+ }; // class XmlRpcMutex
+
+} // namespace XmlRpc
+
+#endif // _XMLRPCMUTEX_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcServer.cpp b/branches/lydia/xmlrpc++/XmlRpcServer.cpp
new file mode 100644
index 0000000..a32604f
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcServer.cpp
@@ -0,0 +1,494 @@
+
+
+#include "XmlRpcServer.h"
+#include "XmlRpcServerConnection.h"
+#include "XmlRpcServerMethod.h"
+#include "XmlRpcSocket.h"
+#include "XmlRpcUtil.h"
+#include "XmlRpcException.h"
+#include "XmlRpc.h"
+
+
+using namespace XmlRpc;
+
+
+// Static data
+const char XmlRpcServer::METHODNAME_TAG[] = "<methodName>";
+const char XmlRpcServer::PARAMS_TAG[] = "<params>";
+const char XmlRpcServer::PARAMS_ETAG[] = "</params>";
+const char XmlRpcServer::PARAM_TAG[] = "<param>";
+const char XmlRpcServer::PARAM_ETAG[] = "</param>";
+
+const std::string XmlRpcServer::METHODNAME = "methodName";
+const std::string XmlRpcServer::PARAMS = "params";
+
+const std::string XmlRpcServer::FAULTCODE = "faultCode";
+const std::string XmlRpcServer::FAULTSTRING = "faultString";
+
+
+
+XmlRpcServer::XmlRpcServer()
+{
+ _introspectionEnabled = false;
+ _listMethods = 0;
+ _methodHelp = 0;
+}
+
+
+XmlRpcServer::~XmlRpcServer()
+{
+ this->shutdown();
+ _methods.clear();
+ delete _listMethods;
+ delete _methodHelp;
+}
+
+
+// Add a command to the RPC server
+void
+XmlRpcServer::addMethod(XmlRpcServerMethod* method)
+{
+ _methods[method->name()] = method;
+}
+
+// Remove a command from the RPC server
+void
+XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
+{
+ MethodMap::iterator i = _methods.find(method->name());
+ if (i != _methods.end())
+ _methods.erase(i);
+}
+
+// Remove a command from the RPC server by name
+void
+XmlRpcServer::removeMethod(const std::string& methodName)
+{
+ MethodMap::iterator i = _methods.find(methodName);
+ if (i != _methods.end())
+ _methods.erase(i);
+}
+
+
+// Look up a method by name
+XmlRpcServerMethod*
+XmlRpcServer::findMethod(const std::string& name) const
+{
+ MethodMap::const_iterator i = _methods.find(name);
+ if (i == _methods.end())
+ return 0;
+ return i->second;
+}
+
+
+// Create a socket, bind to the specified port, and
+// set it in listen mode to make it available for clients.
+bool
+XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
+{
+ int fd = XmlRpcSocket::socket();
+ if (fd < 0)
+ {
+ XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ this->setfd(fd);
+
+ // Don't block on reads/writes
+ if ( ! XmlRpcSocket::setNonBlocking(fd))
+ {
+ this->close();
+ XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ // Allow this port to be re-bound immediately so server re-starts are not delayed
+ if ( ! XmlRpcSocket::setReuseAddr(fd))
+ {
+ this->close();
+ XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ // Bind to the specified port on the default interface
+ if ( ! XmlRpcSocket::bind(fd, port))
+ {
+ this->close();
+ XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ // Set in listening mode
+ if ( ! XmlRpcSocket::listen(fd, backlog))
+ {
+ this->close();
+ XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd);
+
+ // Notify the dispatcher to listen on this source when we are in work()
+ _disp.addSource(this, XmlRpcDispatch::ReadableEvent);
+
+ return true;
+}
+
+
+// Get port number that this server is listening on
+int
+XmlRpcServer::getPort(void) const
+{
+ return XmlRpcSocket::getPort(getfd());
+}
+
+
+
+// Process client requests for the specified time
+void
+XmlRpcServer::work(double msTime)
+{
+ XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
+ _disp.work(msTime);
+}
+
+
+
+// Handle input on the server socket by accepting the connection
+// and reading the rpc request.
+unsigned
+XmlRpcServer::handleEvent(unsigned mask)
+{
+ acceptConnection();
+ return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd
+}
+
+
+// Accept a client connection request and create a connection to
+// handle method calls from the client.
+void
+XmlRpcServer::acceptConnection()
+{
+ int s = XmlRpcSocket::accept(this->getfd());
+ XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
+ if (s < 0)
+ {
+ //this->close();
+ XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ }
+ else if ( ! XmlRpcSocket::setNonBlocking(s))
+ {
+ XmlRpcSocket::close(s);
+ XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
+ }
+ else // Notify the dispatcher to listen for input on this source when we are in work()
+ {
+ XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
+ XmlRpcServerConnection* c = this->createConnection(s);
+ if (c) this->dispatchConnection(c);
+ }
+}
+
+
+// Create a new connection object for processing requests from a specific client.
+XmlRpcServerConnection*
+XmlRpcServer::createConnection(int s)
+{
+ // Specify that the connection object be deleted when it is closed
+ return new XmlRpcServerConnection(s, this, true);
+}
+
+
+// Hand off a new connection to a dispatcher
+void
+XmlRpcServer::dispatchConnection(XmlRpcServerConnection* sc)
+{
+ _disp.addSource(sc, XmlRpcDispatch::ReadableEvent);
+}
+
+
+// Remove a connection. Called by the connection when it closes down.
+void
+XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
+{
+ _disp.removeSource(sc);
+}
+
+
+// Stop processing client requests
+void
+XmlRpcServer::exit()
+{
+ _disp.exit();
+}
+
+
+// Close the server socket file descriptor and stop monitoring connections
+void
+XmlRpcServer::shutdown()
+{
+ // This closes and destroys all connections as well as closing this socket
+ _disp.clear();
+}
+
+
+// Introspection support
+static const std::string LIST_METHODS("system.listMethods");
+static const std::string METHOD_HELP("system.methodHelp");
+static const std::string MULTICALL("system.multicall");
+
+
+// List all methods available on a server
+class ListMethods : public XmlRpcServerMethod
+{
+public:
+ ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
+
+ void execute(XmlRpcValue& params, XmlRpcValue& result)
+ {
+ _server->listMethods(result);
+ }
+
+ std::string help() { return std::string("List all methods available on a server as an array of strings"); }
+};
+
+
+// Retrieve the help string for a named method
+class MethodHelp : public XmlRpcServerMethod
+{
+public:
+ MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
+
+ void execute(XmlRpcValue& params, XmlRpcValue& result)
+ {
+ if (params[0].getType() != XmlRpcValue::TypeString)
+ throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
+
+ XmlRpcServerMethod* m = _server->findMethod(params[0]);
+ if ( ! m)
+ throw XmlRpcException(METHOD_HELP + ": Unknown method name");
+
+ result = m->help();
+ }
+
+ std::string help() { return std::string("Retrieve the help string for a named method"); }
+};
+
+
+// Specify whether introspection is enabled or not. Default is enabled.
+void
+XmlRpcServer::enableIntrospection(bool enabled)
+{
+ if (_introspectionEnabled == enabled)
+ return;
+
+ _introspectionEnabled = enabled;
+
+ if (enabled)
+ {
+ if ( ! _listMethods)
+ {
+ _listMethods = new ListMethods(this);
+ _methodHelp = new MethodHelp(this);
+ } else {
+ addMethod(_listMethods);
+ addMethod(_methodHelp);
+ }
+ }
+ else
+ {
+ removeMethod(LIST_METHODS);
+ removeMethod(METHOD_HELP);
+ }
+}
+
+
+void
+XmlRpcServer::listMethods(XmlRpcValue& result)
+{
+ int i = 0;
+ result.setSize(_methods.size()+1);
+ for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
+ result[i++] = it->first;
+
+ // Multicall support is built into XmlRpcServer::executeRequest
+ result[i] = MULTICALL;
+}
+
+
+
+// Parse the request, run the method, generate a response string.
+std::string
+XmlRpcServer::executeRequest(std::string const& request)
+{
+ XmlRpcValue params, resultValue;
+ std::string methodName = parseRequest(request, params);
+ XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: server calling method '%s'",
+ methodName.c_str());
+
+ std::string response;
+ try {
+
+ if ( ! executeMethod(methodName, params, resultValue) &&
+ ! executeMulticall(methodName, params, resultValue))
+ response = generateFaultResponse(methodName + ": unknown method name");
+ else
+ response = generateResponse(resultValue.toXml());
+
+ } catch (const XmlRpcException& fault) {
+ XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: fault %s.",
+ fault.getMessage().c_str());
+ response = generateFaultResponse(fault.getMessage(), fault.getCode());
+ }
+
+ return response;
+}
+
+// Parse the method name and the argument values from the request.
+std::string
+XmlRpcServer::parseRequest(std::string const& request, XmlRpcValue& params)
+{
+ int offset = 0; // Number of chars parsed from the request
+
+ std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, request, &offset);
+
+ if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, request, &offset))
+ {
+ int nArgs = 0;
+ while (XmlRpcUtil::nextTagIs(PARAM_TAG, request, &offset)) {
+ params[nArgs++] = XmlRpcValue(request, &offset);
+ (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, request, &offset);
+ }
+
+ (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, request, &offset);
+ }
+
+ return methodName;
+}
+
+// Execute a named method with the specified params.
+bool
+XmlRpcServer::executeMethod(const std::string& methodName,
+ XmlRpcValue& params,
+ XmlRpcValue& result)
+{
+ XmlRpcServerMethod* method = findMethod(methodName);
+
+ if ( ! method) return false;
+
+ method->execute(params, result);
+
+ // Ensure a valid result value
+ if ( ! result.valid())
+ result = std::string();
+
+ return true;
+}
+
+// Execute multiple calls and return the results in an array.
+bool
+XmlRpcServer::executeMulticall(const std::string& methodName,
+ XmlRpcValue& params,
+ XmlRpcValue& result)
+{
+ if (methodName != MULTICALL) return false;
+
+ // There ought to be 1 parameter, an array of structs
+ if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
+ throw XmlRpcException(MULTICALL + ": Invalid argument (expected an array)");
+
+ int nc = params[0].size();
+ result.setSize(nc);
+
+ for (int i=0; i<nc; ++i) {
+
+ if ( ! params[0][i].hasMember(METHODNAME) ||
+ ! params[0][i].hasMember(PARAMS)) {
+ result[i][FAULTCODE] = -1;
+ result[i][FAULTSTRING] = MULTICALL +
+ ": Invalid argument (expected a struct with members methodName and params)";
+ continue;
+ }
+
+ const std::string& methodName = params[0][i][METHODNAME];
+ XmlRpcValue& methodParams = params[0][i][PARAMS];
+
+ XmlRpcValue resultValue;
+ resultValue.setSize(1);
+ try {
+ if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
+ ! executeMulticall(methodName, params, resultValue[0]))
+ {
+ result[i][FAULTCODE] = -1;
+ result[i][FAULTSTRING] = methodName + ": unknown method name";
+ }
+ else
+ result[i] = resultValue;
+
+ } catch (const XmlRpcException& fault) {
+ result[i][FAULTCODE] = fault.getCode();
+ result[i][FAULTSTRING] = fault.getMessage();
+ }
+ }
+
+ return true;
+}
+
+
+// Create a response from results xml
+std::string
+XmlRpcServer::generateResponse(std::string const& resultXml)
+{
+ const char RESPONSE_1[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<methodResponse><params><param>\r\n\t";
+ const char RESPONSE_2[] =
+ "\r\n</param></params></methodResponse>\r\n";
+
+ std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
+ std::string header = generateHeader(body);
+ std::string response = header + body;
+
+ XmlRpcUtil::log(5, "XmlRpcServer::generateResponse:\n%s\n", response.c_str());
+ return response;
+}
+
+
+// Prepend http headers
+std::string
+XmlRpcServer::generateHeader(std::string const& body)
+{
+ std::string header =
+ "HTTP/1.1 200 OK\r\n"
+ "Server: ";
+ header += XMLRPC_VERSION;
+ header += "\r\n"
+ "Content-Type: text/xml\r\n"
+ "Content-length: ";
+
+ char buffLen[40];
+ sprintf(buffLen,"%d\r\n\r\n", body.size());
+
+ return header + buffLen;
+}
+
+
+std::string
+XmlRpcServer::generateFaultResponse(std::string const& errorMsg, int errorCode)
+{
+ const char RESPONSE_1[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<methodResponse><fault>\r\n\t";
+ const char RESPONSE_2[] =
+ "\r\n</fault></methodResponse>\r\n";
+
+ XmlRpcValue faultStruct;
+ faultStruct[FAULTCODE] = errorCode;
+ faultStruct[FAULTSTRING] = errorMsg;
+ std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
+ std::string header = generateHeader(body);
+
+ return header + body;
+}
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcServer.h b/branches/lydia/xmlrpc++/XmlRpcServer.h
new file mode 100644
index 0000000..28b34a5
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcServer.h
@@ -0,0 +1,160 @@
+
+#ifndef _XMLRPCSERVER_H_
+#define _XMLRPCSERVER_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <map>
+# include <string>
+#endif
+
+#include "XmlRpcDispatch.h"
+#include "XmlRpcSource.h"
+
+namespace XmlRpc {
+
+
+ // An abstract class supporting XML RPC methods
+ class XmlRpcServerMethod;
+
+ // Class representing connections to specific clients
+ class XmlRpcServerConnection;
+
+ // Class representing argument and result values
+ class XmlRpcValue;
+
+
+ //! A class to handle XML RPC requests
+ class XmlRpcServer : public XmlRpcSource {
+ public:
+ //! Create a server object.
+ XmlRpcServer();
+ //! Destructor.
+ virtual ~XmlRpcServer();
+
+ //! Specify whether introspection is enabled or not. Default is not enabled.
+ void enableIntrospection(bool enabled=true);
+
+ //! Add a command to the RPC server
+ void addMethod(XmlRpcServerMethod* method);
+
+ //! Remove a command from the RPC server
+ void removeMethod(XmlRpcServerMethod* method);
+
+ //! Remove a command from the RPC server by name
+ void removeMethod(const std::string& methodName);
+
+ //! Look up a method by name
+ XmlRpcServerMethod* findMethod(const std::string& name) const;
+
+ //! Create a socket, bind to the specified port, and
+ //! set it in listen mode to make it available for clients.
+ //! @param port The port to bind and listen on (zero to choose an arbitrary port)
+ bool bindAndListen(int port, int backlog = 5);
+
+ //! Get the port number this server is listening on.
+ int getPort(void) const;
+
+ //! Process client requests for the specified time
+ void work(double msTime);
+
+ //! Temporarily stop processing client requests and exit the work() method.
+ void exit();
+
+ //! Close all connections with clients and the socket file descriptor
+ void shutdown();
+
+ //! Introspection support
+ void listMethods(XmlRpcValue& result);
+
+
+ //! Parses the request xml, runs the method, generates the response (header+xml).
+ //! Returns a fault response if an error occurs during method execution.
+ virtual std::string executeRequest(std::string const& request);
+
+
+ // XmlRpcSource interface implementation
+
+ //! Handle client connection requests
+ virtual unsigned handleEvent(unsigned eventType);
+
+ //! Remove a connection from the dispatcher
+ virtual void removeConnection(XmlRpcServerConnection*);
+
+ protected:
+
+ // Static data
+ static const char METHODNAME_TAG[];
+ static const char PARAMS_TAG[];
+ static const char PARAMS_ETAG[];
+ static const char PARAM_TAG[];
+ static const char PARAM_ETAG[];
+
+ static const std::string SYSTEM_MULTICALL;
+ static const std::string METHODNAME;
+ static const std::string PARAMS;
+
+ static const std::string FAULTCODE;
+ static const std::string FAULTSTRING;
+
+
+ //! Accept a client connection request
+ virtual void acceptConnection();
+
+ //! Create a new connection object for processing requests from a specific client.
+ //! If the client is not authorized to connect, close the socket and return 0.
+ virtual XmlRpcServerConnection* createConnection(int socket);
+
+ //! Hand off a new connection object to a dispatcher.
+ virtual void dispatchConnection(XmlRpcServerConnection* sc);
+
+
+ //! Parse the methodName and parameters from the request.
+ //! @returns the methodName
+ std::string parseRequest(std::string const& request, XmlRpcValue& params);
+
+ //! Execute a named method with the specified params.
+ bool executeMethod(const std::string& methodName, XmlRpcValue& params, XmlRpcValue& result);
+
+ //! Execute multiple calls and return the results in an array.
+ //! System.multicall implementation
+ bool executeMulticall(const std::string& methodName, XmlRpcValue& params, XmlRpcValue& result);
+
+ //! Construct a response from the result XML.
+ std::string generateResponse(std::string const& resultXml);
+
+ //! Construct a fault response.
+ std::string generateFaultResponse(std::string const& msg, int errorCode = -1);
+
+ //! Return the appropriate headers for the response.
+ std::string generateHeader(std::string const& body);
+
+
+
+ //! Whether the introspection API is supported by this server
+ bool _introspectionEnabled;
+
+ //! Event dispatcher
+ XmlRpcDispatch _disp;
+
+ //! Collection of methods. This could be a set keyed on method name if we wanted...
+ typedef std::map< std::string, XmlRpcServerMethod* > MethodMap;
+
+ //! Registered RPC methods.
+ MethodMap _methods;
+
+ //! List all registered RPC methods (only available if introspection is enabled)
+ XmlRpcServerMethod* _listMethods;
+
+ //! Return help string for a specified method (only available if introspection is enabled)
+ XmlRpcServerMethod* _methodHelp;
+
+ };
+} // namespace XmlRpc
+
+#endif //_XMLRPCSERVER_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcServerConnection.cpp b/branches/lydia/xmlrpc++/XmlRpcServerConnection.cpp
new file mode 100644
index 0000000..d428ff4
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcServerConnection.cpp
@@ -0,0 +1,205 @@
+
+#include "XmlRpcServerConnection.h"
+
+#include "XmlRpcSocket.h"
+#ifndef MAKEDEPEND
+# include <stdio.h>
+# include <stdlib.h>
+#endif
+
+#include "XmlRpcDispatch.h"
+#include "XmlRpcServer.h"
+#include "XmlRpcUtil.h"
+
+using namespace XmlRpc;
+
+
+
+// The server delegates handling client requests to a serverConnection object.
+XmlRpcServerConnection::XmlRpcServerConnection(int fd,
+ XmlRpcServer* server,
+ bool deleteOnClose /*= false*/) :
+ XmlRpcSource(fd, deleteOnClose)
+{
+ XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
+ _server = server;
+ _connectionState = READ_HEADER;
+ _keepAlive = true;
+}
+
+
+XmlRpcServerConnection::~XmlRpcServerConnection()
+{
+ XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
+ _server->removeConnection(this);
+}
+
+
+// Handle input on the server socket by accepting the connection
+// and reading the rpc request. Return true to continue to monitor
+// the socket for events, false to remove it from the dispatcher.
+unsigned
+XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
+{
+ if (_connectionState == READ_HEADER)
+ if ( ! readHeader()) return 0;
+
+ if (_connectionState == READ_REQUEST)
+ if ( ! readRequest()) return 0;
+
+ if (_connectionState == WRITE_RESPONSE)
+ if ( ! writeResponse()) return 0;
+
+ return (_connectionState == WRITE_RESPONSE)
+ ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
+}
+
+
+bool
+XmlRpcServerConnection::readHeader()
+{
+ // Read available data
+ bool eof;
+ if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof, NULL)) { // was _ssl_ssl
+ // Its only an error if we already have read some data
+ if (_header.length() > 0)
+ XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
+ char *hp = (char*)_header.c_str(); // Start of header
+ char *ep = hp + _header.length(); // End of string
+ char *bp = 0; // Start of body
+ char *lp = 0; // Start of content-length value
+ char *kp = 0; // Start of connection value
+
+ for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
+ if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
+ lp = cp + 16;
+ else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
+ kp = cp + 12;
+ else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
+ bp = cp + 4;
+ else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
+ bp = cp + 2;
+ }
+
+ // If we haven't gotten the entire header yet, return (keep reading)
+ if (bp == 0) {
+ // EOF in the middle of a request is an error, otherwise its ok
+ if (eof) {
+ XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
+ if (_header.length() > 0)
+ XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
+ return false; // Either way we close the connection
+ }
+
+ return true; // Keep reading
+ }
+
+ // Decode content length
+ if (lp == 0) {
+ XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
+ return false; // We could try to figure it out by parsing as we read, but for now...
+ }
+
+ _contentLength = atoi(lp);
+ if (_contentLength <= 0) {
+ XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
+ return false;
+ }
+
+ XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
+
+ // Otherwise copy non-header data to request buffer and set state to read request.
+ _request = bp;
+
+ // Parse out any interesting bits from the header (HTTP version, connection)
+ _keepAlive = true;
+ if (_header.find("HTTP/1.0") != std::string::npos) {
+ if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
+ _keepAlive = false; // Default for HTTP 1.0 is to close the connection
+ } else {
+ if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
+ _keepAlive = false;
+ }
+ XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
+
+
+ _header = "";
+ _connectionState = READ_REQUEST;
+ return true; // Continue monitoring this source
+}
+
+
+
+bool
+XmlRpcServerConnection::readRequest()
+{
+ // If we dont have the entire request yet, read available data
+ if (int(_request.length()) < _contentLength) {
+ bool eof;
+ if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof, NULL)) { // was _ssl_ssl
+ XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+
+ // If we haven't gotten the entire request yet, return (keep reading)
+ if (int(_request.length()) < _contentLength) {
+ if (eof) {
+ XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
+ return false; // Either way we close the connection
+ }
+ return true;
+ }
+ }
+
+ // Otherwise, parse and dispatch the request
+ XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
+ //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
+
+ _connectionState = WRITE_RESPONSE;
+
+ return true; // Continue monitoring this source
+}
+
+
+
+bool
+XmlRpcServerConnection::writeResponse()
+{
+ if (_response.length() == 0) {
+ executeRequest();
+ _bytesWritten = 0;
+ if (_response.length() == 0) {
+ XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
+ return false;
+ }
+ }
+
+ // Try to write the response
+ if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten, NULL)) { // was _ssl_ssl
+ XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
+ return false;
+ }
+ XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
+
+ // Prepare to read the next request
+ if (_bytesWritten == int(_response.length())) {
+ _header = "";
+ _request = "";
+ _response = "";
+ _connectionState = READ_HEADER;
+ }
+
+ return _keepAlive; // Continue monitoring this source if true
+}
+
+
+//! Helper method to execute the client request
+void XmlRpcServerConnection::executeRequest()
+{
+ _response = _server->executeRequest(_request);
+}
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcServerConnection.h b/branches/lydia/xmlrpc++/XmlRpcServerConnection.h
new file mode 100644
index 0000000..31c31d0
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcServerConnection.h
@@ -0,0 +1,82 @@
+#ifndef _XMLRPCSERVERCONNECTION_H_
+#define _XMLRPCSERVERCONNECTION_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+#include "XmlRpcValue.h"
+#include "XmlRpcSource.h"
+
+namespace XmlRpc {
+
+
+ // The server waits for client connections and provides support for executing methods
+ class XmlRpcServer;
+ class XmlRpcServerMethod;
+
+ //! A class to handle XML RPC requests from a particular client
+ class XmlRpcServerConnection : public XmlRpcSource {
+ public:
+
+ //! Constructor
+ XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose = false);
+ //! Destructor
+ virtual ~XmlRpcServerConnection();
+
+ // XmlRpcSource interface implementation
+ //! Handle IO on the client connection socket.
+ //! @param eventType Type of IO event that occurred. @see XmlRpcDispatch::EventType.
+ virtual unsigned handleEvent(unsigned eventType);
+
+ protected:
+
+ //! Reads the http header
+ bool readHeader();
+
+ //! Reads the request (based on the content-length header value)
+ bool readRequest();
+
+ //! Executes the request and writes the resulting response
+ bool writeResponse();
+
+
+ //! Helper method to execute the client request
+ virtual void executeRequest();
+
+
+ //! The XmlRpc server that accepted this connection
+ XmlRpcServer* _server;
+
+ //! Possible IO states for the connection
+ enum ServerConnectionState { READ_HEADER, READ_REQUEST, WRITE_RESPONSE };
+ //! Current IO state for the connection
+ ServerConnectionState _connectionState;
+
+ //! Request headers
+ std::string _header;
+
+ //! Number of bytes expected in the request body (parsed from header)
+ int _contentLength;
+
+ //! Request body
+ std::string _request;
+
+ //! Response
+ std::string _response;
+
+ //! Number of bytes of the response written so far
+ int _bytesWritten;
+
+ //! Whether to keep the current client connection open for further requests
+ bool _keepAlive;
+ };
+} // namespace XmlRpc
+
+#endif // _XMLRPCSERVERCONNECTION_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcServerMethod.cpp b/branches/lydia/xmlrpc++/XmlRpcServerMethod.cpp
new file mode 100644
index 0000000..1616ff4
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcServerMethod.cpp
@@ -0,0 +1,21 @@
+
+#include "XmlRpcServerMethod.h"
+#include "XmlRpcServer.h"
+
+namespace XmlRpc {
+
+
+ XmlRpcServerMethod::XmlRpcServerMethod(std::string const& name, XmlRpcServer* server)
+ {
+ _name = name;
+ _server = server;
+ if (_server) _server->addMethod(this);
+ }
+
+ XmlRpcServerMethod::~XmlRpcServerMethod()
+ {
+ if (_server) _server->removeMethod(this);
+ }
+
+
+} // namespace XmlRpc
diff --git a/branches/lydia/xmlrpc++/XmlRpcServerMethod.h b/branches/lydia/xmlrpc++/XmlRpcServerMethod.h
new file mode 100644
index 0000000..738a9c8
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcServerMethod.h
@@ -0,0 +1,47 @@
+
+#ifndef _XMLRPCSERVERMETHOD_H_
+#define _XMLRPCSERVERMETHOD_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+namespace XmlRpc {
+
+ // Representation of a parameter or result value
+ class XmlRpcValue;
+
+ // The XmlRpcServer processes client requests to call RPCs
+ class XmlRpcServer;
+
+ //! Abstract class representing a single RPC method
+ class XmlRpcServerMethod {
+ public:
+ //! Constructor
+ XmlRpcServerMethod(std::string const& name, XmlRpcServer* server = 0);
+ //! Destructor
+ virtual ~XmlRpcServerMethod();
+
+ //! Returns the name of the method
+ std::string& name() { return _name; }
+
+ //! Execute the method. Subclasses must provide a definition for this method.
+ virtual void execute(XmlRpcValue& params, XmlRpcValue& result) = 0;
+
+ //! Returns a help string for the method.
+ //! Subclasses should define this method if introspection is being used.
+ virtual std::string help() { return std::string(); }
+
+ protected:
+ std::string _name;
+ XmlRpcServer* _server;
+ };
+} // namespace XmlRpc
+
+#endif // _XMLRPCSERVERMETHOD_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcSocket.cpp b/branches/lydia/xmlrpc++/XmlRpcSocket.cpp
new file mode 100644
index 0000000..ed29b1a
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcSocket.cpp
@@ -0,0 +1,285 @@
+
+#include "XmlRpcSocket.h"
+#include "XmlRpcUtil.h"
+
+#ifndef MAKEDEPEND
+
+#if defined(_WINDOWS)
+# include <stdio.h>
+# include <winsock2.h>
+//# pragma lib(WS2_32.lib)
+
+# define EINPROGRESS WSAEINPROGRESS
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# define ETIMEDOUT WSAETIMEDOUT
+
+typedef int socklen_t;
+
+#else
+extern "C" {
+# include <unistd.h>
+# include <stdio.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netdb.h>
+# include <errno.h>
+# include <fcntl.h>
+}
+#endif // _WINDOWS
+
+#endif // MAKEDEPEND
+
+
+using namespace XmlRpc;
+
+
+
+#if defined(_WINDOWS)
+
+static void initWinSock()
+{
+ static bool wsInit = false;
+ if (! wsInit)
+ {
+ WORD wVersionRequested = MAKEWORD( 2, 0 );
+ WSADATA wsaData;
+ WSAStartup(wVersionRequested, &wsaData);
+ wsInit = true;
+ }
+}
+
+#else
+
+#define initWinSock()
+
+#endif // _WINDOWS
+
+
+// These errors are not considered fatal for an IO operation; the operation will be re-tried.
+bool
+XmlRpcSocket::nonFatalError()
+{
+ int err = XmlRpcSocket::getError();
+ return (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
+}
+
+
+int
+XmlRpcSocket::socket()
+{
+ initWinSock();
+ return (int) ::socket(AF_INET, SOCK_STREAM, 0);
+}
+
+
+void
+XmlRpcSocket::close(int fd)
+{
+ XmlRpcUtil::log(4, "XmlRpcSocket::close: fd %d.", fd);
+#if defined(_WINDOWS)
+ closesocket(fd);
+#else
+ ::close(fd);
+#endif // _WINDOWS
+}
+
+
+
+
+bool
+XmlRpcSocket::setNonBlocking(int fd)
+{
+#if defined(_WINDOWS)
+ unsigned long flag = 1;
+ return (ioctlsocket((SOCKET)fd, FIONBIO, &flag) == 0);
+#else
+ return (fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
+#endif // _WINDOWS
+}
+
+
+bool
+XmlRpcSocket::setReuseAddr(int fd)
+{
+ // Allow this port to be re-bound immediately so server re-starts are not delayed
+ int sflag = 1;
+ return (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&sflag, sizeof(sflag)) == 0);
+}
+
+
+// Bind to a specified port
+bool
+XmlRpcSocket::bind(int fd, int port)
+{
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons((u_short) port);
+ return (::bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) == 0);
+}
+
+
+// Set socket in listen mode
+bool
+XmlRpcSocket::listen(int fd, int backlog)
+{
+ return (::listen(fd, backlog) == 0);
+}
+
+
+int
+XmlRpcSocket::accept(int fd)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+
+ return (int) ::accept(fd, (struct sockaddr*)&addr, &addrlen);
+}
+
+
+
+// Connect a socket to a server (from a client)
+bool
+XmlRpcSocket::connect(int fd, std::string& host, int port)
+{
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+
+ struct hostent *hp = gethostbyname(host.c_str());
+ if (hp == 0) return false;
+
+ saddr.sin_family = hp->h_addrtype;
+ memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
+ saddr.sin_port = htons((u_short) port);
+
+ // For asynch operation, this will return EWOULDBLOCK (windows) or
+ // EINPROGRESS (linux) and we just need to wait for the socket to be writable...
+ int result = ::connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ return result == 0 || nonFatalError();
+}
+
+
+
+// Read available text from the specified socket. Returns false on error.
+bool
+XmlRpcSocket::nbRead(int fd, std::string& s, bool *eof, SSL* ssl)
+{
+ const int READ_SIZE = 4096; // Number of bytes to attempt to read at a time
+ char readBuf[READ_SIZE];
+
+ bool wouldBlock = false;
+ *eof = false;
+
+ while ( ! wouldBlock && ! *eof) {
+#if defined(_WINDOWS)
+ int n = recv(fd, readBuf, READ_SIZE-1, 0);
+#else
+ int n;
+ if (ssl != (SSL *) NULL) {
+ n = SSL_read(ssl, readBuf, READ_SIZE-1);
+ } else {
+ n = read(fd, readBuf, READ_SIZE-1);
+ }
+#endif
+ XmlRpcUtil::log(5, "XmlRpcSocket::nbRead: read/recv returned %d.", n);
+
+ if (n > 0) {
+ readBuf[n] = 0;
+ s.append(readBuf, n);
+ } else if (n == 0) {
+ *eof = true;
+ } else if (nonFatalError()) {
+ wouldBlock = true;
+ } else {
+ return false; // Error
+ }
+ }
+ return true;
+}
+
+
+// Write text to the specified socket. Returns false on error.
+bool
+XmlRpcSocket::nbWrite(int fd, std::string& s, int *bytesSoFar, SSL* ssl)
+{
+ int nToWrite = int(s.length()) - *bytesSoFar;
+ char *sp = const_cast<char*>(s.c_str()) + *bytesSoFar;
+ bool wouldBlock = false;
+
+ while ( nToWrite > 0 && ! wouldBlock ) {
+#if defined(_WINDOWS)
+ int n = send(fd, sp, nToWrite, 0);
+#else
+ int n;
+ if (ssl != (SSL *) NULL) {
+ n = SSL_write(ssl, sp, nToWrite);
+ } else {
+ n = write(fd, sp, nToWrite);
+ }
+#endif
+ XmlRpcUtil::log(5, "XmlRpcSocket::nbWrite: send/write returned %d.", n);
+
+ if (n > 0) {
+ sp += n;
+ *bytesSoFar += n;
+ nToWrite -= n;
+ } else if (nonFatalError()) {
+ wouldBlock = true;
+ } else {
+ return false; // Error
+ }
+ }
+ return true;
+}
+
+// Get the port of a bound socket
+int
+XmlRpcSocket::getPort(int socket)
+{
+ struct sockaddr_in saddr;
+ socklen_t saddr_len = sizeof(saddr);
+ int port;
+
+ int result = ::getsockname(socket, (sockaddr*) &saddr, &saddr_len);
+
+ if (result != 0) {
+ port = -1;
+ } else {
+ port = ntohs(saddr.sin_port);
+ }
+ return port;
+}
+
+
+// Returns last errno
+int
+XmlRpcSocket::getError()
+{
+#if defined(_WINDOWS)
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+
+// Returns message corresponding to last errno
+std::string
+XmlRpcSocket::getErrorMsg()
+{
+ return getErrorMsg(getError());
+}
+
+// Returns message corresponding to errno... well, it should anyway
+std::string
+XmlRpcSocket::getErrorMsg(int error)
+{
+ char err[60];
+ snprintf(err,sizeof(err),"error %d", error);
+ return std::string(err);
+}
+
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcSocket.h b/branches/lydia/xmlrpc++/XmlRpcSocket.h
new file mode 100644
index 0000000..115d3e0
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcSocket.h
@@ -0,0 +1,76 @@
+#ifndef _XMLRPCSOCKET_H_
+#define _XMLRPCSOCKET_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+#include <openssl/ssl.h>
+
+namespace XmlRpc {
+
+ //! A platform-independent socket API.
+ class XmlRpcSocket {
+ public:
+ // TODO - typedef the socket type, casting to int won't work for 64 bit windows
+
+ //! Creates a stream (TCP) socket. Returns -1 on failure.
+ static int socket();
+
+ //! Closes a socket.
+ static void close(int socket);
+
+
+ //! Sets a stream (TCP) socket to perform non-blocking IO. Returns false on failure.
+ static bool setNonBlocking(int socket);
+
+ //! Read text from the specified socket. Returns false on error.
+ static bool nbRead(int socket, std::string& s, bool *eof, SSL *ssl);
+
+ //! Write text to the specified socket. Returns false on error.
+ static bool nbWrite(int socket, std::string& s, int *bytesSoFar, SSL* ssl);
+
+
+ // The next four methods are appropriate for servers.
+
+ //! Allow the port the specified socket is bound to to be re-bound immediately so
+ //! server re-starts are not delayed. Returns false on failure.
+ static bool setReuseAddr(int socket);
+
+ //! Bind to a specified port
+ static bool bind(int socket, int port);
+
+ //! Set socket in listen mode
+ static bool listen(int socket, int backlog);
+
+ //! Accept a client connection request
+ static int accept(int socket);
+
+ //! Connect a socket to a server (from a client)
+ static bool connect(int socket, std::string& host, int port);
+
+ //! Get the port of a bound socket
+ static int getPort(int socket);
+
+ //! Returns true if the last error was not a fatal one (eg, EWOULDBLOCK)
+ static bool nonFatalError();
+
+ //! Returns last errno
+ static int getError();
+
+ //! Returns message corresponding to last error
+ static std::string getErrorMsg();
+
+ //! Returns message corresponding to error
+ static std::string getErrorMsg(int error);
+ };
+
+} // namespace XmlRpc
+
+#endif
diff --git a/branches/lydia/xmlrpc++/XmlRpcSource.cpp b/branches/lydia/xmlrpc++/XmlRpcSource.cpp
new file mode 100644
index 0000000..31ab771
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcSource.cpp
@@ -0,0 +1,40 @@
+
+#include "XmlRpcSource.h"
+#include "XmlRpcSocket.h"
+#include "XmlRpcUtil.h"
+
+namespace XmlRpc {
+
+
+ XmlRpcSource::XmlRpcSource(int fd /*= -1*/, bool deleteOnClose /*= false*/)
+ : _fd(fd), _deleteOnClose(deleteOnClose), _keepOpen(false)
+ {
+ }
+
+ XmlRpcSource::~XmlRpcSource()
+ {
+ }
+
+
+ void
+ XmlRpcSource::close()
+ {
+ if (_fd != -1) {
+ XmlRpcUtil::log(2,"XmlRpcSource::close: closing socket %d.", _fd);
+ XmlRpcSocket::close(_fd);
+ XmlRpcUtil::log(2,"XmlRpcSource::close: done closing socket %d.", _fd);
+ _fd = -1;
+ }
+ if (_deleteOnClose) {
+ XmlRpcUtil::log(2,"XmlRpcSource::close: deleting this");
+ _deleteOnClose = false;
+ delete this;
+ }
+ if (_ssl_ssl != (SSL *) NULL) {
+ SSL_shutdown (_ssl_ssl);
+ SSL_free (_ssl_ssl);
+ SSL_CTX_free (_ssl_ctx);
+ }
+ }
+
+} // namespace XmlRpc
diff --git a/branches/lydia/xmlrpc++/XmlRpcSource.h b/branches/lydia/xmlrpc++/XmlRpcSource.h
new file mode 100644
index 0000000..2843241
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcSource.h
@@ -0,0 +1,67 @@
+
+#ifndef _XMLRPCSOURCE_H_
+#define _XMLRPCSOURCE_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+// Deal with SSL dependencies
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+namespace XmlRpc {
+
+ //! An RPC source represents a file descriptor to monitor
+ class XmlRpcSource {
+ public:
+ //! Constructor
+ //! @param fd The socket file descriptor to monitor.
+ //! @param deleteOnClose If true, the object deletes itself when close is called.
+ XmlRpcSource(int fd = -1, bool deleteOnClose = false);
+
+ //! Destructor
+ virtual ~XmlRpcSource();
+
+ //! Return the file descriptor being monitored.
+ int getfd() const { return _fd; }
+ //! Specify the file descriptor to monitor.
+ void setfd(int fd) { _fd = fd; }
+
+ //! Return whether the file descriptor should be kept open if it is no longer monitored.
+ bool getKeepOpen() const { return _keepOpen; }
+ //! Specify whether the file descriptor should be kept open if it is no longer monitored.
+ void setKeepOpen(bool b=true) { _keepOpen = b; }
+
+ //! Close the owned fd. If deleteOnClose was specified at construction, the object is deleted.
+ virtual void close();
+
+ //! Return true to continue monitoring this source
+ virtual unsigned handleEvent(unsigned eventType) = 0;
+
+ // Keep track of SSL status and other such things
+ bool _ssl;
+ SSL_CTX* _ssl_ctx;
+ SSL* _ssl_ssl;
+ SSL_METHOD* _ssl_meth;
+ private:
+
+ // Socket. This should really be a SOCKET (an alias for unsigned int*) on windows...
+ int _fd;
+
+ // In the server, a new source (XmlRpcServerConnection) is created
+ // for each connected client. When each connection is closed, the
+ // corresponding source object is deleted.
+ bool _deleteOnClose;
+
+ // In the client, keep connections open if you intend to make multiple calls.
+ bool _keepOpen;
+ };
+} // namespace XmlRpc
+
+#endif //_XMLRPCSOURCE_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcThread.cpp b/branches/lydia/xmlrpc++/XmlRpcThread.cpp
new file mode 100644
index 0000000..8a91d87
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcThread.cpp
@@ -0,0 +1,74 @@
+#if defined(XMLRPC_THREADS)
+
+#include "XmlRpcThread.h"
+
+#if defined(_WINDOWS)
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <process.h>
+#else
+# include <pthread.h>
+#endif
+
+
+using namespace XmlRpc;
+
+
+//! Destructor. Does not perform a join() (ie, the thread may continue to run).
+XmlRpcThread::~XmlRpcThread()
+{
+ if (_pThread)
+ {
+#if defined(_WINDOWS)
+ ::CloseHandle((HANDLE)_pThread);
+#else
+ ::pthread_detach((pthread_t)_pThread);
+#endif
+ _pThread = 0;
+ }
+}
+
+//! Execute the run method of the runnable object in a separate thread.
+//! Returns immediately in the calling thread.
+void
+XmlRpcThread::start()
+{
+ if ( ! _pThread)
+ {
+#if defined(_WINDOWS)
+ unsigned threadID;
+ _pThread = (HANDLE)_beginthreadex(NULL, 0, &runInThread, this, 0, &threadID);
+#else
+ ::pthread_create((pthread_t*) &_pThread, NULL, &runInThread, this);
+#endif
+ }
+}
+
+//! Waits until the thread exits.
+void
+XmlRpcThread::join()
+{
+ if (_pThread)
+ {
+#if defined(_WINDOWS)
+ ::WaitForSingleObject(_pThread, INFINITE);
+ ::CloseHandle(_pThread);
+#else
+ ::pthread_join((pthread_t)_pThread, 0);
+#endif
+ _pThread = 0;
+ }
+}
+
+//! Start the runnable going in a thread
+unsigned int
+XmlRpcThread::runInThread(void* pThread)
+{
+ XmlRpcThread* t = (XmlRpcThread*)pThread;
+ t->getRunnable()->run();
+ return 0;
+}
+
+#endif // XMLRPC_THREADS
+
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcThread.h b/branches/lydia/xmlrpc++/XmlRpcThread.h
new file mode 100644
index 0000000..6f4ec78
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcThread.h
@@ -0,0 +1,60 @@
+#ifndef _XMLRPCTHREAD_H_
+#define _XMLRPCTHREAD_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+namespace XmlRpc {
+
+ //! An abstract class providing an interface for objects that can run in a separate thread.
+ class XmlRpcRunnable {
+ public:
+ //! Code to be executed.
+ virtual void run() = 0;
+ }; // class XmlRpcRunnable
+
+
+ //! A simple platform-independent thread API implemented for posix and windows.
+ class XmlRpcThread {
+ public:
+ //! Construct a thread object. Not usable until setRunnable() has been called.
+ XmlRpcThread() : _runner(0), _pThread(0) {}
+
+ //! Construct a thread object.
+ XmlRpcThread(XmlRpcRunnable* runnable) : _runner(runnable), _pThread(0) {}
+
+ //! Destructor. Does not perform a join() (ie, the thread may continue to run).
+ ~XmlRpcThread();
+
+ //! Execute the run method of the runnable object in a separate thread.
+ //! Returns immediately in the calling thread.
+ void start();
+
+ //! Waits until the thread exits.
+ void join();
+
+ //! Access the runnable
+ XmlRpcRunnable* getRunnable() const { return _runner; }
+
+ //! Set the runnable
+ void setRunnable(XmlRpcRunnable* r) { _runner = r; }
+
+ private:
+
+ //! Start the runnable going in a thread
+ static unsigned int __stdcall runInThread(void* pThread);
+
+ //! Code to be executed
+ XmlRpcRunnable* _runner;
+
+ //! Native thread object
+ void* _pThread;
+
+ }; // class XmlRpcThread
+
+} // namespace XmlRpc
+
+#endif // _XMLRPCTHREAD_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcThreadedServer.cpp b/branches/lydia/xmlrpc++/XmlRpcThreadedServer.cpp
new file mode 100644
index 0000000..3f40e49
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcThreadedServer.cpp
@@ -0,0 +1,27 @@
+#if defined(XMLRPC_THREADS)
+
+#include "XmlRpcThreadedServer.h"
+//#include "XmlRpcServerConnection.h"
+
+
+using namespace XmlRpc;
+
+// executeRequestThreaded:
+// remove the serverConnection from the dispatcher (but don't close the socket)
+// push the request onto the request queue
+// (acquire the mutex, push_back request, release mutex, incr semaphore)
+//
+
+// worker::run
+// while ! stopped
+// pop a request off the request queue (block on semaphore/decr, acquire mutex, get request, rel)
+// executeRequest (parse, run, generate response)
+// notify the serverConnection that the response is available
+// (the serverConnection needs to add itself back to the dispatcher safely - mutex)
+
+// How do I interrupt the dispatcher if it is waiting in a select call?
+// i) Replace select with WaitForMultipleObjects, using WSAEventSelect to associate
+// each socket with an event object, and adding an additional "signal" event.
+//
+
+#endif // XMLRPC_THREADS
diff --git a/branches/lydia/xmlrpc++/XmlRpcThreadedServer.h b/branches/lydia/xmlrpc++/XmlRpcThreadedServer.h
new file mode 100644
index 0000000..bedbeec
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcThreadedServer.h
@@ -0,0 +1,67 @@
+
+#ifndef _XMLRPCTHREADEDSERVER_H_
+#define _XMLRPCTHREADEDSERVER_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <map>
+# include <vector>
+#endif
+
+
+#include "XmlRpcMutex.h"
+#include "XmlRpcServer.h"
+#include "XmlRpcThread.h"
+
+
+namespace XmlRpc {
+
+ //! A class to handle multiple simultaneous XML RPC requests
+ class XmlRpcThreadedServer : public XmlRpcServer {
+ public:
+
+ //! Create a server object with a specified number of worker threads.
+ XmlRpcThreadedServer(int nWorkers = 6) : _workers(nWorkers) {}
+
+
+ //! Execute a request
+
+ protected:
+
+ //! Each client request is assigned to one worker to handle.
+ //! Workers are executed on separate threads, and one worker may be
+ //! responsible for dispatching events to multiple client connections.
+ class Worker : XmlRpcRunnable {
+ public:
+ //! Constructor. Executes the run method in a separate thread.
+ Worker() { _thread.setRunnable(this); _thread.start(); }
+
+ //! Implement the Runnable interface
+ void run();
+
+ protected:
+
+ //! The thread this worker is running in.
+ XmlRpcThread _thread;
+
+ };
+
+
+ //! The worker pool
+ std::vector<Worker> _workers;
+
+
+ //! Serialize dispatcher access
+ XmlRpcMutex _mutex;
+
+
+ }; // class XmlRpcThreadedServer
+
+}
+
+#endif // _XMLRPCTHREADEDSERVER_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcUtil.cpp b/branches/lydia/xmlrpc++/XmlRpcUtil.cpp
new file mode 100644
index 0000000..a084814
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcUtil.cpp
@@ -0,0 +1,259 @@
+
+#include "XmlRpcUtil.h"
+
+#ifndef MAKEDEPEND
+# include <ctype.h>
+# include <iostream>
+# include <stdarg.h>
+# include <stdio.h>
+# include <string.h>
+#endif
+
+#include "XmlRpc.h"
+
+using namespace XmlRpc;
+
+
+//#define USE_WINDOWS_DEBUG // To make the error and log messages go to VC++ debug output
+#ifdef USE_WINDOWS_DEBUG
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+// Version id
+const char XmlRpc::XMLRPC_VERSION[] = "XMLRPC++ 0.8";
+
+// Default log verbosity: 0 for no messages through 5 (writes everything)
+int XmlRpcLogHandler::_verbosity = 0;
+
+// Default log handler
+static class DefaultLogHandler : public XmlRpcLogHandler {
+public:
+
+ void log(int level, const char* msg) {
+#ifdef USE_WINDOWS_DEBUG
+ if (level <= _verbosity) { OutputDebugString(msg); OutputDebugString("\n"); }
+#else
+ if (level <= _verbosity) std::cout << msg << std::endl;
+#endif
+ }
+
+} defaultLogHandler;
+
+// Message log singleton
+XmlRpcLogHandler* XmlRpcLogHandler::_logHandler = &defaultLogHandler;
+
+
+// Default error handler
+static class DefaultErrorHandler : public XmlRpcErrorHandler {
+public:
+
+ void error(const char* msg) {
+#ifdef USE_WINDOWS_DEBUG
+ OutputDebugString(msg); OutputDebugString("\n");
+#else
+ std::cerr << msg << std::endl;
+#endif
+ }
+} defaultErrorHandler;
+
+
+// Error handler singleton
+XmlRpcErrorHandler* XmlRpcErrorHandler::_errorHandler = &defaultErrorHandler;
+
+
+// Easy API for log verbosity
+int XmlRpc::getVerbosity() { return XmlRpcLogHandler::getVerbosity(); }
+void XmlRpc::setVerbosity(int level) { XmlRpcLogHandler::setVerbosity(level); }
+
+
+
+void XmlRpcUtil::log(int level, const char* fmt, ...)
+{
+ if (level <= XmlRpcLogHandler::getVerbosity())
+ {
+ va_list va;
+ char buf[1024];
+ va_start( va, fmt);
+ vsnprintf(buf,sizeof(buf)-1,fmt,va);
+ buf[sizeof(buf)-1] = 0;
+ XmlRpcLogHandler::getLogHandler()->log(level, buf);
+ }
+}
+
+
+void XmlRpcUtil::error(const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ char buf[1024];
+ vsnprintf(buf,sizeof(buf)-1,fmt,va);
+ buf[sizeof(buf)-1] = 0;
+ XmlRpcErrorHandler::getErrorHandler()->error(buf);
+}
+
+
+// Returns contents between <tag> and </tag>, updates offset to char after </tag>
+std::string
+XmlRpcUtil::parseTag(const char* tag, std::string const& xml, int* offset)
+{
+ if (*offset >= int(xml.length())) return std::string();
+ size_t istart = xml.find(tag, *offset);
+ if (istart == std::string::npos) return std::string();
+ istart += strlen(tag);
+ std::string etag = "</";
+ etag += tag + 1;
+ size_t iend = xml.find(etag, istart);
+ if (iend == std::string::npos) return std::string();
+
+ *offset = int(iend + etag.length());
+ return xml.substr(istart, iend-istart);
+}
+
+
+// Returns true if the tag is found and updates offset to the char after the tag
+bool
+XmlRpcUtil::findTag(const char* tag, std::string const& xml, int* offset)
+{
+ if (*offset >= int(xml.length())) return false;
+ size_t istart = xml.find(tag, *offset);
+ if (istart == std::string::npos)
+ return false;
+
+ *offset = int(istart + strlen(tag));
+ return true;
+}
+
+
+// Returns true if the tag is found at the specified offset (modulo any whitespace)
+// and updates offset to the char after the tag
+bool
+XmlRpcUtil::nextTagIs(const char* tag, std::string const& xml, int* offset)
+{
+ if (*offset >= int(xml.length())) return false;
+ const char* cp = xml.c_str() + *offset;
+ int nc = 0;
+ while (*cp && isspace(*cp)) {
+ ++cp;
+ ++nc;
+ }
+
+ int len = int(strlen(tag));
+ if (*cp && (strncmp(cp, tag, len) == 0)) {
+ *offset += nc + len;
+ return true;
+ }
+ return false;
+}
+
+// Returns the next tag and updates offset to the char after the tag, or empty string
+// if the next non-whitespace character is not '<'. Ignores parameters and values within
+// the tag entity.
+std::string
+XmlRpcUtil::getNextTag(std::string const& xml, int* offset)
+{
+ if (*offset >= int(xml.length())) return std::string();
+
+ const char* cp = xml.c_str() + size_t(*offset);
+ const char* startcp = cp;
+ while (*cp && isspace(*cp))
+ ++cp;
+
+
+ if (*cp != '<') return std::string();
+
+ // Tag includes the non-blank characters after <
+ const char* start = cp++;
+ while (*cp != '>' && *cp != 0 && ! isspace(*cp))
+ ++cp;
+
+ std::string s(start, cp-start+1);
+
+ if (*cp != '>') // Skip parameters and values
+ {
+ while (*cp != '>' && *cp != 0)
+ ++cp;
+
+ s[s.length()-1] = *cp;
+ }
+
+ *offset += int(cp - startcp + 1);
+ return s;
+}
+
+
+
+// xml encodings (xml-encoded entities are preceded with '&')
+static const char AMP = '&';
+static const char rawEntity[] = { '<', '>', '&', '\'', '\"', 0 };
+static const char* xmlEntity[] = { "lt;", "gt;", "amp;", "apos;", "quot;", 0 };
+static const int xmlEntLen[] = { 3, 3, 4, 5, 5 };
+
+
+// Replace xml-encoded entities with the raw text equivalents.
+
+std::string
+XmlRpcUtil::xmlDecode(const std::string& encoded)
+{
+ std::string::size_type iAmp = encoded.find(AMP);
+ if (iAmp == std::string::npos)
+ return encoded;
+
+ std::string decoded(encoded, 0, iAmp);
+ std::string::size_type iSize = encoded.size();
+ decoded.reserve(iSize);
+
+ const char* ens = encoded.c_str();
+ while (iAmp != iSize) {
+ if (encoded[iAmp] == AMP && iAmp+1 < iSize) {
+ int iEntity;
+ for (iEntity=0; xmlEntity[iEntity] != 0; ++iEntity)
+ //if (encoded.compare(iAmp+1, xmlEntLen[iEntity], xmlEntity[iEntity]) == 0)
+ if (strncmp(ens+iAmp+1, xmlEntity[iEntity], xmlEntLen[iEntity]) == 0)
+ {
+ decoded += rawEntity[iEntity];
+ iAmp += xmlEntLen[iEntity]+1;
+ break;
+ }
+ if (xmlEntity[iEntity] == 0) // unrecognized sequence
+ decoded += encoded[iAmp++];
+
+ } else {
+ decoded += encoded[iAmp++];
+ }
+ }
+
+ return decoded;
+}
+
+
+// Replace raw text with xml-encoded entities.
+
+std::string
+XmlRpcUtil::xmlEncode(const std::string& raw)
+{
+ std::string::size_type iRep = raw.find_first_of(rawEntity);
+ if (iRep == std::string::npos)
+ return raw;
+
+ std::string encoded(raw, 0, iRep);
+ std::string::size_type iSize = raw.size();
+
+ while (iRep != iSize) {
+ int iEntity;
+ for (iEntity=0; rawEntity[iEntity] != 0; ++iEntity)
+ if (raw[iRep] == rawEntity[iEntity])
+ {
+ encoded += AMP;
+ encoded += xmlEntity[iEntity];
+ break;
+ }
+ if (rawEntity[iEntity] == 0)
+ encoded += raw[iRep];
+ ++iRep;
+ }
+ return encoded;
+}
+
+
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcUtil.h b/branches/lydia/xmlrpc++/XmlRpcUtil.h
new file mode 100644
index 0000000..8128f72
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcUtil.h
@@ -0,0 +1,61 @@
+#ifndef _XMLRPCUTIL_H_
+#define _XMLRPCUTIL_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <string>
+#endif
+
+#if defined(_MSC_VER)
+# define snprintf _snprintf
+# define vsnprintf _vsnprintf
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+#elif defined(__BORLANDC__)
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+#endif
+
+namespace XmlRpc {
+
+ //! Utilities for XML parsing, encoding, and decoding and message handlers.
+ class XmlRpcUtil {
+ public:
+ // hokey xml parsing
+ //! Returns contents between <tag> and </tag>, updates offset to char after </tag>
+ static std::string parseTag(const char* tag, std::string const& xml, int* offset);
+
+ //! Returns true if the tag is found and updates offset to the char after the tag
+ static bool findTag(const char* tag, std::string const& xml, int* offset);
+
+ //! Returns the next tag and updates offset to the char after the tag, or empty string
+ //! if the next non-whitespace character is not '<'
+ static std::string getNextTag(std::string const& xml, int* offset);
+
+ //! Returns true if the tag is found at the specified offset (modulo any whitespace)
+ //! and updates offset to the char after the tag
+ static bool nextTagIs(const char* tag, std::string const& xml, int* offset);
+
+
+ //! Convert raw text to encoded xml.
+ static std::string xmlEncode(const std::string& raw);
+
+ //! Convert encoded xml to raw text
+ static std::string xmlDecode(const std::string& encoded);
+
+
+ //! Dump messages somewhere
+ static void log(int level, const char* fmt, ...);
+
+ //! Dump error messages somewhere
+ static void error(const char* fmt, ...);
+
+ };
+} // namespace XmlRpc
+
+#endif // _XMLRPCUTIL_H_
diff --git a/branches/lydia/xmlrpc++/XmlRpcValue.cpp b/branches/lydia/xmlrpc++/XmlRpcValue.cpp
new file mode 100644
index 0000000..69627a4
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcValue.cpp
@@ -0,0 +1,611 @@
+
+#include "XmlRpcValue.h"
+#include "XmlRpcException.h"
+#include "XmlRpcUtil.h"
+#include "base64.h"
+
+#ifndef MAKEDEPEND
+# include <iostream>
+# include <ostream>
+# include <stdlib.h>
+# include <stdio.h>
+#endif
+
+namespace XmlRpc {
+
+
+ static const char VALUE_TAG[] = "<value>";
+ static const char VALUE_ETAG[] = "</value>";
+
+ static const char BOOLEAN_TAG[] = "<boolean>";
+ static const char BOOLEAN_ETAG[] = "</boolean>";
+ static const char DOUBLE_TAG[] = "<double>";
+ static const char DOUBLE_ETAG[] = "</double>";
+ static const char INT_TAG[] = "<int>";
+ static const char I4_TAG[] = "<i4>";
+ static const char I4_ETAG[] = "</i4>";
+ static const char STRING_TAG[] = "<string>";
+ static const char DATETIME_TAG[] = "<dateTime.iso8601>";
+ static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
+ static const char BASE64_TAG[] = "<base64>";
+ static const char BASE64_ETAG[] = "</base64>";
+
+ static const char ARRAY_TAG[] = "<array>";
+ static const char DATA_TAG[] = "<data>";
+ static const char DATA_ETAG[] = "</data>";
+ static const char ARRAY_ETAG[] = "</array>";
+
+ static const char STRUCT_TAG[] = "<struct>";
+ static const char MEMBER_TAG[] = "<member>";
+ static const char NAME_TAG[] = "<name>";
+ static const char NAME_ETAG[] = "</name>";
+ static const char MEMBER_ETAG[] = "</member>";
+ static const char STRUCT_ETAG[] = "</struct>";
+
+
+
+ // Format strings
+ std::string XmlRpcValue::_doubleFormat("%f");
+
+
+
+ // Clean up
+ void XmlRpcValue::invalidate()
+ {
+ switch (_type) {
+ case TypeString: delete _value.asString; break;
+ case TypeDateTime: delete _value.asTime; break;
+ case TypeBase64: delete _value.asBinary; break;
+ case TypeArray: delete _value.asArray; break;
+ case TypeStruct: delete _value.asStruct; break;
+ default: break;
+ }
+ _type = TypeInvalid;
+ _value.asBinary = 0;
+ }
+
+
+ // Type checking
+ void XmlRpcValue::assertTypeOrInvalid(Type t)
+ {
+ if (_type == TypeInvalid)
+ {
+ _type = t;
+ switch (_type) { // Ensure there is a valid value for the type
+ case TypeString: _value.asString = new std::string(); break;
+ case TypeDateTime: _value.asTime = new struct tm(); break;
+ case TypeBase64: _value.asBinary = new BinaryData(); break;
+ case TypeArray: _value.asArray = new ValueArray(); break;
+ case TypeStruct: _value.asStruct = new ValueStruct(); break;
+ default: _value.asBinary = 0; break;
+ }
+ }
+ else if (_type != t)
+ throw XmlRpcException("type error");
+ }
+
+ void XmlRpcValue::assertArray(int size) const
+ {
+ if (_type != TypeArray)
+ throw XmlRpcException("type error: expected an array");
+ else if (int(_value.asArray->size()) < size)
+ throw XmlRpcException("range error: array index too large");
+ }
+
+
+ void XmlRpcValue::assertArray(int size)
+ {
+ if (_type == TypeInvalid) {
+ _type = TypeArray;
+ _value.asArray = new ValueArray(size);
+ } else if (_type == TypeArray) {
+ if (int(_value.asArray->size()) < size)
+ _value.asArray->resize(size);
+ } else
+ throw XmlRpcException("type error: expected an array");
+ }
+
+ void XmlRpcValue::assertStruct()
+ {
+ if (_type == TypeInvalid) {
+ _type = TypeStruct;
+ _value.asStruct = new ValueStruct();
+ } else if (_type != TypeStruct)
+ throw XmlRpcException("type error: expected a struct");
+ }
+
+
+ // Operators
+ XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
+ {
+ if (this != &rhs)
+ {
+ invalidate();
+ _type = rhs._type;
+ switch (_type) {
+ case TypeBoolean: _value.asBool = rhs._value.asBool; break;
+ case TypeInt: _value.asInt = rhs._value.asInt; break;
+ case TypeDouble: _value.asDouble = rhs._value.asDouble; break;
+ case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break;
+ case TypeString: _value.asString = new std::string(*rhs._value.asString); break;
+ case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break;
+ case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break;
+ case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break;
+ default: _value.asBinary = 0; break;
+ }
+ }
+ return *this;
+ }
+
+
+ // Predicate for tm equality
+ static bool tmEq(struct tm const& t1, struct tm const& t2) {
+ return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
+ t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday &&
+ t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
+ }
+
+ bool XmlRpcValue::operator==(XmlRpcValue const& other) const
+ {
+ if (_type != other._type)
+ return false;
+
+ switch (_type) {
+ case TypeBoolean: return ( !_value.asBool && !other._value.asBool) ||
+ ( _value.asBool && other._value.asBool);
+ case TypeInt: return _value.asInt == other._value.asInt;
+ case TypeDouble: return _value.asDouble == other._value.asDouble;
+ case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime);
+ case TypeString: return *_value.asString == *other._value.asString;
+ case TypeBase64: return *_value.asBinary == *other._value.asBinary;
+ case TypeArray: return *_value.asArray == *other._value.asArray;
+
+ // The map<>::operator== requires the definition of value< for kcc
+ case TypeStruct: //return *_value.asStruct == *other._value.asStruct;
+ {
+ if (_value.asStruct->size() != other._value.asStruct->size())
+ return false;
+
+ ValueStruct::const_iterator it1=_value.asStruct->begin();
+ ValueStruct::const_iterator it2=other._value.asStruct->begin();
+ while (it1 != _value.asStruct->end()) {
+ const XmlRpcValue& v1 = it1->second;
+ const XmlRpcValue& v2 = it2->second;
+ if ( ! (v1 == v2))
+ return false;
+ it1++;
+ it2++;
+ }
+ return true;
+ }
+ default: break;
+ }
+ return true; // Both invalid values ...
+ }
+
+ bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
+ {
+ return !(*this == other);
+ }
+
+
+ // Works for strings, binary data, arrays, and structs.
+ int XmlRpcValue::size() const
+ {
+ switch (_type) {
+ case TypeString: return int(_value.asString->size());
+ case TypeBase64: return int(_value.asBinary->size());
+ case TypeArray: return int(_value.asArray->size());
+ case TypeStruct: return int(_value.asStruct->size());
+ default: break;
+ }
+
+ throw XmlRpcException("type error");
+ }
+
+ // Checks for existence of struct member
+ bool XmlRpcValue::hasMember(const std::string& name) const
+ {
+ return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end();
+ }
+
+ // Set the value from xml. The chars at *offset into valueXml
+ // should be the start of a <value> tag. Destroys any existing value.
+ bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset)
+ {
+ int savedOffset = *offset;
+
+ invalidate();
+ if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset))
+ return false; // Not a value, offset not updated
+
+ int afterValueOffset = *offset;
+ std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset);
+ bool result = false;
+ if (typeTag == BOOLEAN_TAG)
+ result = boolFromXml(valueXml, offset);
+ else if (typeTag == I4_TAG || typeTag == INT_TAG)
+ result = intFromXml(valueXml, offset);
+ else if (typeTag == DOUBLE_TAG)
+ result = doubleFromXml(valueXml, offset);
+ else if (typeTag.empty() || typeTag == STRING_TAG)
+ result = stringFromXml(valueXml, offset);
+ else if (typeTag == DATETIME_TAG)
+ result = timeFromXml(valueXml, offset);
+ else if (typeTag == BASE64_TAG)
+ result = binaryFromXml(valueXml, offset);
+ else if (typeTag == ARRAY_TAG)
+ result = arrayFromXml(valueXml, offset);
+ else if (typeTag == STRUCT_TAG)
+ result = structFromXml(valueXml, offset);
+ // Watch for empty/blank strings with no <string>tag
+ else if (typeTag == VALUE_ETAG)
+ {
+ *offset = afterValueOffset; // back up & try again
+ result = stringFromXml(valueXml, offset);
+ }
+
+ if (result) // Skip over the </value> tag
+ XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset);
+ else // Unrecognized tag after <value>
+ *offset = savedOffset;
+
+ return result;
+ }
+
+ // Encode the Value in xml
+ std::string XmlRpcValue::toXml() const
+ {
+ switch (_type) {
+ case TypeBoolean: return boolToXml();
+ case TypeInt: return intToXml();
+ case TypeDouble: return doubleToXml();
+ case TypeString: return stringToXml();
+ case TypeDateTime: return timeToXml();
+ case TypeBase64: return binaryToXml();
+ case TypeArray: return arrayToXml();
+ case TypeStruct: return structToXml();
+ default: break;
+ }
+ return std::string(); // Invalid value
+ }
+
+
+ // Boolean
+ bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset)
+ {
+ const char* valueStart = valueXml.c_str() + *offset;
+ char* valueEnd;
+ long ivalue = strtol(valueStart, &valueEnd, 10);
+ if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1))
+ return false;
+
+ _type = TypeBoolean;
+ _value.asBool = (ivalue == 1);
+ *offset += int(valueEnd - valueStart);
+ return true;
+ }
+
+ std::string XmlRpcValue::boolToXml() const
+ {
+ std::string xml = VALUE_TAG;
+ xml += BOOLEAN_TAG;
+ xml += (_value.asBool ? "1" : "0");
+ xml += BOOLEAN_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+ // Int
+ bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset)
+ {
+ const char* valueStart = valueXml.c_str() + *offset;
+ char* valueEnd;
+ long ivalue = strtol(valueStart, &valueEnd, 10);
+ if (valueEnd == valueStart)
+ return false;
+
+ _type = TypeInt;
+ _value.asInt = int(ivalue);
+ *offset += int(valueEnd - valueStart);
+ return true;
+ }
+
+ std::string XmlRpcValue::intToXml() const
+ {
+ char buf[256];
+ snprintf(buf, sizeof(buf)-1, "%d", _value.asInt);
+ buf[sizeof(buf)-1] = 0;
+ std::string xml = VALUE_TAG;
+ xml += I4_TAG;
+ xml += buf;
+ xml += I4_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+ // Double
+ bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset)
+ {
+ const char* valueStart = valueXml.c_str() + *offset;
+ char* valueEnd;
+ double dvalue = strtod(valueStart, &valueEnd);
+ if (valueEnd == valueStart)
+ return false;
+
+ _type = TypeDouble;
+ _value.asDouble = dvalue;
+ *offset += int(valueEnd - valueStart);
+ return true;
+ }
+
+ std::string XmlRpcValue::doubleToXml() const
+ {
+ char buf[256];
+ snprintf(buf, sizeof(buf)-1, getDoubleFormat().c_str(), _value.asDouble);
+ buf[sizeof(buf)-1] = 0;
+
+ std::string xml = VALUE_TAG;
+ xml += DOUBLE_TAG;
+ xml += buf;
+ xml += DOUBLE_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+ // String
+ bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset)
+ {
+ size_t valueEnd = valueXml.find('<', *offset);
+ if (valueEnd == std::string::npos)
+ return false; // No end tag;
+
+ _type = TypeString;
+ _value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset)));
+ *offset += int(_value.asString->length());
+ return true;
+ }
+
+ std::string XmlRpcValue::stringToXml() const
+ {
+ std::string xml = VALUE_TAG;
+ //xml += STRING_TAG; optional
+ xml += XmlRpcUtil::xmlEncode(*_value.asString);
+ //xml += STRING_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+ // DateTime (stored as a struct tm)
+ bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset)
+ {
+ size_t valueEnd = valueXml.find('<', *offset);
+ if (valueEnd == std::string::npos)
+ return false; // No end tag;
+
+ std::string stime = valueXml.substr(*offset, valueEnd-*offset);
+
+ struct tm t;
+ if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6)
+ return false;
+
+ t.tm_year -= 1900;
+ t.tm_isdst = -1;
+ _type = TypeDateTime;
+ _value.asTime = new struct tm(t);
+ *offset += int(stime.length());
+ return true;
+ }
+
+ std::string XmlRpcValue::timeToXml() const
+ {
+ struct tm* t = _value.asTime;
+ char buf[20];
+ snprintf(buf, sizeof(buf)-1, "%04d%02d%02dT%02d:%02d:%02d",
+ 1900+t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
+ buf[sizeof(buf)-1] = 0;
+
+ std::string xml = VALUE_TAG;
+ xml += DATETIME_TAG;
+ xml += buf;
+ xml += DATETIME_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+
+ // Base64
+ bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset)
+ {
+ size_t valueEnd = valueXml.find('<', *offset);
+ if (valueEnd == std::string::npos)
+ return false; // No end tag;
+
+ _type = TypeBase64;
+ std::string asString = valueXml.substr(*offset, valueEnd-*offset);
+ _value.asBinary = new BinaryData();
+ // check whether base64 encodings can contain chars xml encodes...
+
+ // convert from base64 to binary
+ int iostatus = 0;
+ base64<char> decoder;
+ std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary));
+ decoder.get(asString.begin(), asString.end(), ins, iostatus);
+
+ *offset += int(asString.length());
+ return true;
+ }
+
+
+ std::string XmlRpcValue::binaryToXml() const
+ {
+ // convert to base64
+ std::vector<char> base64data;
+ int iostatus = 0;
+ base64<char> encoder;
+ std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
+ encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf());
+
+ // Wrap with xml
+ std::string xml = VALUE_TAG;
+ xml += BASE64_TAG;
+ xml.append(base64data.begin(), base64data.end());
+ xml += BASE64_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+
+ // Array
+ bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset)
+ {
+ if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset))
+ return false;
+
+ _type = TypeArray;
+ _value.asArray = new ValueArray;
+ XmlRpcValue v;
+ while (v.fromXml(valueXml, offset))
+ _value.asArray->push_back(v); // copy...
+
+ // Skip the trailing </data>
+ (void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset);
+ return true;
+ }
+
+
+ // In general, its preferable to generate the xml of each element of the
+ // array as it is needed rather than glomming up one big string.
+ std::string XmlRpcValue::arrayToXml() const
+ {
+ std::string xml = VALUE_TAG;
+ xml += ARRAY_TAG;
+ xml += DATA_TAG;
+
+ int s = int(_value.asArray->size());
+ for (int i=0; i<s; ++i)
+ xml += _value.asArray->at(i).toXml();
+
+ xml += DATA_ETAG;
+ xml += ARRAY_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+
+ // Struct
+ bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset)
+ {
+ _type = TypeStruct;
+ _value.asStruct = new ValueStruct;
+
+ while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) {
+ // name
+ const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset);
+ // value
+ XmlRpcValue val(valueXml, offset);
+ if ( ! val.valid()) {
+ invalidate();
+ return false;
+ }
+ const std::pair<const std::string, XmlRpcValue> p(name, val);
+ _value.asStruct->insert(p);
+
+ (void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset);
+ }
+ return true;
+ }
+
+
+ // In general, its preferable to generate the xml of each element
+ // as it is needed rather than glomming up one big string.
+ std::string XmlRpcValue::structToXml() const
+ {
+ std::string xml = VALUE_TAG;
+ xml += STRUCT_TAG;
+
+ ValueStruct::const_iterator it;
+ for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) {
+ xml += MEMBER_TAG;
+ xml += NAME_TAG;
+ xml += XmlRpcUtil::xmlEncode(it->first);
+ xml += NAME_ETAG;
+ xml += it->second.toXml();
+ xml += MEMBER_ETAG;
+ }
+
+ xml += STRUCT_ETAG;
+ xml += VALUE_ETAG;
+ return xml;
+ }
+
+
+
+ // Write the value without xml encoding it
+ std::ostream& XmlRpcValue::write(std::ostream& os) const {
+ switch (_type) {
+ default: break;
+ case TypeBoolean: os << _value.asBool; break;
+ case TypeInt: os << _value.asInt; break;
+ case TypeDouble: os << _value.asDouble; break;
+ case TypeString: os << *_value.asString; break;
+ case TypeDateTime:
+ {
+ struct tm* t = _value.asTime;
+ char buf[20];
+ snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
+ t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
+ buf[sizeof(buf)-1] = 0;
+ os << buf;
+ break;
+ }
+ case TypeBase64:
+ {
+ int iostatus = 0;
+ std::ostreambuf_iterator<char> out(os);
+ base64<char> encoder;
+ encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf());
+ break;
+ }
+ case TypeArray:
+ {
+ int s = int(_value.asArray->size());
+ os << '{';
+ for (int i=0; i<s; ++i)
+ {
+ if (i > 0) os << ',';
+ _value.asArray->at(i).write(os);
+ }
+ os << '}';
+ break;
+ }
+ case TypeStruct:
+ {
+ os << '[';
+ ValueStruct::const_iterator it;
+ for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it)
+ {
+ if (it!=_value.asStruct->begin()) os << ',';
+ os << it->first << ':';
+ it->second.write(os);
+ }
+ os << ']';
+ break;
+ }
+
+ }
+
+ return os;
+ }
+
+} // namespace XmlRpc
+
+
+// ostream
+std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v)
+{
+ // If you want to output in xml format:
+ //return os << v.toXml();
+ return v.write(os);
+}
+
diff --git a/branches/lydia/xmlrpc++/XmlRpcValue.h b/branches/lydia/xmlrpc++/XmlRpcValue.h
new file mode 100644
index 0000000..b490ae4
--- /dev/null
+++ b/branches/lydia/xmlrpc++/XmlRpcValue.h
@@ -0,0 +1,259 @@
+
+#ifndef _XMLRPCVALUE_H_
+#define _XMLRPCVALUE_H_
+//
+// XmlRpc++ Copyright (c) 2002-2003 by Chris Morley
+//
+
+#if defined(_MSC_VER)
+# pragma warning(disable:4786) // identifier was truncated in debug info
+#endif
+
+#ifndef MAKEDEPEND
+# include <map>
+# include <string>
+# include <vector>
+# include <time.h>
+#endif
+
+namespace XmlRpc {
+
+ //! A class to represent RPC arguments and results.
+ //! Each XmlRpcValue object contains a typed value,
+ //! where the type is determined by the initial value
+ //! assigned to the object.
+ // should probably refcount them...
+ class XmlRpcValue {
+ public:
+
+ //! XmlRpcValue types
+ enum Type {
+ TypeInvalid,
+ TypeBoolean,
+ TypeInt,
+ TypeDouble,
+ TypeString,
+ TypeDateTime,
+ TypeBase64,
+ TypeArray,
+ TypeStruct
+ };
+
+ // Non-primitive types
+ typedef std::vector<char> BinaryData;
+ typedef std::vector<XmlRpcValue> ValueArray;
+ typedef std::map<std::string, XmlRpcValue> ValueStruct;
+
+
+ // Constructors
+ //! Construct an empty XmlRpcValue
+ XmlRpcValue() : _type(TypeInvalid) { _value.asBinary = 0; }
+
+ //! Construct an XmlRpcValue with a bool value
+ XmlRpcValue(bool value) : _type(TypeBoolean) { _value.asBool = value; }
+
+ //! Construct an XmlRpcValue with an int value
+ XmlRpcValue(int value) : _type(TypeInt) { _value.asInt = value; }
+
+ //! Construct an XmlRpcValue with a double value
+ XmlRpcValue(double value) : _type(TypeDouble) { _value.asDouble = value; }
+
+ //! Construct an XmlRpcValue with a string value
+ XmlRpcValue(std::string const& value) : _type(TypeString)
+ { _value.asString = new std::string(value); }
+
+ //! Construct an XmlRpcValue with a string value.
+ //! @param value A null-terminated (C) string.
+ XmlRpcValue(const char* value) : _type(TypeString)
+ { _value.asString = new std::string(value); }
+
+ //! Construct an XmlRpcValue with a date/time value.
+ //! @param value A pointer to a struct tm (see localtime)
+ XmlRpcValue(struct tm* value) : _type(TypeDateTime)
+ { _value.asTime = new struct tm(*value); }
+
+ //! Construct an XmlRpcValue with a binary data value
+ //! @param value A pointer to data
+ //! @param nBytes The length of the data pointed to, in bytes
+ XmlRpcValue(void* value, int nBytes) : _type(TypeBase64)
+ {
+ _value.asBinary = new BinaryData((char*)value, ((char*)value)+nBytes);
+ }
+
+ //! Construct from xml, beginning at *offset chars into the string, updates offset
+ XmlRpcValue(std::string const& xml, int* offset) : _type(TypeInvalid)
+ { if ( ! fromXml(xml,offset)) _type = TypeInvalid; }
+
+ //! Copy constructor
+ XmlRpcValue(XmlRpcValue const& rhs) : _type(TypeInvalid) { *this = rhs; }
+
+ //! Destructor (make virtual if you want to subclass)
+ /*virtual*/ ~XmlRpcValue() { invalidate(); }
+
+ //! Erase the current value
+ void clear() { invalidate(); }
+
+ // Operators
+ //! Assignment from one XmlRpcValue to this one.
+ //! @param rhs The value in rhs is copied to this value.
+ XmlRpcValue& operator=(XmlRpcValue const& rhs);
+
+ //! Assign an int to this XmlRpcValue.
+ XmlRpcValue& operator=(int const& rhs) { return operator=(XmlRpcValue(rhs)); }
+
+ //! Assign a double to this XmlRpcValue.
+ XmlRpcValue& operator=(double const& rhs) { return operator=(XmlRpcValue(rhs)); }
+
+ //! Assign a string to this XmlRpcValue.
+ XmlRpcValue& operator=(const char* rhs) { return operator=(XmlRpcValue(std::string(rhs))); }
+
+ //! Tests two XmlRpcValues for equality
+ bool operator==(XmlRpcValue const& other) const;
+
+ //! Tests two XmlRpcValues for inequality
+ bool operator!=(XmlRpcValue const& other) const;
+
+ //! Treat an XmlRpcValue as a bool.
+ //! Throws XmlRpcException if the value is initialized to
+ //! a type that is not TypeBoolean.
+ operator bool&() { assertTypeOrInvalid(TypeBoolean); return _value.asBool; }
+
+ //! Treat an XmlRpcValue as an int.
+ //! Throws XmlRpcException if the value is initialized to
+ //! a type that is not TypeInt.
+ operator int&() { assertTypeOrInvalid(TypeInt); return _value.asInt; }
+
+ //! Treat an XmlRpcValue as a double.
+ //! Throws XmlRpcException if the value is initialized to
+ //! a type that is not TypeDouble.
+ operator double&() { assertTypeOrInvalid(TypeDouble); return _value.asDouble; }
+
+ //! Treat an XmlRpcValue as a string.
+ //! Throws XmlRpcException if the value is initialized to
+ //! a type that is not TypeString.
+ operator std::string&() { assertTypeOrInvalid(TypeString); return *_value.asString; }
+
+ //! Access the BinaryData value.
+ //! Throws XmlRpcException if the value is initialized to
+ //! a type that is not TypeBase64.
+ operator BinaryData&() { assertTypeOrInvalid(TypeBase64); return *_value.asBinary; }
+
+ //! Access the DateTime value.
+ //! Throws XmlRpcException if the value is initialized to
+ //! a type that is not TypeDateTime.
+ operator struct tm&() { assertTypeOrInvalid(TypeDateTime); return *_value.asTime; }
+
+
+ //! Const array value accessor.
+ //! Access the ith value of the array.
+ //! Throws XmlRpcException if the value is not an array or if the index i is
+ //! not a valid index for the array.
+ XmlRpcValue const& operator[](int i) const { assertArray(i+1); return _value.asArray->at(i); }
+
+ //! Array value accessor.
+ //! Access the ith value of the array, growing the array if necessary.
+ //! Throws XmlRpcException if the value is not an array.
+ XmlRpcValue& operator[](int i) { assertArray(i+1); return _value.asArray->at(i); }
+
+ //! Struct entry accessor.
+ //! Returns the value associated with the given entry, creating one if necessary.
+ XmlRpcValue& operator[](std::string const& k) { assertStruct(); return (*_value.asStruct)[k]; }
+
+ //! Struct entry accessor.
+ //! Returns the value associated with the given entry, creating one if necessary.
+ XmlRpcValue& operator[](const char* k) { assertStruct(); std::string s(k); return (*_value.asStruct)[s]; }
+
+ //! Access the struct value map.
+ //! Can be used to iterate over the entries in the map to find all defined entries.
+ operator ValueStruct const&() { assertStruct(); return *_value.asStruct; }
+
+ // Accessors
+ //! Return true if the value has been set to something.
+ bool valid() const { return _type != TypeInvalid; }
+
+ //! Return the type of the value stored. \see Type.
+ Type const &getType() const { return _type; }
+
+ //! Return the size for string, base64, array, and struct values.
+ int size() const;
+
+ //! Specify the size for array values. Array values will grow beyond this size if needed.
+ void setSize(int size) { assertArray(size); }
+
+ //! Check for the existence of a struct member by name.
+ bool hasMember(const std::string& name) const;
+
+ //! Decode xml. Destroys any existing value.
+ bool fromXml(std::string const& valueXml, int* offset);
+
+ //! Encode the Value in xml
+ std::string toXml() const;
+
+ //! Write the value (no xml encoding)
+ std::ostream& write(std::ostream& os) const;
+
+ // Formatting
+ //! Return the format used to write double values.
+ static std::string const& getDoubleFormat() { return _doubleFormat; }
+
+ //! Specify the format used to write double values.
+ static void setDoubleFormat(const char* f) { _doubleFormat = f; }
+
+
+ protected:
+ // Clean up
+ void invalidate();
+
+ // Type checking
+ void assertTypeOrInvalid(Type t);
+ void assertArray(int size) const;
+ void assertArray(int size);
+ void assertStruct();
+
+ // XML decoding
+ bool boolFromXml(std::string const& valueXml, int* offset);
+ bool intFromXml(std::string const& valueXml, int* offset);
+ bool doubleFromXml(std::string const& valueXml, int* offset);
+ bool stringFromXml(std::string const& valueXml, int* offset);
+ bool timeFromXml(std::string const& valueXml, int* offset);
+ bool binaryFromXml(std::string const& valueXml, int* offset);
+ bool arrayFromXml(std::string const& valueXml, int* offset);
+ bool structFromXml(std::string const& valueXml, int* offset);
+
+ // XML encoding
+ std::string boolToXml() const;
+ std::string intToXml() const;
+ std::string doubleToXml() const;
+ std::string stringToXml() const;
+ std::string timeToXml() const;
+ std::string binaryToXml() const;
+ std::string arrayToXml() const;
+ std::string structToXml() const;
+
+ // Format strings
+ static std::string _doubleFormat;
+
+ // Type tag and values
+ Type _type;
+
+ // At some point I will split off Arrays and Structs into
+ // separate ref-counted objects for more efficient copying.
+ union {
+ bool asBool;
+ int asInt;
+ double asDouble;
+ struct tm* asTime;
+ std::string* asString;
+ BinaryData* asBinary;
+ ValueArray* asArray;
+ ValueStruct* asStruct;
+ } _value;
+
+ };
+} // namespace XmlRpc
+
+
+std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v);
+
+
+#endif // _XMLRPCVALUE_H_
diff --git a/branches/lydia/xmlrpc++/base64.h b/branches/lydia/xmlrpc++/base64.h
new file mode 100644
index 0000000..519ee0f
--- /dev/null
+++ b/branches/lydia/xmlrpc++/base64.h
@@ -0,0 +1,379 @@
+
+
+// base64.hpp
+// Autor Konstantin Pilipchuk
+// mailto:lostd@ukr.net
+//
+//
+
+#if !defined(__BASE64_H_INCLUDED__)
+#define __BASE64_H_INCLUDED__ 1
+
+#ifndef MAKEDEPEND
+# include <iterator>
+#endif
+
+static
+int _base64Chars[]= {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
+ '0','1','2','3','4','5','6','7','8','9',
+ '+','/' };
+
+
+#define _0000_0011 0x03
+#define _1111_1100 0xFC
+#define _1111_0000 0xF0
+#define _0011_0000 0x30
+#define _0011_1100 0x3C
+#define _0000_1111 0x0F
+#define _1100_0000 0xC0
+#define _0011_1111 0x3F
+
+#define _EQUAL_CHAR (-1)
+#define _UNKNOWN_CHAR (-2)
+
+#define _IOS_FAILBIT std::ios_base::failbit
+#define _IOS_EOFBIT std::ios_base::eofbit
+#define _IOS_BADBIT std::ios_base::badbit
+#define _IOS_GOODBIT std::ios_base::goodbit
+
+// TEMPLATE CLASS base64_put
+template<class _E = char, class _Tr = std::char_traits<_E> >
+class base64
+{
+public:
+
+ typedef unsigned char byte_t;
+ typedef _E char_type;
+ typedef _Tr traits_type;
+
+ // base64 requires max line length <= 72 characters
+ // you can fill end of line
+ // it may be crlf, crlfsp, noline or other class like it
+
+
+ struct crlf
+ {
+ template<class _OI>
+ _OI operator()(_OI _To) const{
+ *_To = _Tr::to_char_type('\r'); ++_To;
+ *_To = _Tr::to_char_type('\n'); ++_To;
+
+ return (_To);
+ }
+ };
+
+
+ struct crlfsp
+ {
+ template<class _OI>
+ _OI operator()(_OI _To) const{
+ *_To = _Tr::to_char_type('\r'); ++_To;
+ *_To = _Tr::to_char_type('\n'); ++_To;
+ *_To = _Tr::to_char_type(' '); ++_To;
+
+ return (_To);
+ }
+ };
+
+ struct noline
+ {
+ template<class _OI>
+ _OI operator()(_OI _To) const{
+ return (_To);
+ }
+ };
+
+ struct three2four
+ {
+ void zero()
+ {
+ _data[0] = 0;
+ _data[1] = 0;
+ _data[2] = 0;
+ }
+
+ byte_t get_0() const
+ {
+ return _data[0];
+ }
+ byte_t get_1() const
+ {
+ return _data[1];
+ }
+ byte_t get_2() const
+ {
+ return _data[2];
+ }
+
+ void set_0(byte_t _ch)
+ {
+ _data[0] = _ch;
+ }
+
+ void set_1(byte_t _ch)
+ {
+ _data[1] = _ch;
+ }
+
+ void set_2(byte_t _ch)
+ {
+ _data[2] = _ch;
+ }
+
+ // 0000 0000 1111 1111 2222 2222
+ // xxxx xxxx xxxx xxxx xxxx xxxx
+ // 0000 0011 1111 2222 2233 3333
+
+ int b64_0() const {return (_data[0] & _1111_1100) >> 2;}
+ int b64_1() const {return ((_data[0] & _0000_0011) << 4) + ((_data[1] & _1111_0000)>>4);}
+ int b64_2() const {return ((_data[1] & _0000_1111) << 2) + ((_data[2] & _1100_0000)>>6);}
+ int b64_3() const {return (_data[2] & _0011_1111);}
+
+ void b64_0(int _ch) {_data[0] = ((_ch & _0011_1111) << 2) | (_0000_0011 & _data[0]);}
+
+ void b64_1(int _ch) {
+ _data[0] = ((_ch & _0011_0000) >> 4) | (_1111_1100 & _data[0]);
+ _data[1] = ((_ch & _0000_1111) << 4) | (_0000_1111 & _data[1]); }
+
+ void b64_2(int _ch) {
+ _data[1] = ((_ch & _0011_1100) >> 2) | (_1111_0000 & _data[1]);
+ _data[2] = ((_ch & _0000_0011) << 6) | (_0011_1111 & _data[2]); }
+
+ void b64_3(int _ch){
+ _data[2] = (_ch & _0011_1111) | (_1100_0000 & _data[2]);}
+
+ private:
+ byte_t _data[3];
+
+ };
+
+
+
+
+ template<class _II, class _OI, class _State, class _Endline>
+ _II put(_II _First, _II _Last, _OI _To, _State& _St, _Endline _Endl) const
+ {
+ three2four _3to4;
+ int line_octets = 0;
+
+ while(_First != _Last)
+ {
+ _3to4.zero();
+
+ // берём по 3 символа
+ _3to4.set_0(*_First);
+ _First++;
+
+ if(_First == _Last)
+ {
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
+ *_To = _Tr::to_char_type('='); ++_To;
+ *_To = _Tr::to_char_type('='); ++_To;
+ goto __end;
+ }
+
+ _3to4.set_1(*_First);
+ _First++;
+
+ if(_First == _Last)
+ {
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To;
+ *_To = _Tr::to_char_type('='); ++_To;
+ goto __end;
+ }
+
+ _3to4.set_2(*_First);
+ _First++;
+
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To;
+ *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_3()]); ++_To;
+
+ if(line_octets == 17) // base64 позволяет длину строки не более 72 символов
+ {
+ //_To = _Endl(_To);
+ *_To = '\n'; ++_To;
+ line_octets = 0;
+ }
+ else
+ ++line_octets;
+ }
+
+ __end: ;
+
+ return (_First);
+
+ }
+
+
+ template<class _II, class _OI, class _State>
+ _II get(_II _First, _II _Last, _OI _To, _State& _St) const
+ {
+ three2four _3to4;
+ int _Char;
+
+ while(_First != _Last)
+ {
+
+ // Take octet
+ _3to4.zero();
+
+ // -- 0 --
+ // Search next valid char...
+ while((_Char = _getCharType(*_First)) < 0 && _Char == _UNKNOWN_CHAR)
+ {
+ if(++_First == _Last)
+ {
+ _St |= _IOS_FAILBIT|_IOS_EOFBIT; return _First; // unexpected EOF
+ }
+ }
+
+ if(_Char == _EQUAL_CHAR){
+ // Error! First character in octet can't be '='
+ _St |= _IOS_FAILBIT;
+ return _First;
+ }
+ else
+ _3to4.b64_0(_Char);
+
+
+ // -- 1 --
+ // Search next valid char...
+ while(++_First != _Last)
+ if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
+ break;
+
+ if(_First == _Last) {
+ _St |= _IOS_FAILBIT|_IOS_EOFBIT; // unexpected EOF
+ return _First;
+ }
+
+ if(_Char == _EQUAL_CHAR){
+ // Error! Second character in octet can't be '='
+ _St |= _IOS_FAILBIT;
+ return _First;
+ }
+ else
+ _3to4.b64_1(_Char);
+
+
+ // -- 2 --
+ // Search next valid char...
+ while(++_First != _Last)
+ if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
+ break;
+
+ if(_First == _Last) {
+ // Error! Unexpected EOF. Must be '=' or base64 character
+ _St |= _IOS_FAILBIT|_IOS_EOFBIT;
+ return _First;
+ }
+
+ if(_Char == _EQUAL_CHAR){
+ // OK!
+ _3to4.b64_2(0);
+ _3to4.b64_3(0);
+
+ // chek for EOF
+ if(++_First == _Last)
+ {
+ // Error! Unexpected EOF. Must be '='. Ignore it.
+ //_St |= _IOS_BADBIT|_IOS_EOFBIT;
+ _St |= _IOS_EOFBIT;
+ }
+ else
+ if(_getCharType(*_First) != _EQUAL_CHAR)
+ {
+ // Error! Must be '='. Ignore it.
+ //_St |= _IOS_BADBIT;
+ }
+ else
+ ++_First; // Skip '='
+
+ // write 1 byte to output
+ *_To = (byte_t) _3to4.get_0();
+ return _First;
+ }
+ else
+ _3to4.b64_2(_Char);
+
+
+ // -- 3 --
+ // Search next valid char...
+ while(++_First != _Last)
+ if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
+ break;
+
+ if(_First == _Last) {
+ // Unexpected EOF. It's error. But ignore it.
+ //_St |= _IOS_FAILBIT|_IOS_EOFBIT;
+ _St |= _IOS_EOFBIT;
+
+ return _First;
+ }
+
+ if(_Char == _EQUAL_CHAR)
+ {
+ // OK!
+ _3to4.b64_3(0);
+
+ // write to output 2 bytes
+ *_To = (byte_t) _3to4.get_0();
+ *_To = (byte_t) _3to4.get_1();
+
+ ++_First; // set position to next character
+
+ return _First;
+ }
+ else
+ _3to4.b64_3(_Char);
+
+
+ // write to output 3 bytes
+ *_To = (byte_t) _3to4.get_0();
+ *_To = (byte_t) _3to4.get_1();
+ *_To = (byte_t) _3to4.get_2();
+
+ ++_First;
+
+
+ } // while(_First != _Last)
+
+ return (_First);
+ }
+
+protected:
+
+ int _getCharType(int _Ch) const
+ {
+ if(_base64Chars[62] == _Ch)
+ return 62;
+
+ if(_base64Chars[63] == _Ch)
+ return 63;
+
+ if((_base64Chars[0] <= _Ch) && (_base64Chars[25] >= _Ch))
+ return _Ch - _base64Chars[0];
+
+ if((_base64Chars[26] <= _Ch) && (_base64Chars[51] >= _Ch))
+ return _Ch - _base64Chars[26] + 26;
+
+ if((_base64Chars[52] <= _Ch) && (_base64Chars[61] >= _Ch))
+ return _Ch - _base64Chars[52] + 52;
+
+ if(_Ch == _Tr::to_int_type('='))
+ return _EQUAL_CHAR;
+
+ return _UNKNOWN_CHAR;
+ }
+
+
+};
+
+
+#endif
diff --git a/branches/lydia/xmlrpc.cpp b/branches/lydia/xmlrpc.cpp
new file mode 100644
index 0000000..8386607
--- /dev/null
+++ b/branches/lydia/xmlrpc.cpp
@@ -0,0 +1,308 @@
+/* IvySync - Video SyncStarter
+ *
+ * (c) Copyright 2004 - 2006 Denis Rojo <jaromil@dyne.org>
+ * Nederlands Instituut voor Mediakunst
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <decoder.h>
+#include <xmlrpc.h>
+#include <utils.h>
+
+static bool quit;
+
+IvySyncDaemon::IvySyncDaemon(XmlRpcServer *srv) {
+
+ xmlrpc = srv;
+ xmlrpc->_ssl = false;
+ xmlrpc->_ssl_ssl = NULL;
+
+ quit = false;
+
+}
+
+bool IvySyncDaemon::init(int port) {
+
+ if( ! xmlrpc->bindAndListen(port) ) return false;
+
+ // to be introspective we can list our own methods
+ // xmlrpc->enableIntrospection(true);
+
+ return true;
+}
+
+void IvySyncDaemon::run(double mstime) {
+ // running = true;
+ // unlock();
+
+ if(::quit) {
+ quit = true;
+ return;
+ }
+
+ // run for amount of milliseconds (-1.0 for infinite)
+ xmlrpc->work( mstime );
+
+}
+
+
+Play::Play(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("Play", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+Stop::Stop(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("Stop", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+Pause::Pause(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("Pause", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+GetPos::GetPos(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("GetPos", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+SetPos::SetPos(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("SetPos", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+GetOffset::GetOffset(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("GetOffset", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+SetOffset::SetOffset(XmlRpcServer* srv, Linklist *decoders)
+ : XmlRpcServerMethod("SetOffset", srv),
+ IvySyncPublicMethod(decoders)
+{ }
+
+Open::Open(XmlRpcServer* src, Linklist *decoders)
+ : XmlRpcServerMethod("Open", src),
+ IvySyncPublicMethod(decoders)
+{ }
+
+Quit::Quit(XmlRpcServer* src, Linklist *decoders)
+ : XmlRpcServerMethod("Quit", src),
+ IvySyncPublicMethod(decoders)
+{ }
+
+void Quit::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ Decoder *dec;
+
+ dec = (Decoder*)decoders->begin();
+ while(dec) {
+
+ dec->stop();
+ dec->close();
+
+ dec = (Decoder*)dec->next;
+ }
+
+ result = 1.0;
+
+ ::quit = true;
+}
+
+void Open::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+ char *path;
+
+ if( params.size() < 2) {
+ E("XMLRPC: Open called with invalid number of arguments(%u)",
+ params.size() );
+ result = 0.0;
+ return;
+ }
+
+ // get out the decoder parameter
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ // get out the path to the file to be opened
+ path = (char*) (std::string(params[1])).c_str();
+ D("XMLRPC: Open decoder %u file %s", decnum, path);
+
+ FILE *fd;
+ fd = fopen(path, "r");
+ if(!fd) {
+ result = 0.0;
+ return;
+ } else fclose(fd);
+
+ dec->stop();
+ dec->empty();
+ dec->append(path);
+ result = 1.0;
+
+}
+
+void Play::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+
+ if( params.size() < 1) {
+ E("XMLRPC: Play called with invalid number of arguments (%u)",
+ params.size() );
+ result = 0.0;
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ D("XMLRPC: Play decoder %u", decnum );
+ result = (double) dec->play();
+}
+
+
+
+void Stop::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+
+ if( params.size() < 1) {
+ E("XMLRPC: Stop called with invalid number of arguments (%u)",
+ params.size() );
+ result = 0.0;
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ D("XMLRPC: Stop decoder %u", decnum);
+ result = (double) dec->stop();
+}
+
+void Pause::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+
+ if( params.size() < 1) {
+ E("XMLRPC: Pause called with invalid number of arguments (%u)",
+ params.size() );
+ result = 0.0;
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ D("XMLRPC: Pause decoder %u", decnum);
+ result = (double) dec->pause();
+}
+
+
+void GetPos::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+ double pos;
+
+ if( params.size() < 1) {
+ E("XMLRPC: GetPos called with invalid number of arguments (%u)",
+ params.size() );
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ pos = (double) dec->getpos();
+ result = pos;
+ // D("XMLRPC: GetPos decoder %u returns %u", decnum, pos);
+
+}
+
+void GetOffset::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+ int pos;
+
+ if( params.size() < 1) {
+ E("XMLRPC: GetOffset called with invalid number of arguments (%u)",
+ params.size() );
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ pos = (int) dec->getoffset();
+ result = pos;
+ D("XMLRPC: GetOffset decoder %u returns %d", decnum, pos);
+}
+
+
+void SetPos::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+ int pos;
+
+ if( params.size() < 2) {
+ E("XMLRPC: SetPos called with invalid number of arguments (%u)",
+ params.size() );
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ pos = (int) params[1];
+ D("XMLRPC: SetPos decoder %u to position %i", decnum, pos);
+ dec->setpos( pos );
+}
+
+void SetOffset::execute(XmlRpcValue &params, XmlRpcValue &result) {
+ int decnum;
+ double pos;
+
+ if( params.size() < 2) {
+ E("XMLRPC: SetOffset called with invalid number of arguments (%u)",
+ params.size() );
+ return;
+ }
+
+ decnum = (int) params[0];
+ Decoder *dec = (Decoder*) (*decoders)[decnum];
+ if(!dec) {
+ E("video decoder %i not present", decnum);
+ result = 0.0; return; }
+
+ pos = (int) params[1];
+ D("XMLRPC: SetOffset decoder %u to position %d", decnum, pos);
+ dec->setoffset( (off64_t) pos );
+}
diff --git a/branches/lydia/xmlrpc.h b/branches/lydia/xmlrpc.h
new file mode 100644
index 0000000..0dfab27
--- /dev/null
+++ b/branches/lydia/xmlrpc.h
@@ -0,0 +1,209 @@
+/* IvySync - Video SyncStarter
+ *
+ * (c) Copyright 2004 - 2006 Denis Rojo <jaromil@dyne.org>
+ * Nederlands Instituut voor Mediakunst
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __XMLRPC_H__
+#define __XMLRPC_H__
+
+
+#include <XmlRpc.h>
+
+#include <iostream>
+#include <stdlib.h>
+
+#include <thread.h>
+
+using namespace XmlRpc;
+
+// damn STL
+#include <vector>
+using namespace std;
+using namespace __gnu_cxx;
+
+
+// METHODS:
+class Open;
+class Play;
+class Stop;
+class GetPos;
+class SetPos;
+class Quit;
+
+
+class IvySyncDaemon {
+public:
+ IvySyncDaemon(XmlRpcServer *srv);
+ ~IvySyncDaemon() { };
+
+ bool init(int port);
+ void run(double mstime);
+
+ bool quit;
+
+private:
+
+ XmlRpcServer *xmlrpc;
+
+};
+
+class IvySyncPublicMethod {
+public:
+
+ IvySyncPublicMethod(Linklist *decs) {
+ decoders = decs;
+ }
+
+ ~IvySyncPublicMethod() { };
+
+ Linklist *decoders;
+
+};
+
+//RPC(Stop,"Stop playing a channel");
+//RPC(PlaylistAppend,"Append a file to a channel's playlist");
+
+
+class Play : public XmlRpcServerMethod, IvySyncPublicMethod {
+public:
+
+ Play(XmlRpcServer* srv, Linklist *decoders);
+
+ ~Play() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Start playing the channel"); }
+
+};
+
+
+class Stop : public XmlRpcServerMethod, IvySyncPublicMethod {
+public:
+
+ Stop(XmlRpcServer* srv, Linklist *decoders);
+
+ ~Stop() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Stop playing the channel"); }
+
+};
+
+class Pause : public XmlRpcServerMethod, IvySyncPublicMethod {
+public:
+
+ Pause(XmlRpcServer* srv, Linklist *decoders);
+
+ ~Pause() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Pause the channel"); }
+
+};
+
+class GetPos : public XmlRpcServerMethod, IvySyncPublicMethod {
+public:
+
+ GetPos(XmlRpcServer* srv, Linklist *decoders);
+
+ ~GetPos() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Return position of channel in percentage"); }
+
+};
+
+class GetOffset : public XmlRpcServerMethod, IvySyncPublicMethod {
+ public:
+
+ GetOffset(XmlRpcServer* srv, Linklist *decoders);
+
+ ~GetOffset() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Return position of channel in byte offset"); }
+};
+
+class SetPos : public XmlRpcServerMethod, IvySyncPublicMethod {
+ public:
+
+ SetPos(XmlRpcServer* srv, Linklist *decoders);
+
+ ~SetPos() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Skip channel to position in percentage"); }
+
+};
+
+
+class SetOffset : public XmlRpcServerMethod, IvySyncPublicMethod {
+ public:
+
+ SetOffset(XmlRpcServer* srv, Linklist *decoders);
+
+ ~SetOffset() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Skip channel to position in byte offset"); }
+
+};
+
+class Open : public XmlRpcServerMethod, IvySyncPublicMethod {
+ public:
+
+ Open(XmlRpcServer* srv, Linklist *decoders);
+
+ ~Open() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Open a new file to be played"); }
+
+};
+
+class Quit : public XmlRpcServerMethod, IvySyncPublicMethod {
+ public:
+
+ Quit(XmlRpcServer* srv, Linklist *decoders);
+
+ ~Quit() { };
+
+ void execute(XmlRpcValue &params, XmlRpcValue &result);
+
+ std::string help() {
+ return std::string("Quit the ivysync from running"); }
+
+};
+
+#endif